问题描述
我在一个使用 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);
一切顺利,直到我们意识到我们使用的另一个库 - 春季框架 v3.0.0(使用 commons logging ) - 不使用上面的技术播放球 - 弹簧记录数据仅由从log4.configuration文件中初始化的附录,而不是由运行时创建的附录. 所以,回到第一个.
经过一番调查,我发现新的和改进的 logback 有一个appender- siftingappender - 它确实可以执行我们需要的内容,即我们需要的线程级别登录独立文件.
目前,转到记录是一个选择,因此,被log4j粘住,我如何实现类似siftingappender的功能并保持春季快乐?
注意:Spring仅用于
log4j.logger.org.springframework = debug 按照指示在这里.
推荐答案
在log4j2中,我们现在可以使用/a>:
RoutingAppender评估logevents,然后将它们路由到下属的appender. Target Appender可以是先前配置的Appender,可以用其名称引用,也可以根据需要动态创建Appender.
来自他们的 faq :
如何动态写入分离日志文件? 查看RoutingAppender.您可以在配置中定义多个路由,并将值放在线程context映射中,以确定该线程中的哪个日志文件后续事件将登录到.
.
其他推荐答案
通过 slf4j api 访问.这里有一个适配库,称为公开Commons记录接口,但将所有日志记录到SLF4J API中,该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>
(并将Commons-Logging添加到排除列表中,请参见这里)
其他推荐答案
我努力努力寻找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; } }
希望这有所帮助.
问题描述
I work in a project that uses Log4J. One of the requirement is to create a separate log file for each thread; this itself was a odd issue, somewhat sorted by creating a new FileAppender on the fly and attaching it to the Logger instance.
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);
Everything went fine until we realised that another library we use - Spring Framework v3.0.0 (which use Commons Logging) - does not play ball with the technique above – the Spring logging data is “seen” only by Appenders initialised from the log4.configuration file but not by the runtime created Appenders. So, back to square one.
After some investigation, I found out that the new and improved LogBack has an appender - SiftingAppender – which does exactly what we need i.e. thread level logging on independent files.
At the moment, moving to LogBack is not an option, so, being stuck with Log4J, how can I achieve SiftingAppender-like functionality and keep Spring happy as well ?
Note: Spring is only used for JdbcTemplate functionality, no IOC; in order to “hook” Spring’s Commons Logging to Log4J I added this line in the log4j.properties file:
log4j.logger.org.springframework=DEBUG
as instructed here.
推荐答案
In Log4j2, we can now use RoutingAppender:
The RoutingAppender evaluates LogEvents and then routes them to a subordinate Appender. The target Appender may be an appender previously configured and may be referenced by its name or the Appender can be dynamically created as needed.
From their FAQ:
How do I dynamically write to separate log files? Look at the RoutingAppender. You can define multiple routes in the configuration, and put values in the ThreadContext map that determine which log file subsequent events in this thread get logged to.
其他推荐答案
LogBack is accessed via the slf4j api. There is an adapter library called jcl-over-sjf4j which exposes the commons logging interface but makes all the logging to the slf4j API, which goes directly to the implementation - LogBack. If you are using maven, here are the dependencies:
<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>
(and add the commons-logging to the exclusion list, see here)
其他推荐答案
I struggled for a while to find SiftingAppender-like functionality in log4j (we couldn't switch to logback because of some dependencies), and ended up with a programmatic solution that works pretty well, using an MDC and appending loggers at runtime:
// 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");
The filter appended above just checks for a specific process 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; } }
Hope this helps a bit.