• Java日志框架


    日志框架

    slf4j

    slf4j是一个面向java Logging框架的开源包,其将各个不同的日志框架抽象成一套统一的API进行操作。slf4j不参与具体的日志代码实现,仅仅是根据程序的配置来绑定具体的日志系统。因此,基于slf4j可以让日志使用独立于具体的日志框架。

    如上图,slf4j配合具体日志框架的桥接可以在多个日志框架之间切换。

    扩展:Java日志框架的行号是怎么获取到的?

    用过很多框架,如log4j,logback等。每种框架都支持在日志中输出行号,那框架是如何得到行号的呢?

    以logback来说。

    在发起logger.info("xxx");调用时,会build一个LoggingEvent对象。在此对象中有获取当前当前调用栈的方法:

    1     public StackTraceElement[] getCallerData() {
    2         if (callerDataArray == null) {
    3             callerDataArray = CallerData
    4                             .extract(new Throwable(), fqnOfLoggerClass, loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages());
    5         }
    6         return callerDataArray;
    7     }

    在这里,通过new Threwable(),然后遍历StackTraceElement来获取当前调用有效栈。因为堆栈是从当前点往外逐层叠加的,那遍历栈时判断栈归属类是否为ch.qos.logback.classic.Logger.class.getName() || org.apache.log4j.Category || org.slf4j.Logger || 是否在框架自身的package内。当跳出此循环后,即定位到日志输出行。在StackTraceElement中有lineNumber记录,直接读取即可得到此行号。此过程如下:

     1     /**
     2      * Extract caller data information as an array based on a Throwable passed as
     3      * parameter
     4      */
     5     public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, final int maxDepth, List<String> frameworkPackageList) {
     6         if (t == null) {
     7             return null;
     8         }
     9 
    10         StackTraceElement[] steArray = t.getStackTrace();
    11         StackTraceElement[] callerDataArray;
    12 
    13         int found = LINE_NA;
    14         for (int i = 0; i < steArray.length; i++) {
    15             if (isInFrameworkSpace(steArray[i].getClassName(), fqnOfInvokingClass, frameworkPackageList)) {
    16                 // the caller is assumed to be the next stack frame, hence the +1.
    17                 found = i + 1;
    18             } else {
    19                 if (found != LINE_NA) {
    20                     break;
    21                 }
    22             }
    23         }
    24 
    25         // we failed to extract caller data
    26         if (found == LINE_NA) {
    27             return EMPTY_CALLER_DATA_ARRAY;
    28         }
    29 
    30         int availableDepth = steArray.length - found;
    31         int desiredDepth = maxDepth < (availableDepth) ? maxDepth : availableDepth;
    32 
    33         callerDataArray = new StackTraceElement[desiredDepth];
    34         for (int i = 0; i < desiredDepth; i++) {
    35             callerDataArray[i] = steArray[found + i];
    36         }
    37         return callerDataArray;
    38     }

    扩展日志框架在日志内容中插入自定义的内容时,需要注意框架包声明。

    因为是在扩展中调用具体日志框架实例,那日志输出的行号会定位到你的扩展实现中,此时丢失业务日志输出行号。从上述逻辑可以看出,在定位有效堆栈的时候,框架package是一个过滤条件,那将扩展所在的package加入框架package中即可解决此问题。

    相比声明框架包外,使用标准的MDC是更推荐的一种实现方式。支持MDC的日志框架只有logback与log4j。其余的日志框架,在slf4j下会走到空处理,需要通过代码从MDC中读出来。

    MDC本质是一个维护一个ThreadLocal对象,存储每个线程的上下文,然后在日志文件中用%X{param}读取变量并输出到日志文件中。

    这样看来,日志要获取输出点行号是比较复杂的一个过程,日志输出如果过多,节点资源损耗预计比较可观。

  • 相关阅读:
    PCLint
    pthread_join
    作业过程查找
    sqlcmd (转)
    整合问题
    PATINDEX
    回归Dos操作的快感,进入PowerShell世界 (转)
    Javascript 面向对象编程(一):封装
    理解闭包
    Javascript 面向对象编程(三):非构造函数的继承
  • 原文地址:https://www.cnblogs.com/asfeixue/p/8074829.html
Copyright © 2020-2023  润新知