log4j中使用MDC动态命名日志文件

发布于 2024-12-13 08:11:55 字数 96 浏览 1 评论 0原文

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

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

Is it possible some how to use MDC to name the log file at run time.

I have a single web application which is being called by different names at the same time using tomcat docbase. So i need to have separate log files for each of them.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

素罗衫 2024-12-20 08:11:55

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

Logback 旨在作为流行的 log4j 项目的后继者,继承 log4j 的不足。

请参阅筛选 Appender 的文档

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

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

This can be accomplished in Logback, the successor to Log4J.

Logback is intended as a successor to the popular log4j project, picking up where log4j leaves off.

See the documentation for Sifting Appender

The SiftingAppender is unique in its capacity to reference and configure nested appenders. In the above example, within the SiftingAppender there will be nested FileAppender instances, each instance identified by the value associated with the "userid" MDC key. Whenever the "userid" MDC key is assigned a new value, a new FileAppender instance will be built from scratch. The SiftingAppender keeps track of the appenders it creates. Appenders unused for 30 minutes will be automatically closed and discarded.

In the example, they generate a separate log file for each user based on an MDC value.
Other MDC values could be used depending on your needs.

蘑菇王子 2024-12-20 08:11:55

使用 log4j 也可以实现这一点。您可以通过实现自己的附加程序来做到这一点。我想最简单的方法是
子类 AppenderSkeleton

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

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

,然后您可以使用此信息打开要写入的文件。
查看标准附加程序的实现可能会有所帮助,例如 RollingFileAppender 来弄清楚剩下的事情。

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

This is also possible with log4j. You can do this by implementing your own appender. I guess the easiest way is to
subclass AppenderSkeleton.

All logging events end up in the append(LoggingEvent event) method you have to implement.

In that method you can access the MDC by event.getMDC("nameOfTheKeyToLookFor");

Then you could use this information to open the file to write to.
It may be helpful to have a look at the implementation of the standard appenders like RollingFileAppender to figure out the rest.

I used this approach myself in an application to separate the logs of different threads into different log files and it worked very well.

2024-12-20 08:11:55

我花了一段时间在 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 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.

一指流沙 2024-12-20 08:11:55

截至 2020 年 1 月 20 日,这现已成为 Log4j 的默认功能。

要实现这一点,您只需要使用 RoutingAppender与MDC。

示例:

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

As of 20-01-2020, this is now a default functionality of Log4j.

To achieve that you just need to use a RoutingAppender with MDC.

Example:

<?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>

To known more, see Log4j - How to route message to log file created dynamically.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文