pentaho/pentaho-kettle · error · KettleException

Unable to create a logging event listener to write to file

Error message

Unable to create a logging event listener to write to file '${filename}'

What it means

FileLoggingEventListener's constructor opens a VFS output stream to the given filename so log events can be appended to it. If KettleVFS cannot open that stream for any reason (bad path, locked file, filesystem error), it wraps the cause in a KettleException. The listener is therefore never usable and the caller gets this error at construction time.

Solutions

  1. Verify the filename is an absolute, valid path and the parent directory exists (create it first).
  2. Ensure the process has write permission on the target file/directory.
  3. Close any other process holding the file locked, or use a different filename.
  4. Inspect the wrapped cause (KettleException.getCause()) for the underlying VFS error.

Example fix

// before
new FileLoggingEventListener("logs/kettle.log", true); // logs/ missing
// after
File logDir = new File("logs");
logDir.mkdirs();
new FileLoggingEventListener(logDir.getAbsolutePath() + "/kettle.log", true);
Defensive patterns

Strategy: try-catch

Validate before calling

File f = new File(filename);
if (f.isDirectory() || (f.exists() && !f.canWrite())) throw new IllegalStateException("Cannot write log file: " + filename);
File parent = f.getAbsoluteFile().getParentFile();
if (parent != null) parent.mkdirs();

Type guard

static boolean canOpenForWrite(String path) {
  try (java.io.OutputStream os = new java.io.FileOutputStream(path, true)) { return true; }
  catch (Exception e) { return false; }
}

Try / catch

try {
  FileLoggingEventListener listener = new FileLoggingEventListener(filename, true);
  KettleLogStore.getAppender().addLoggingEventListener(listener);
} catch (KettleException e) {
  log.logError("Could not open log file listener: " + e.getCause(), e);
}

Prevention

When it happens

Trigger: new FileLoggingEventListener(filename, append) where KettleVFS.getFileObject succeeds but getOutputStream throws — e.g. the path points to a directory, the parent folder does not exist, the file is locked by another process, or no write permission.

Common situations: Configuring file-based logging with a typo'd or relative path that doesn't resolve; writing to a log file already held open exclusively by another job; running under a service account without write access to the log directory.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/9fa812af9d9362a9. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/logging/FileLoggingEventListener.java:72

   * file.
   *
   * @param logChannelId
   * @param filename
   * @param append
   * @throws KettleException
   */
  public FileLoggingEventListener( String logChannelId, String filename, boolean append ) throws KettleException {
    this.logChannelId = logChannelId;
    this.filename = filename;
    this.layout = new KettleLogLayout( true );
    this.exception = null;

    file = KettleVFS.getInstance( DefaultBowl.getInstance() ).getFileObject( filename );
    outputStream = null;
    try {
      outputStream = KettleVFS.getInstance( DefaultBowl.getInstance() ).getOutputStream( file, append );
    } catch ( Exception e ) {
      throw new KettleException(
        "Unable to create a logging event listener to write to file '" + filename + "'", e );
    }
  }

  @Override
  public void eventAdded( KettleLoggingEvent event ) {

    try {
      Object messageObject = event.getMessage();
      if ( messageObject instanceof LogMessage ) {
        boolean logToFile = false;

        if ( logChannelId == null ) {
          logToFile = true;
        } else {
          LogMessage message = (LogMessage) messageObject;
          // This should be fast enough cause cached.
          List<String> logChannelChildren = LoggingRegistry.getInstance().getLogChannelChildren( logChannelId );

View on GitHub (pinned to f3058517a1)