是否有可能在运行时使用 MDC 来命名日志文件。
我有一个使用 tomcat docbase 同时被不同名称调用的 Web 应用程序。所以我需要为他们每个人都有单独的日志文件。
这可以在Log4J 的继承者Logback中完成。
Logback 旨在作为流行的 log4j 项目的继承者,在 log4j 停止的地方接手。
SiftingAppender 在引用和配置嵌套附加程序方面是独一无二的。在上面的示例中,在 SiftingAppender 中会有嵌套的 FileAppender 实例,每个实例由与“userid”MDC 键关联的值标识。每当“userid”MDC 键被分配一个新值时,都会从头开始构建一个新的 FileAppender 实例。SiftingAppender 跟踪它创建的附加程序。30 分钟未使用的 Appender 将被自动关闭并丢弃。
在示例中,他们根据 MDC 值为每个用户生成单独的日志文件。根据您的需要,可以使用其他 MDC 值。
这对于 log4j 也是可能的。您可以通过实现自己的附加程序来做到这一点。我想最简单的方法是继承 AppenderSkeleton。
所有日志记录事件append(LoggingEvent event)
都以您必须实现的方法结束。
在该方法中,您可以通过以下方式访问 MDCevent.getMDC("nameOfTheKeyToLookFor");
然后您可以使用此信息打开要写入的文件。查看标准附加程序(如RollingFileAppender )的实现以找出其余部分可能会有所帮助。
我自己在一个应用程序中使用了这种方法,将不同线程的日志分离到不同的日志文件中,效果很好。
我努力在 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;
}
}
希望这个对你有帮助。
自 20-01-2020 起,这现在是 Log4j 的默认功能。
要实现这一点,您只需使用带有 MDC的RoutingAppender 。
例子:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Appenders>
<Routing name="Analytics" ignoreExceptions="false">
<Routes>
<Script name="RoutingInit" language="JavaScript"><![CDATA[
// This script must return a route name
//
// Example from https://logging.apache.org/log4j/2.x/manual/appenders.html#RoutingAppender
// on how to get a MDC value
// logEvent.getContextMap().get("event_type");
//
// but as we use only one route with dynamic name, we return 1
1
]]>
</Script>
<Route>
<RollingFile
name="analytics-${ctx:event_type}"
fileName="logs/analytics/${ctx:event_type}.jsonl"
filePattern="logs/analytics/$${date:yyyy-MM}/analytics-${ctx:event_type}-%d{yyyy-dd-MM-}-%i.jsonl.gz">
<PatternLayout>
<pattern>%m%n</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
</RollingFile>
</Route>
</Routes>
<!-- Created appender TTL -->
<IdlePurgePolicy timeToLive="15" timeUnit="minutes"/>
</Routing>
</Appenders>
<Loggers>
<Logger name="net.bytle.api.http.AnalyticsLogger" level="debug" additivity="false">
<AppenderRef ref="Analytics"/>
</Logger>
</Loggers>
</Configuration>
要了解更多信息,请参阅Log4j - 如何将消息路由到动态创建的日志文件。