我在使用Log4J的项目中工作。其中一个要求是为每个线程创建一个单独的日志文件;这本身就是一个奇怪的问题,通过动态创建一个新的FileAppender并将其附加到Logger实例来进行排序。
Logger logger = Logger.getLogger(<thread dependent string>);
FileAppender appender = new FileAppender();
appender.setFile(fileName);
appender.setLayout(new PatternLayout(lp.getPattern()));
appender.setName(<thread dependent string>);
appender.setThreshold(Level.DEBUG);
appender.activateOptions();
logger.addAppender(appender);
一切顺利,直到我们意识到我们使用的另一个库 - Spring Framework v3.0.0(使用Commons Logging) - 不使用上述技术播放 - Spring日志数据被“看到”只有Appenders从log4.configuration文件初始化而不是由运行时创建的Appender。 所以,回到原点。
经过一番调查后,我发现新的LogBack有一个appender - SiftingAppender - 这正是我们所需要的,即独立文件的线程级别记录。
目前,转向LogBack不是一个选择,因此,如果坚持使用Log4J,我怎样才能实现类似SiftingAppender的功能并让Spring保持高兴?
注意:Spring仅用于JdbcTemplate功能,没有IOC;为了“挂钩”Spring的Commons Logging Log4J我在log4j.properties文件中添加了这一行:
log4j.logger.org.springframework = DEBUG
按指示here。
答案 0 :(得分:3)
通过slf4j api访问LogBack。有一个名为jcl-over-sjf4j的适配器库,它公开了commons日志记录接口,但是将所有日志记录到slf4j API,它直接进入实现 - LogBack。如果您使用的是maven,则以下是依赖项:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.18</version>
</dependency>
(并将公共日志记录添加到排除列表,请参阅here)
答案 1 :(得分:3)
我挣扎了一段时间在log4j中找到类似SiftingAppender的功能(由于某些依赖性,我们无法切换到logback),最终得到一个运行良好的编程解决方案,使用MDC并在运行时附加记录器:
// this can be any thread-specific string
String processID = request.getProcessID();
Logger logger = Logger.getRootLogger();
// append a new file logger if no logger exists for this tag
if(logger.getAppender(processID) == null){
try{
String pattern = "%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n";
String logfile = "log/"+processID+".log";
FileAppender fileAppender = new FileAppender(
new PatternLayout(pattern), logfile, true);
fileAppender.setName(processID);
// add a filter so we can ignore any logs from other threads
fileAppender.addFilter(new ProcessIDFilter(processID));
logger.addAppender(fileAppender);
}catch(Exception e){
throw new RuntimeException(e);
}
}
// tag all child threads with this process-id so we can separate out log output
MDC.put("process-id", processID);
//whatever you want to do in the thread
LOG.info("This message will only end up in "+processID+".log!");
MDC.remove("process-id");
上面附加的过滤器只检查特定的进程ID:
public class RunIdFilter extends Filter {
private final String runId;
public RunIdFilter(String runId) {
this.runId = runId;
}
@Override
public int decide(LoggingEvent event) {
Object mdc = event.getMDC("run-id");
if (runId.equals(mdc)) {
return Filter.ACCEPT;
}
return Filter.DENY;
}
}
希望这会有所帮助。
答案 2 :(得分:1)
在Log4j2中,我们现在可以使用RoutingAppender:
RoutingAppender评估LogEvents,然后将它们路由到从属Appender。目标Appender可以是先前配置的appender,可以通过其名称引用,也可以根据需要动态创建Appender。
来自FAQ:
如何动态写入单独的日志文件? 看看RoutingAppender。您可以在配置中定义多个路由,并将值放在ThreadContext映射中,以确定此线程中的后续事件将记录到哪个日志文件。
答案 3 :(得分:0)
我喜欢将所有slf4j外观/重新路由器/ whateverycallthem所有。还要注意“提供的”hack,它可以防止依赖于公共记录;以前我使用的是一个假的空公共日志库,名为version-99.0-does-not-exists。
另见http://blog.springsource.com/2009/12/04/logging-dependencies-in-spring/
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<!-- use provided scope on real JCL instead -->
<!-- <version>99.0-does-not-exist</version> -->
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<!-- use provided scope on real JCL instead -->
<!-- <version>99.0-does-not-exist</version> -->
<version>1.1</version>
<scope>provided</scope>
</dependency>
<!-- the slf4j commons-logging replacement -->
<!-- if any package is using jakarta commons logging this will -->
<!-- re-route it through slf4j. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${version.slf4j}</version>
</dependency>
<!-- the slf4j log4j replacement. -->
<!-- if any package is using log4j this will re-route -->
<!-- it through slf4j. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${version.slf4j}</version>
</dependency>
<!-- the slf4j java.util.logging replacement. -->
<!-- if any package is using java.util.logging this will re-route -->
<!-- it through slf4j. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${version.slf4j}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${version.slf4j}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${version.logback}</version>
</dependency>
</dependencies>
<properties>
<version.logback>0.9.15</version.logback>
<version.slf4j>1.5.8</version.slf4j>
</properties>
答案 4 :(得分:0)