7

是否有可能在运行时使用 MDC 来命名日志文件。

我有一个使用 tomcat docbase 同时被不同名称调用的 Web 应用程序。所以我需要为他们每个人都有单独的日志文件。

4

4 回答 4

11

这可以在Log4J 的继承者Logback中完成。

Logback 旨在作为流行的 log4j 项目的继承者,在 log4j 停止的地方接手。

查看Sifting Appender的文档

SiftingAppender 在引用和配置嵌套附加程序方面是独一无二的。在上面的示例中,在 SiftingAppender 中会有嵌套的 FileAppender 实例,每个实例由与“userid”MDC 键关联的值标识。每当“userid”MDC 键被分配一个新值时,都会从头开始构建一个新的 FileAppender 实例。SiftingAppender 跟踪它创建的附加程序。30 分钟未使用的 Appender 将被自动关闭并丢弃。

在示例中,他们根据 MDC 值为每个用户生成单独的日志文件。根据您的需要,可以使用其他 MDC 值。

于 2011-11-03T15:58:07.493 回答
10

这对于 log4j 也是可能的。您可以通过实现自己的附加程序来做到这一点。我想最简单的方法是继承 AppenderSkeleton

所有日志记录事件append(LoggingEvent event) 都以您必须实现的方法结束。

在该方法中,您可以通过以下方式访问 MDCevent.getMDC("nameOfTheKeyToLookFor");

然后您可以使用此信息打开要写入的文件。查看标准附加程序(如RollingFileAppender )的实现以找出其余部分可能会有所帮助。

我自己在一个应用程序中使用了这种方法,将不同线程的日志分离到不同的日志文件中,效果很好。

于 2011-11-03T16:06:07.873 回答
6

我努力在 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;
  }
}

希望这个对你有帮助。

于 2013-06-18T19:37:55.900 回答
1

自 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 - 如何将消息路由到动态创建的日志文件

于 2020-01-20T08:12:57.537 回答