MDC介绍

在比较复杂的应用中,一个请求需要走很多个方法的处理,怎么样才能快速查找一个请求的全部日志呢。在分布式系统中,我们可以用链路追踪,比如zipkin、skywalking去快速查找日志,从而定位问题。在比较复杂的单体管理系统中,我们可以使用slf4j的MDC去实现类似的功能。

MDC ( Mapped Diagnostic Contexts ),是为了便于我们诊断线上问题而出现的方法工具类。使用ThreadLocal实现的,在MDC中的变量,每个线程都会有单独的副本,多线程不会相互干扰。MDC功能,logback 和 log4j 提供了支持。在Matrix-Web中,使用logback和slf4j进行日志的答应。

MDC原理

MDC类是一个静态工具类,对外提供了类似Map的接口:

public class MDC { // 清空 map 所有的条目。public static void clear(); // 根据 key 值返回相应的对象public static object get(String key); // 返回所有的 key 值 . public static Enumeration getKeys(); // 把 key 值和关联的对象,插入 map 中public static void put(String key, Object val), // 删除 key 对应的对象public static  remove(String key)
}

为了弄清楚MDC的原理,我们来跟下MDC的源码,比如put方法,最终交给mdcAdapter去处理。

public static void put(String key, String val) throws IllegalArgumentException {if (key == null) {throw new IllegalArgumentException("key parameter cannot be null");}if (mdcAdapter == null) {throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);}mdcAdapter.put(key, val);}

跟踪代码mdcAdapter是由StaticMDCBinder初始化出现的,即LogbackMDCAdapter的实例。

public class StaticMDCBinder {/*** The unique instance of this class.*/public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();private StaticMDCBinder() {}/*** Currently this method always returns an instance of * {@link StaticMDCBinder}.*/public MDCAdapter getMDCA() {return new LogbackMDCAdapter();}

LogbackMDCAdapter中put方法最终是由copyOnThreadLocal去处理的。

  public void put(String key, String val) throws IllegalArgumentException {if (key == null) {throw new IllegalArgumentException("key cannot be null");}Map<String, String> oldMap = copyOnThreadLocal.get();Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);if (wasLastOpReadOrNull(lastOp) || oldMap == null) {Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);newMap.put(key, val);} else {oldMap.put(key, val);}}

而copyOnThreadLocal是一个ThreadLocal。

final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();

由此可见MDC最终是由ThreadLocal去存放和取key、value的。

在Matrix-Web中使用MDC

在Matrix-web中使用Filter去做MDC的处理,在请求进入业务请求逻辑之前,将前端生成的REQUEST_ID存储在MDC中。当请求的业务逻辑完成后,将MDC清除。

public class LogFilter implements Filter {public static final String REQUEST_ID="REQUEST_ID";@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;//REQUEST_ID由前端生成,MDC.put(REQUEST_ID, getRequestId(httpServletRequest));filterChain.doFilter(servletRequest, servletResponse);MDC.clear();}}
}

在logback.xml中配置打印REQUEST_ID,用这个配置%X{REQUEST_ID}。具体配置如下:

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{REQUEST_ID} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder></appender><!-- 按照每天生成日志文件 --><appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名--><FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.log</FileNamePattern><!--日志文件保留天数--><MaxHistory>30</MaxHistory></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{REQUEST_ID} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder><!--日志文件最大的大小--><triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"><MaxFileSize>10MB</MaxFileSize></triggeringPolicy></appender>

定义一个RespDTO,该类用于Controller统一返回结果,在该类中,会自动requestId赋值给这个类,统一给前端,这样前端页面也能够从请求结果上查到requestId。

public class RespDTO<T> implements Serializable {public int code = 0;public String message = "";public T data;public String requestId;public static RespDTO onSuc(Object data) {RespDTO resp = new RespDTO();String requestId = MDC.get(REQUEST_ID);if (!StringUtils.isEmpty(requestId)) {resp.requestId = requestId;}resp.message="sucess";resp.data = data;return resp;}@Overridepublic String toString() {return "RespDTO{" +"code=" + code +", error='" + message + '\'' +", data=" + data +'}';}
}

使用MDC能够将一个请求的所有业务处理逻辑的日志通过一个唯一的标识串起来,方便日志的排查。

跟我学Springboot开发后端管理系统8:AOP+logback+MDC日志输出相关推荐

  1. 跟我学Springboot开发后端管理系统9:AOP+logback+MDC日志输出

    MDC介绍 在比较复杂的应用中,一个请求需要走很多个方法的处理,怎么样才能快速查找一个请求的全部日志呢.在分布式系统中,我们可以用链路追踪,比如zipkin.skywalking去快速查找日志,从而定 ...

  2. 跟我学Springboot开发后端管理系统8:Matrxi-Web权限设计实现

    上篇文章讲述了Matrix-web整体实现的权限控制的思路.现在来回顾一下: 首先,用户需要登录,填用户名.密码,后端接收到登录请求,进行用户.密码的校验,校验成功后则根据用户名生成Token,并返回 ...

  3. 跟我学Springboot开发后端管理系统7:Matrxi-Web权限设计

    Matrxi-Web权限设计 对于一个后端系统来说,权限是基础设施,是安全保障.没有权限,系统可能随时面临各种风险,所以权限设计对后端系统来说至关重要.在Javaweb开发中,有很多权限开发的框架,比 ...

  4. 跟我学Springboot开发后端管理系统6:缓存框架Caffeine

    Caffeine是一个基于Java8的高性能缓存框架,号称趋于完美.Caffeine受启发于Guava Cache的API,使用API和Guava是一致的.它借鉴了Guava Cache和Concur ...

  5. 跟我学Springboot开发后端管理系统5:数据库读写分离

    在Matrix-web后台管理系统中,使用到了数据库的读写分离技术.采用的开源的Sharding-JDBC作为数据库读写分离的框架.Matrix-Web后台数据库这一块采用的技术栈如下: 使用Myba ...

  6. 跟我学Springboot开发后端管理系统4:数据库连接池Druid和HikariCP

    上一篇文章主要讲解了如何再Matrix-Web中使用Mybatis-Plus,Mybatis-Plus作为Orm框架,连接数据库需要连接数据库的依赖.WEB 系统高并发环境下,频繁的进行数据库连接操作 ...

  7. 跟我学Springboot开发后端管理系统3:Mybatis-Plus实战2

    在上一篇文章讲述了如何使用Mybatis-plus自动生成代码,生成的代码具有单表操作数据库的能力,节约了开发时间.然后讲述了如何在Spring Boot中整合Mybatis-Plus.这篇文章讲述如 ...

  8. 跟我学Springboot开发后端管理系统2:Mybatis-Plus实战

    在Matrix-Web项目中使用Mybatis-Plus作为操作数据库的ORM框架.在市面上常用的ORM框架有hibernetes.mybatis.JPA等,那么为什么选择Mybatis-Plus呢? ...

  9. mybatis plus springboot test_跟我学Springboot开发后端管理系统3:Mybatis-Plus实战2

    文章来源:https://mp.weixin.qq.com/s/YL0FDjn8BczNfIJJ9DgMRA 原文作者:forezp 来源平台:微信公众号 在上一篇文章讲述了如何使用Mybatis-p ...

最新文章

  1. MindCon | 5天啦,你有领取MSG城市专属徽章吗?
  2. python主题壁纸_Python教你如何下载你喜欢的桌面壁纸
  3. mybatisPlus分页限制500条数据
  4. 【static关键字的作用是什么?它用在什么场景?static关键字深度解析来袭】
  5. ubuntu10.04 android编译问题
  6. EOJ_1081_朋友圈
  7. 菜鸟教程-css3学习笔记
  8. mysql安装失败net_mysql安装后.net程序运行出错的解决方法
  9. server接收dtu透传代码_ESP8266 STM32 HTTP json透传实验
  10. MIT自动驾驶导航系统,不看地图也能穿梭乡间野路
  11. 剑指offer面试题55 - I. 二叉树的深度(DFS)(递归)
  12. gitblit如何迁移入gitlab合并迁移_最新gitlab备份迁移方案
  13. 又一打包工具介绍:Installshield 打包安装包心得
  14. 基于go语言的牛牛游戏服务器搭建
  15. python webp图片转化格式
  16. nginx服务器配置
  17. 21世纪青年人最该阅读的书籍清单
  18. Python绘图记录专栏
  19. 【PTA】【C语言】复盘练习——编程题
  20. lqc_10大linux必备运维工具

热门文章

  1. java finalize逃脱_Java对象回收与finalize方法
  2. 源码圈内推信息整理(2018-02-24)
  3. 用TreeSet存储以下数据: “xiaoqiang”、 “zhangsan”、“lisi”、“xiaohua”、 “ruhua”、 “wangcai” 要求按照字符串的长度进行排序后再存储(如
  4. 肠子的小心思(三):通往消化道的入口也很神奇
  5. 【CTF之Crypto】与佛论禅解密~罰亦般諳醯至上闍切羯哆究南缽寫奢婆罰夢梵究怯娑
  6. word 中的文字有点颜色,感觉像色差一样的处理办法 ,你试试
  7. PHP中goto的使用方法
  8. 洛伦兹曲线看财富分配的公平性
  9. Linux文件系统ugo权限划分详解
  10. 2020全球无人机五十强企业