Log日志

作者: lionel880 | 来源:发表于2018-11-12 15:29 被阅读0次

1.关于MDC

为什么要用log的MDC,它是用来解决什么问题?
多线程,高并发下,多个线程打印,无法形成连贯的思路。
MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
MDC中的put方法其实就是讲键值对放入一个Hashtable对象中,然后赋值给当前线程的ThreadLocal.ThreadLocalMap对象,即threadLocals,这保证了各个线程的在MDC键值对的独立性。

public static void put(String key, Object o)
  {
    mdc.put0(key, o);
  }

  private void put0(String key, Object o)
  {
    if (this.java1) {
      return;
    }
    Hashtable ht = (Hashtable)((ThreadLocalMap)this.tlm).get();
    if (ht == null) {
      ht = new Hashtable(7);
      ((ThreadLocalMap)this.tlm).set(ht);
    }
    ht.put(key, o);
  }

在 Web 应用中增加用户跟踪功能
参考资料:https://www.ibm.com/developerworks/cn/web/wa-lo-usertrack/index.html#fig1

1.2 子线程打印

我们知道MDC的本质实现是通过ThreadLocal实现的,那么如何实现子线程继承 父线程的信息。
基本原理:InheritableThreadLocal
一、基础知识:InheritableThreadLocal<T> extends ThreadLocal<T>
重写了父类的几个方法,使得创建子线程时,会继承父线程的ContextMap中的内容
测试案例

 @Test 
public void TestInheritableThreacLocal() { 
    final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<String>(); 
    inheritableThreadLocal.set("fatherName"); 
    new Thread(new Runnable() { 
        public void run() { 
            String childThreadName = inheritableThreadLocal.get(); 
            log.info("childThreadName is:{}", childThreadName); 
    } }).start(); 
  try { 
        Thread.sleep(100000); 
  } catch (InterruptedException e) { 
        e.printStackTrace(); 
  } 
}

结果为

2019-02-25 10:43:44,300 [SpringClientMainTest] [Thread-1] INFO com.magpie.quickstarts.v2.SpringClientMainTest - childThreadName is:fatherName
可以继承父线程内容

MDC默认实现为ThreadLocal,但可以指定为InheritableThreadLocal
指定方式,官方有3重,1、JVM参数 2、环境变量 3、JAVA 系统变量,以下测试为java系统参数设置
XML配置文件格式
<Property name="pattern">%d{yyyy-MM-dd HH:mm:ss,SSS} %X{traceId} [%c{1}] [%t] %-5level %logger{36} - %msg%n</Property>

测试案例为:
static { 
        System.setProperty("isThreadContextMapInheritable", "true"); 
} 
private static Logger log = LoggerFactory.getLogger(SpringClientMainTest.class);

@Test 
public void testMDCInheritable() { 

    MDC.put("traceId", "123456"); 
    log.info("Father thread traceId"); 

    Thread childThread = new Thread(new Runnable() { 
    public void run() { 
        String childTraceId=MDC.get("traceId"); 
        MDC.put("traceId",childTraceId); 
        while (true) { 
            log.info("Child thread traceId"); 
            try { 
                    Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                    e.printStackTrace(); 
            } 
    } 
} 
}); 
        childThread.start(); 

        try { 
                log.info("Father thread traceId"); 
                    Thread.sleep(100000); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                } 

}

日志结果为:
2019-02-25 12:30:20,023 123456 [SpringClientMainTest] [main] INFO com.magpie.quickstarts.v2.SpringClientMainTest - Father thread traceId 
2019-02-25 12:30:20,216 123456 [SpringClientMainTest] [main] INFO com.magpie.quickstarts.v2.SpringClientMainTest - Father thread traceId 
2019-02-25 12:30:20,244 123456 [SpringClientMainTest] [Thread-1] INFO com.magpie.quickstarts.v2.SpringClientMainTest - Child thread traceId 
2019-02-25 12:30:21,244 123456 [SpringClientMainTest] [Thread-1] INFO com.magpie.quickstarts.v2.SpringClientMainTest - Child thread traceId

TIP:不知道为什么,子线程中只能get一次,第二次就会为null,比较忙,等闲的时候排查一下代码

2.log4j2

log4j2 相比于1,配置等改动较大。生产比较实用的功能有:

  • 异步打印,提升程序性能
  • 动态配置,这一点对于某些debug日志平时,关闭,关键时刻打开非常方便

实际中,还有一个,我们一般将root设为一个级别,而在排查问题等时,将一些其他的可以针对性配置,如mybatis,你只需要

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Logger name="xxx.xxx.db.mapper" level="debug" addivity="true">
    </Logger>
    <Root level="info">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

利用addictivity 多余性,可以直接在root的输出中添加进去,结合动态配置的功能,在生产排查问题时,就可以做到不停机的情况下,针对性排查了

2.1 异步打印深化

log4j2有多种异步打印方式,其中最推荐的一种需要依赖Disruptor框架。
Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作

相关文章

网友评论

      本文标题:Log日志

      本文链接:https://www.haomeiwen.com/subject/xlfbrftx.html