配置Java FileHandler日志记录以创建不存在的目录

时间:2009-08-12 01:59:23

标签: java logging log4j

我正在尝试配置Java Logging API FileHandler以将我的服务器记录到我的主目录中的文件夹中的文件,但我不想在每个目录中创建这些目录机器正在运行。

例如,在logging.properties文件中,我指定:

java.util.logging.FileHandler
java.util.logging.FileHandler.pattern=%h/app-logs/MyApplication/MyApplication_%u-%g.log

这将允许我在MyApplication的主目录(%h)中收集日志,并将它们旋转(使用%u和%g变量)。

当我在log4j.properties中指定时,Log4j支持这一点:

log4j.appender.rolling.File=${user.home}/app-logs/MyApplication-log4j/MyApplication.log

看起来Logging FileHandler存在一个错误: Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist

听起来他们不打算修复它或暴露任何属性来解决问题(除了让你的应用程序解析logging.properties或硬编码所需的路径):

  

看起来像   java.util.logging.FileHandler没有   期望指定的目录   可能不存在。通常,它必须   无论如何要检查这个条件。它,它   必须检查目录编写   权限也是如此。另一个问题   如果其中一个检查,该怎么办   不通过。

     

一种可能性是创造   如果在路径中缺少目录   用户具有适当的权限。另一个   是用一个抛出一个IOException   明确的信息有什么不对。该   后一种方法看起来更加一致。

5 个答案:

答案 0 :(得分:8)

似乎log4j版本1.2.15就可以了。

以下是执行此操作的代码片段

public
 synchronized
 void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
                                                        throws IOException {
    LogLog.debug("setFile called: "+fileName+", "+append);

    // It does not make sense to have immediate flush and bufferedIO.
    if(bufferedIO) {
      setImmediateFlush(false);
    }

    reset();
    FileOutputStream ostream = null;
    try {
          //
          //   attempt to create file
          //
          ostream = new FileOutputStream(fileName, append);
    } catch(FileNotFoundException ex) {
          //
          //   if parent directory does not exist then
          //      attempt to create it and try to create file
          //      see bug 9150
          //
          String parentName = new File(fileName).getParent();
          if (parentName != null) {
             File parentDir = new File(parentName);
             if(!parentDir.exists() && parentDir.mkdirs()) {
                ostream = new FileOutputStream(fileName, append);
             } else {
                throw ex;
             }
          } else {
             throw ex;
          }
    }
    Writer fw = createWriter(ostream);
    if(bufferedIO) {
      fw = new BufferedWriter(fw, bufferSize);
    }
    this.setQWForFiles(fw);
    this.fileName = fileName;
    this.fileAppend = append;
    this.bufferedIO = bufferedIO;
    this.bufferSize = bufferSize;
    writeHeader();
    LogLog.debug("setFile ended");
}

这段代码来自FileAppender,RollingFileAppender扩展了FileAppender。

这里没有检查我们是否有权创建父文件夹,但如果父文件夹不存在,那么它将尝试创建父文件夹。

EDITED

如果你想要一些额外的功能,你可以随时扩展RollingFileAppender并覆盖setFile()方法。

答案 1 :(得分:5)

你可以写这样的东西。

package org.log;

import java.io.IOException;
import org.apache.log4j.RollingFileAppender;

public class MyRollingFileAppender extends RollingFileAppender {

    @Override
    public synchronized void setFile(String fileName, boolean append,
        boolean bufferedIO, int bufferSize) throws IOException {
        //Your logic goes here
        super.setFile(fileName, append, bufferedIO, bufferSize);
    }

}

然后在你的配置中

log4j.appender.fileAppender=org.log.MyRollingFileAppender

这对我来说很有效。

答案 2 :(得分:4)

解决Java Logging框架的局限性和未解决的bug:Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist

我提出了两种方法(虽然只有第一种方法实际可行),两者都需要你的应用程序的静态void main()方法来初始化日志记录系统。

e.g。

public static void main(String[] args) {    

    initLogging();
     ...
    }

第一种方法对您希望存在的日志目录进行硬编码,如果它们不存在则创建它们。

private static void initLogging() {
    try {
        //Create logging.properties specified directory for logging in home directory
        //TODO: If they ever fix this bug (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6244047) in the Java Logging API we wouldn't need this hack
        File homeLoggingDir = new File (System.getProperty("user.home")+"/webwars-logs/weblings-gameplatform/");
        if (!homeLoggingDir.exists() ) {
            homeLoggingDir.mkdirs();
            logger.info("Creating missing logging directory: " + homeLoggingDir);
        }
    } catch(Exception e) {
        e.printStackTrace();
    }

    try {
        logger.info("[GamePlatform] : Starting...");
    } catch (Exception exc) {
        exc.printStackTrace();

    }
}

第二种方法可以捕获IOException并创建异常中列出的目录,这种方法的问题是Logging框架已经无法创建FileHandler因此捕获和解决错误仍然使得日志系统处于不良状态状态。

答案 3 :(得分:1)

作为一种可能的解决方案,我认为有两种方法(请看一些以前的答案)。我可以扩展Java Logging Handler类并编写自己的自定义处理程序。我还可以复制log4j功能并使其适应Java Logging框架。

以下是复制基本FileHandler并创建CustomFileHandler的示例,请参阅pastebin for full class

关键是openFiles()方法,它尝试创建FileOutputStream并检查并创建父目录(如果它不存在)(我还必须复制包受保护的LogManager方法,为什么他们甚至使这些包受到保护反正):

// Private method to open the set of output files, based on the
// configured instance variables.
private void openFiles() throws IOException {
    LogManager manager = LogManager.getLogManager();

...

    // Create a lock file. This grants us exclusive access
    // to our set of output files, as long as we are alive.
    int unique = -1;
    for (;;) {
        unique++;
        if (unique > MAX_LOCKS) {
            throw new IOException("Couldn't get lock for " + pattern);
        }
        // Generate a lock file name from the "unique" int.
        lockFileName = generate(pattern, 0, unique).toString() + ".lck";
        // Now try to lock that filename.
        // Because some systems (e.g. Solaris) can only do file locks
        // between processes (and not within a process), we first check
        // if we ourself already have the file locked.
        synchronized (locks) {
            if (locks.get(lockFileName) != null) {
                // We already own this lock, for a different FileHandler
                // object. Try again.
                continue;
            }
            FileChannel fc;
            try {
                File lockFile = new File(lockFileName);
                if (lockFile.getParent() != null) {
                    File lockParentDir = new File(lockFile.getParent());
                    // create the log dir if it does not exist
                    if (!lockParentDir.exists()) {
                        lockParentDir.mkdirs();
                    }
                }

                lockStream = new FileOutputStream(lockFileName);
                fc = lockStream.getChannel();
            } catch (IOException ix) {
                // We got an IOException while trying to open the file.
                // Try the next file.
                continue;
            }
            try {
                FileLock fl = fc.tryLock();
                if (fl == null) {
                    // We failed to get the lock. Try next file.
                    continue;
                }
                // We got the lock OK.
            } catch (IOException ix) {
                // We got an IOException while trying to get the lock.
                // This normally indicates that locking is not supported
                // on the target directory. We have to proceed without
                // getting a lock. Drop through.
            }
            // We got the lock. Remember it.
            locks.put(lockFileName, lockFileName);
            break;
        }
    }

...     }

答案 4 :(得分:1)

我通常会尽量避免使用静态代码,但是要解决这个问题,我的方法就是刚刚适用于我的项目。

我将java.util.logging.FileHandler子类化,并使用超级调用实现了所有构造函数。我在类中放置了一个静态代码块,如果它们不存在,则会在user.home文件夹中为我的应用程序创建文件夹。

在我的日志属性文件中,我用我的新类替换了java.util.logging.FileHandler。