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

问题描述 投票:0回答:5

我正在尝试配置 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:无法指定目录来记录 FileHandler,除非它们存在

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

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

一种可能性是创建 如果路径中缺少目录 用户有适当的权限。其他 是抛出一个 IOException 明确消息出了什么问题。这 后一种方法看起来更一致。

java logging log4j
5个回答
9
投票

看来 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。

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

已编辑

如果您想要一些附加功能,您可以随时扩展 RollingFileAppender 并重写 setFile() 方法。


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

这对我来说非常有效。


4
投票

解决 Java 日志框架的限制和未解决的错误:JDK-6244047:无法指定要记录 FileHandler 的目录,除非它们存在

我想出了两种方法(尽管只有第一种方法实际上有效),两种方法都需要您的应用程序使用

static 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 (https://bugs.java.com/bugdatabase/view_bug?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
并创建异常中列出的目录,这种方法的问题是日志记录框架已经无法创建FileHandler,因此捕获并解决错误仍然会使日志系统处于糟糕的状态状态。


1
投票

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

这是复制基本 FileHandler 并创建 CustomFileHandler 的示例,请参阅 pastebin 了解完整类

关键是 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;
        }
    }

... }


1
投票

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

我对 java.util.logging.FileHandler 进行了子类化,并通过其 super 调用实现了所有构造函数。我在类中放置了一个静态代码块,用于在 user.home 文件夹中为我的应用程序创建文件夹(如果它们不存在)。

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

© www.soinside.com 2019 - 2024. All rights reserved.