我正在尝试配置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 明确的信息有什么不对。该 后一种方法看起来更加一致。
答案 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。