13

我在一个使用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 日志数据仅由从 log4.configuration 初始化的 Appenders “看到”文件,但不是由运行时创建的 Appenders。所以,回到第一方。

经过一番调查,我发现新的和改进的LogBack有一个附加程序 - SiftingAppender - 它完全可以满足我们的需要,即在独立文件上进行线程级日志记录。

目前,迁移到 LogBack 不是一种选择,所以,被 Log4J 卡住了,我怎样才能实现类似 SiftingAppender 的功能并让 Spring 满意呢?

注意:Spring 仅用于JdbcTemplate功能,没有 IOC;为了将 Spring 的 Commons Logging “挂钩”到 Log4J,我在 log4j.properties 文件中添加了这一行:

log4j.logger.org.springframework=调试

按照这里的指示。

4

5 回答 5

4

在 Log4j2 中,我们现在可以使用RoutingAppender

RoutingAppender 评估 LogEvents,然后将它们路由到从属 Appender。目标 Appender 可以是之前配置的 appender,可以通过其名称引用,也可以根据需要动态创建 Appender。

从他们的常见问题解答

如何动态写入单独的日志文件? 查看 RoutingAppender。您可以在配置中定义多个路由,并将值放入 ThreadContext 映射中,以确定该线程中的后续事件记录到哪个日志文件。

于 2016-09-04T09:50:28.197 回答
3

LogBack 通过slf4j api访问。有一个名为jcl-over-sjf4j的适配器库,它公开了公共日志接口,但将所有日志记录到 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>

(并将 commons-logging 添加到排除列表中,请参见此处

于 2009-12-20T16:03:28.633 回答
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;
  }
}

希望这个对你有帮助。

于 2013-06-18T19:34:33.433 回答
0

你看过 log4j.NDC 和 MDC 吗?这至少允许您将线程特定数据标记到日志记录中。不完全是您的要求,但可能有用。这里有讨论。

于 2009-12-24T15:19:08.790 回答
0

I like to include all of the slf4j facades/re-routers/whateveryoucallthem. Also note the "provided" hack, which keeps dependencies from pulling in commons logging; previously I was using a fake empty commons logging library called version-99.0-does-not-exist.

Also see 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>
于 2009-12-22T08:10:11.823 回答