配置 Java FileHandler Logging 以在目录不存在时创建目录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1263876/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Configuring Java FileHandler Logging to create directories if they do not exist
提问by Dougnukem
I'm trying to configure the Java LoggingAPI's FileHandlerto log my server to a file within a folder in my home directory, but I don't want to have to create those directories on every machine it's running.
我正在尝试配置Java LoggingAPI 的FileHandler以将我的服务器记录到我的主目录中文件夹内的文件中,但我不想在它运行的每台机器上都创建这些目录。
For example in the logging.properties file I specify:
例如在 logging.properties 文件中我指定:
java.util.logging.FileHandler
java.util.logging.FileHandler.pattern=%h/app-logs/MyApplication/MyApplication_%u-%g.log
This would allow me to collect logs in my home directory (%h) for MyApplication and would rotate them (using the %u, and %g variables).
这将允许我在我的主目录 (%h) 中为 MyApplication 收集日志并轮换它们(使用 %u 和 %g 变量)。
Log4j supports this when I specify in my log4j.properties:
当我在 log4j.properties 中指定时,Log4j 支持这一点:
log4j.appender.rolling.File=${user.home}/app-logs/MyApplication-log4j/MyApplication.log
It looks like there is a bug against the Logging FileHandler: Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist
看起来有一个针对 Logging FileHandler 的错误: 错误 6244047:不可能指定记录 FileHandler 的目录,除非它们存在
It sounds like they don't plan on fixing it or exposing any properties to work around the issue (beyond having your application parse the logging.properties or hard code the path needed):
听起来他们不打算修复它或公开任何属性来解决这个问题(除了让您的应用程序解析 logging.properties 或硬编码所需的路径):
It looks like the java.util.logging.FileHandler does not expect that the specified directory may not exist. Normally, it has to check this condition anyway. Also, it has to check the directory writing permissions as well. Another question is what to do if one of these check does not pass.
One possibility is to create the missing directories in the path if the user has proper permissions. Another is to throw an IOException with a clear message what is wrong. The latter approach looks more consistent.
看起来 java.util.logging.FileHandler 不希望指定的目录可能不存在。通常,它无论如何都必须检查此条件。此外,它还必须检查目录写入权限。另一个问题是,如果其中一项检查未通过,该怎么办。
如果用户具有适当的权限,一种可能性是在路径中创建缺少的目录。另一种方法是抛出一个 IOException 并清楚地说明出了什么问题。后一种方法看起来更一致。
回答by Arun P Johny
It seems like log4j version 1.2.15 does it.
似乎 log4j 版本 1.2.15 做到了。
Here is the snippet of the code which does it
这是执行此操作的代码片段
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");
}
This piece of code is from FileAppender, RollingFileAppender extends FileAppender.
这段代码来自 FileAppender,RollingFileAppender 扩展了 FileAppender。
Here it is not checking whether we have permission to create the parent folders, but if the parent folders is not existing then it will try to create the parent folders.
这里不是检查我们是否有权限创建父文件夹,但如果父文件夹不存在,那么它会尝试创建父文件夹。
EDITED
已编辑
If you want some additional functionalily, you can always extend RollingFileAppender and override the setFile() method.
如果你想要一些额外的功能,你总是可以扩展 RollingFileAppender 并覆盖 setFile() 方法。
回答by Arun P Johny
You can write something like this.
你可以写这样的东西。
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);
}
}
Then in your configuration
然后在你的配置中
log4j.appender.fileAppender=org.log.MyRollingFileAppender
This works perfectly for me.
这对我来说非常有效。
回答by Dougnukem
To work around the limitations of the Java Logging framework, and the unresolved bug: Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist
解决 Java Logging 框架的局限性和未解决的错误:错误 6244047:不可能指定记录 FileHandler 的目录,除非它们存在
I've come up with 2 approaches (although only the first approach will actually work), both require your static void main() method for your app to initialize the logging system.
我提出了 2 种方法(虽然只有第一种方法实际上有效),两者都需要您的应用程序的 static void main() 方法来初始化日志记录系统。
e.g.
例如
public static void main(String[] args) {
initLogging();
...
}
The first approach hard-codes the log directories you expect to exist and creates them if they don't exist.
第一种方法对您希望存在的日志目录进行硬编码,如果它们不存在则创建它们。
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();
}
}
The second approach could catch the IOException and create the directories listed in the exception, the problem with this approach is that the Logging framework has already failed to create the FileHandler so catching and resolving the error still leaves the logging system in a bad state.
第二种方法可以捕获 IOException 并创建异常中列出的目录,这种方法的问题是 Logging 框架已经无法创建 FileHandler,因此捕获和解决错误仍然会使日志系统处于错误状态。
回答by Dougnukem
As a possible solution I think there are 2 approaches (look at some of the previous answers). I can extend a Java Logging Handler class and write my own custom handler. I could also copy the log4j functionality and adapt it to the Java Logging framework.
作为可能的解决方案,我认为有 2 种方法(查看之前的一些答案)。我可以扩展 Java Logging Handler 类并编写自己的自定义处理程序。我还可以复制 log4j 功能并使其适应 Java Logging 框架。
Here's an example of copying the basic FileHandler and creating a CustomFileHandler see pastebin for full class:
这是复制基本 FileHandler 并创建 CustomFileHandler 的示例,请参阅pastebin 以获取完整类:
The key is the openFiles() method where it tries to create a FileOutputStream and checking and creating the parent directory if it doesn't exist (I also had to copy package protected LogManager methods, why did they even make those package protected anyways):
关键是 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;
}
}
... }
... }
回答by Gryphie
I generally try to avoid static code but to work around this limitaton here is my approach that worked on my project just now.
我通常会尽量避免使用静态代码,但要解决这个限制,这是我刚才在我的项目中使用的方法。
I subclassed java.util.logging.FileHandler and implemented all constructors with their super calls. I put a static block of code in the class that creates the folders for my app in the user.home folder if they don't exist.
我子类化 java.util.logging.FileHandler 并使用它们的超级调用实现所有构造函数。我在类中放置了一个静态代码块,用于在 user.home 文件夹中为我的应用程序创建文件夹(如果它们不存在)。
In my logging properties file I replaced java.util.logging.FileHandler with my new class.
在我的日志属性文件中,我用我的新类替换了 java.util.logging.FileHandler。