【问题标题】:Pass Logger as argument to another class将 Logger 作为参数传递给另一个类
【发布时间】:2013-10-13 08:48:14
【问题描述】:
public class SessionLogger {

    private final String sessionId;

    public SessionLogger(String sessionId) {
        this.sessionId = sessionId;
    }

    public void info(Log log, String message, Object... args) {
        log.info(formatMessage(message, args));
    }

    public void error(Log log, String message, Throwable t, Object... args) {
        log.error(formatMessage(message, args), t);
    }

    private String formatMessage(String message, Object... args) {
        for (int i = 0; i < args.length; i++) {
            message = message.replaceFirst("\\{\\}", args[i].toString());
        }
        return String.format("SessionId [%s]: %s", sessionId, message);
    }
}

我想要做的是将 Logger 实例传递给 SessionLogger 类,我想查看初始化 Logger 的类名。

public class A {
  private static final Log log = LogFactory.getLog(A.class)
  public void doIt() {
     sessionLogger.info(log, "hello world");
  }
}

我希望在日志消息中看到 A 类而不是 SessionLogger:

2013-10-07 00:29:27,328  INFO [main] (SessionLogger.java:17) - SessionId [123]: Hello world

我在类路径中有 commons-logging.jar 和 log4j-1.2.16.jar。 Logger 是 org.apache.commons.logging.Log 的一个实例

更新

刚刚发布这是预期的行为,因为Logger 记录了调用 log 方法的代码行。所以应该以其他方式完成

【问题讨论】:

  • >> 导致 Logger 记录调用 log 方法的代码行
  • 是的。但是记录代码行也很好。由于 log4j 调用 Thread.currentThread().getStackTrace() ... 在运行时获取行号,所以在这种情况下我不能使用这种方法

标签: java logging log4j apache-commons-logging


【解决方案1】:

看了一眼你想要实现的目标后,我相信与其做那些棘手的 SessionLogger 东西,不如使用 MDC 可能是一个更合理的选择。

在 MDC 中设置会话 ID(取决于您的应用程序设计。对于 Web 应用程序,有一个 servlet 过滤器进行 MDC 设置工作是合理的),并让每个人都可以像往常一样简单地使用记录器。通过使用适当的模式,您可以将会话 ID 放入结果日志消息中。

不确定 MDC 是否在 Apache Commons Logging 中公开,但它在 SLF4J 或 Log4J 中可用。

只是好奇,有什么理由使用 ACL(众所周知,它有很多问题)。考虑改用近年来更广泛采用的 SLF4J。

【讨论】:

  • 感谢分享 MDC 方法。但它不适合我,因为它依赖于 ThreadLocal 变量。
  • 我很想知道为什么依赖 ThreadLocal 是个问题?除非您正在做一些棘手的多线程工作,否则 ThreadLocal 很少会成为问题。 ThreadLocal 的使用量超出了您的预期,例如在事务管理等方面。
  • 是的,有很多多线程、执行器、生产者/消费者等。这不是一个简单的网络应用程序
  • 但是,我仍然使用 MDC 是要走的路。在您原来的方法中,记录器的调用者应该传入会话 ID,或者有人需要创建一个带有会话 ID 的记录器并传递记录器。没有什么特殊原因不能传递会话信息,并在适当的地方进行相应的 MDC 设置。美妙之处在于,这种 MDC 设置大部分可以隐藏在实际处理逻辑中(而当前处理逻辑知道会话 ID/特殊会话记录器),并且处理逻辑可以进行正常记录。
【解决方案2】:

我认为解决方案是编写本文中介绍的自定义 Log4j 模式布局:http://fw-geekycoder.blogspot.com/2010/07/creating-log4j-custom-patternlayout.html

那么您将不需要 SessionLogger,这将大大简化您的代码。

布局:

public class MyPatternLayout extends PatternLayout {

    @Override
    protected PatternParser createPatternParser(String pattern) {
        return new MyPatternParser(pattern);
    }
}

模式解析器:

public class MyPatternParser extends PatternParser {

    private static final char USERNAME_CHAR = 'S';

    public MyPatternParser(String pattern) {
        super(pattern);
    }

    @Override
    protected void finalizeConverter(char c) {
        switch (c) {
            case USERNAME_CHAR:
                currentLiteral.setLength(0);
                addConverter(new MyPatternConverter());
                break;
            default:
                super.finalizeConverter(c);
        }
    }
}

模式转换器:

public class MyPatternConverter extends PatternConverter {
    @Override
    protected String convert(LoggingEvent event) {
        // Retrieve SessionID
        return "123";
    }
}

Log4j 配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="MyPatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss,SSS} %-5p (%F:%L) - Session ID:%S %m%n"/>
        </layout>
    </appender>

    <root>
        <priority value ="debug" />
        <appender-ref ref="console" />
    </root>

</log4j:configuration>

【讨论】:

    猜你喜欢
    • 2020-03-14
    • 2012-10-18
    • 2023-01-18
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 1970-01-01
    • 1970-01-01
    • 2017-08-07
    相关资源
    最近更新 更多