log4j 配置Java文件日志记录以在目录不存在时创建目录

pbgvytdp  于 12个月前  发布在  Java
关注(0)|答案(6)|浏览(150)

我正在尝试配置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


它看起来像是一个针对日志文件的bug:Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist
听起来他们不打算修复它或公开任何属性来解决这个问题(除了让你的应用程序解析logging.properties或硬编码所需的路径):
它看起来像java.util.logging. FileStream不期望指定的目录可能不存在。通常,它无论如何都必须检查此条件。此外,它还必须检查目录写入权限。另一个问题是如果这些检查之一没有通过该怎么办。
一种可能性是在用户拥有适当权限的情况下在路径中创建缺少的目录。另一种可能性是抛出一个IOException,并明确指出错误所在。后一种方法看起来更一致。

mlnl4t2r

mlnl4t2r1#

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");
}

字符串
这段代码来自于FileTender,RollingFileTender扩展了FileTender。
在这里,它不会检查我们是否有权限创建父文件夹,但如果父文件夹不存在,那么它将尝试创建父文件夹。
编辑
如果你想要一些额外的功能,你总是可以扩展RollingFileDataReader并覆盖setFile()方法。

rdrgkggo

rdrgkggo2#

你可以这样写。

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


这对我来说很完美。

nvbavucw

nvbavucw3#

解决Java日志框架的限制和未解决的bug:JDK-6244047 : impossible to specify directories to logging FileHandler unless they exist
我提出了两种方法(尽管只有第一种方法实际上有效),这两种方法都需要你的应用程序的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并创建异常中列出的目录,这种方法的问题是日志记录框架已经无法创建FileLog,因此捕获和解决错误仍然会使日志记录系统处于错误状态。

ar5n3qh5

ar5n3qh54#

作为一种可能的解决方案,我认为有两种方法(看看前面的一些答案)。我可以扩展Java Logging类并编写自己的自定义处理程序。我还可以复制log4j功能并将其适应Java Logging框架。
下面是一个复制基本文件夹并创建自定义文件夹的示例,请参阅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;
        }
    }


{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}

vom3gejh

vom3gejh5#

我通常会尽量避免静态代码,但为了绕过这个限制,这里是我刚才在我的项目中使用的方法。
我子类化了java.util.logging. FileCloud并实现了所有的构造函数及其超级调用。我在类中放置了一个静态代码块,如果我的应用程序不存在,它会在user.home文件夹中创建文件夹。
在日志属性文件中,我用新类替换了java.util.logg.FileObject。

u4vypkhs

u4vypkhs6#

错误(JDK-6244047 : impossible to specify directories to logging FileHandler unless they exist)在Java 8中已被修复。将Java版本更新到8或更高版本应无需解决此问题。

相关问题