【问题标题】:Customize SLF4J Logger自定义 SLF4J 记录器
【发布时间】:2016-11-04 16:12:20
【问题描述】:

我正在尝试找到一种不错的方法来为我的日志添加前缀,而无需在每次调用时都传递它,也无需再次实例化 Logger。

目的是单独跟踪 Rest 调用。 (前缀会在每次调用时使用 UUID 重新生成)

这就像

@RestController
class MyClass {
    //Here the prefix is initialise once
    //default value is X
    Logger LOG = LoggerFactory.getLogger(MyClass.class);

    @RequestMapping("/a")
    void methodA() {
        LOG.debug("foo");
    }   

    @RequestMapping("/b")
    void methodB() {    
        LOG.setPrefix("B");

        LOG.debug("bar");
}

这个输出

[...] [prefix X] foo
[...] [prefix B] bar

【问题讨论】:

  • 你在 SLF4J 中使用什么日志框架?
  • 使用 spring 进行回退

标签: spring logging logback slf4j


【解决方案1】:

正如您所说,您正在使用 Logback,这里有几个选项可以做您想做的事情:

标记

每个日志条目都可以为其建立一个“标记”。 (我见过的最好的文档是在SLF4J FAQ。)类似于:

class MyClass {
    Marker methodBMarker = MarkerFactory.getMarker("B");
    Logger logger = LoggerFactory.getLogger(MyClass.class);
    …
    void methodB() {    
        logger.debug(methodBMarker, "bar");
    }
}

您需要更新每个方法中的所有日志条目以使用适当的标记。然后,您可以将%marker 放入您的布局中,以将日志条目的标记放入日志中。

MDC

另一个选项是使用“Mapped Diagnostic Context”功能为每个日志条目指定当前“上下文”。

class MyClass {
    Logger logger = LoggerFactory.getLogger(MyClass.class);
    …
    void methodB() {
        MDC.put("method", "b");
        try {
            …
            logger.debug("bar");
            …
        } finally {
            MDC.clear();
        }
    }
}

然后,您将在布局中使用 %mdc{method} 来输出该特定 MDC 值。请注意,MDC 确实旨在用于每个线程的值,例如特定于 Web 连接的东西,因此当您离开想要记录值的上下文时,确保将其清除掉您不想要的东西很重要在。

【讨论】:

    【解决方案2】:

    有关如何使用 MDC 的示例,请参阅 http://www.slf4j.org/extensions.html#event_logger。您不必使用 EventLogger。一旦您在 MDC 中设置了内容,它们就会出现在每个日志记录中。

    标记不符合您的条件,因为必须在每次调用时指定它。

    【讨论】:

      【解决方案3】:

      这里解释了我的 MDC 实现,以与 MDC 分享我的实验。

      //In this abstract class i'm defining initLogData methods to set MDC context
      //It would be inherited by Controller and other classes who needs logging with traced transactions
      public abstract class AbstractService {
          protected LogData initLogData() {
              return LogData.init();
          }
      
          protected LogData initLogData(String tName) {
              return LogData.init(tName);
          }
      }
      
      //LogData holds the MDC logic
      public class LogData {
          private final static int nRandom = 8;
          //this keys are defined in logback pattern (see below)
          private final static String tIdKey = "TID";
          private final static String tNameKey = "TNAME";
      
          //Transaction id
          private String tId;
          //Transaction name
          private String tName;
      
          public String getTId() {
              return tId;
          }
      
          public void setTId(String tId) {
              this.tId = tId;
          }
      
          public String gettName() {
              return tName;
          }
      
          public void settName(String tName) {
              this.tName = tName;
          }
      
          //random transaction id
          //I'm not using uuid since its too longs and perfect unicity is not critical here
          public String createTId(){
              Random r = new Random();
              StringBuilder sb = new StringBuilder();
              while(sb.length() < nRandom){
                  sb.append(Integer.toHexString(r.nextInt()));
              }
              return sb.toString().substring(0, nRandom);
          }
      
          //private constructors (use init() methods to set LogData)
          private LogData(String tId, String tName) {
              this.tId = tId;
              this.tName = tName;
          }
      
          private LogData(String tName) {
              this.tId = createTId();
              this.tName = tName;
          }
      
          private LogData() {
              this.tId = createTId();
          }
      
          //init MDC with cascading calls processing (using same id/name within same context
          //even if init() is called again)
          public static LogData init(String tName) {
              String previousTId = MDC.get(tIdKey);
              String previousTName = MDC.get(tNameKey);
      
              MDC.clear();
              LogData logData = null;
              if(previousTId != null) {
                  logData = new LogData(previousTId, previousTName);
              } else {
                  logData = new LogData(tName);
              }
              MDC.put(tIdKey, logData.getTId());
              MDC.put(tNameKey, logData.gettName());
              return logData;
          }
      
          //init MDC without cascading calls management (new keys are generated for each init() call)
          public static LogData init() {
              MDC.clear();
              LogData logData = new LogData();
      
              MDC.put(tIdKey, logData.getTId());
              return logData;
          }
      
      }
      
      //logback.xml : values to include in log pattern
      [%X{TID}] [%X{TNAME}]
      
      @RestController
      @RequestMapping("/test")
      public class RestControllerTest extends AbstractRestService {
          private final Logger LOG = LoggerFactory.getLogger(ServiceRestEntrypointStatus.class);
      
          @RequestMapping(value="/testA")
          public void testA() {
              initLogData("testA");
              LOG.debug("This is A");
          }
      
          @RequestMapping(value="/testB")
          public void testB() {
              initLogData("testA");
              LOG.debug("This is B");
          }
      
          @RequestMapping(value="/testC")
          public void testC() {
              initLogData("testC");
              LOG.debug("This is C");
              testA();
              testB();
          }
      }
      

      调用 RestControllerTest 映射的 /test/testA 产生:

      [fdb5d310] [testA] This is A
      

      调用 /test/testC 产生(即使在子方法中调用 initLogData 也会保留 id 和 name):

      [c7b0af53] [testC] This is C
      [c7b0af53] [testC] This is A
      [c7b0af53] [testC] This is B
      

      【讨论】:

        猜你喜欢
        • 2011-02-08
        • 2021-10-27
        • 2012-10-08
        • 2023-04-01
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 2013-01-09
        • 1970-01-01
        相关资源
        最近更新 更多