【问题标题】:Configuring Java FileHandler Logging to create directories if they do not exist配置 Java FileHandler 日志记录以在目录不存在时创建目录
【发布时间】:2009-08-12 01:59:23
【问题描述】:

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

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

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

这将允许我在我的主目录 (%h) 中为 MyApplication 收集日志并轮换它们(使用 %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 清楚的消息什么是错的。这 后一种方法看起来更一致。

【问题讨论】:

    标签: java logging log4j


    【解决方案1】:

    好像 log4j 版本 1.2.15 可以做到。

    这是执行此操作的代码的 sn-p

    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。

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

    已编辑

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

    【讨论】:

    • 如果我在 log4j 框架内但我使用的是 Java Logging 框架,那么代码 sn-p 可以工作,并且没有钩子可以拦截 FileHandler 创建调用(除了捕获异常第一次访问 Logger,例如 logger.info("test").
    • 我的意思是您可以编写自己的附加程序。
    • 用户没有询问 Log4J。
    • 是的,这是 Log4j 的问题。我从 1.2.8 升级到 1.2.17 .. 它得到了修复。如果文件夹不存在,则开始创建文件夹,但如果线程有权更新目标文件夹,则很明显。
    【解决方案2】:

    你可以这样写。

    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
    

    这对我来说非常适合。

    【讨论】:

    • 我将不得不研究如何在 Java 日志框架中创建自定义日志处理程序,但我敢打赌它类似于 Log4j 框架。
    【解决方案3】:

    要解决 Java Logging 框架的限制和未解决的错误:Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist

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

    例如

    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,因此捕获和解决错误仍然会使日志系统处于错误状态状态。

    【讨论】:

    • 你认为这种方法将所有场景都像登录一个Web应用程序。在初始化记录器之前,我们需要调用一个拦截器。由于我们可以编写自己的 logAppender,我认为这将是一个更好的选择,它可以解决大多数情况。
    • 我同意,我将在 Logging Framework 中执行此操作,并附上我的答案。
    • 我刚刚尝试通过复制 java.util.logging.FileHandler 创建自己的 CustomFileHandler,有几个初始问题。您需要在 java.util.logging.* 包中才能访问 Manager .getStringProperty 方法。所以我尝试使用该包创建一个类,但在运行时加载该类时出现安全异常:无法加载日志处理程序“java.util.logging.CustomFileHandler”java.lang.SecurityException:禁止的包名称:java.util .logging
    • 由于 log4j 使用的是 Apache 许可证,版本 2.0,我们可以使用 log4j 类和接口来创建自定义类。我认为扩展现有的 log4j FileAppender 比编写新的日志处理程序容易。我上面给出的示例从 log4j 扩展了 RollingFileAppender。在 RollingFileAppender 中,我们有一个名为 setFile() 的方法,它创建它必须记录到的文件。所以你可以重写这个方法来得到想要的结果
    • 由于setFile()中有fileName,即文件的绝对路径,所以可以在这里查看文件/父目录是否存在。如果不是,您可以创建父目录或根据需要抛出适当的错误消息。
    【解决方案4】:

    作为一种可能的解决方案,我认为有两种方法(查看之前的一些答案)。我可以扩展 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;
            }
        }
    

    ... }

    【讨论】:

      【解决方案5】:

      我通常会尽量避免使用静态代码,但要解决这个限制,这是我刚才在我的项目中使用的方法。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        • 2016-09-04
        • 1970-01-01
        • 1970-01-01
        • 2021-08-27
        • 2017-02-01
        • 2015-07-22
        相关资源
        最近更新 更多