SLF4J MDC机制与链路追踪

MDC简介

  • MDC Mapped Diagnostic Contexts slf4j+logBack
  • 使用简单在代码中MDC.put将指定值加到线程上下文中的Map,在同线程中使用时get 出来,最后调用clear方法清理要丢弃的数据
Slf4j MDC :
   对外接口,就是对参数进行校验,然后调用MDCAdapter的方法实现。
Logback MDC :
   LogbackMDCAdapter类实现MDCAdapter接口,实现 put、get、remove等方法。
   copyOnThreadLocal:存储每个线程的多个变量
   当在logback.xml中配置了%X{key} 或 SiftingAppender的,在需要输出日志的时候,从MDC中获取对应的key值,然后append到日志字符串中或生成文件路径,然后输出。

使用实例

必须得jar


    log4j
    log4j
    1.2.17
  
  
      org.slf4j
      slf4j-log4j12
      1.7.21
   
自定义日志文件与格式 logback.xml

使用 %{key} 作为占位符




    
        
            
            %d{yyyy-MM-dd HH:mm:ss} %-5level [%logger{16}] [%X{REQID}] [%X{SID}] [%X{UID}] %msg%n
        
    
    
    
        /logs/logs.log
        
            
            
            /logs/logs.%d{yyyy-MM-dd}.log 
            
            
            
            6
        

        true
        
             
            %d{yyyy-MM-dd HH:mm:ss} %-5level [%logger{16}] [%X{REQID}] [%X{SID}] [%X{UID}] %msg%n
        
    

 MDC.put("TESE_ID", String.valueOf(Thread.currentThread().getId()));
        logger.info("测试日志中是否有当前自定义log");
  • 以上简单场景 单线程

  • 线程池场景
可以在每个任务之前设置子线程的MDC数据
- 在记录日志的时候,一般情况下我们会使用MDC来存储每个线程的特有参数,如身份信息等,以便更好的查询日志。
- 但是Logback在最新的版本中因为性能问题,不会自动的将MDC的内存传给子线程。
- 所以Logback建议在执行异步线程前先通过MDC.getCopyOfContextMap()方法将MDC内存获取出来,再传给线程。
- 并在子线程的执行的最开始调用MDC.setContextMap(context)方法将父线程的MDC内容传给子线程。

import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.Map;
public class MdcThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    @Override
    public void execute(Runnable runnable) {
        // 获取父线程MDC中的内容,必须在run方法之前,否则等异步线程执行的时候有可能MDC里面的值已经被清空了,这个时候就会返回null
        Map context = MDC.getCopyOfContextMap();
        super.execute(() -> run(runnable, context));
    }
    
    /**
     * 子线程委托的执行方法
     *
     * @param runnable {@link Runnable}
     * @param context  父线程MDC内容
     */
    private void run(Runnable runnable, Map context) {
        // 将父线程的MDC内容传给子线程
        MDC.setContextMap(context);
        try {
            // 执行异步操作
            runnable.run();
        } finally {
            // 清空MDC内容
            MDC.clear();
        }
    }
}

  • Web请求session 拦截器 LogInterceptor
public class LogInterceptor extends HandlerInterceptorAdapter {
    /**
     * 会话ID
     */
    private final static String SESSION_KEY = "sessionId";
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        // 删除SessionId
        MDC.remove(SESSION_KEY);
    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                           Object arg2, ModelAndView arg3) throws Exception {
    }

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {

        // 设置SessionId
        String token = UUID.randomUUID().toString().replace("-", "");
        MDC.put(SESSION_KEY, token);
        return true;
    }
}

  • Dubbo rpc调用记录请求消息 filter
package com.xxx;
 
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
import org.slf4j.MDC; 
public class XxxFilter implements Filter {
    public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
        // before filter ...
        Result result = invoker.invoke(invocation);
        
        // 将所有参数封装到 Map中
        Object [] objs=invocation.getArguments();
        Map paramMap = new ConcurrentHashMap(20);
        if(cla.equals(String.class)) {
            paramMap.put(cla.toString(),(String)objs[i]);
        }else if(cla.equals(Integer.class)){
            paramMap.put(cla.toString(),(Integer)objs[i]);
        }else if (cla.equals(Map.class)) {
            paramMap.putAll((Map) objs[i]);
        }    
        // after filter ...
        return result;
    }
}

你可能感兴趣的:(分布式)