失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 跟我学Springboot开发后端管理系统9:AOP+logback+MDC日志输出

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

时间:2023-08-18 20:31:34

相关推荐

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

MDC介绍

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

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

MDC原理

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

publicclassMDC{//清空 map 所有的条目。publicstaticvoidclear();//根据key值返回相应的对象publicstaticobjectget(Stringkey);//返回所有的key值.publicstaticEnumerationgetKeys();//把key值和关联的对象,插入map中publicstaticvoidput(Stringkey,Objectval),//删除key对应的对象publicstaticremove(Stringkey)}

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

publicstaticvoidput(Stringkey,Stringval)throwsIllegalArgumentException{if(key==null){thrownewIllegalArgumentException("keyparametercannotbenull");}if(mdcAdapter==null){thrownewIllegalStateException("MDCAdaptercannotbenull.Seealso"+NULL_MDCA_URL);}mdcAdapter.put(key,val);}

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

publicclassStaticMDCBinder{/***Theuniqueinstanceofthisclass.*/publicstaticfinalStaticMDCBinderSINGLETON=newStaticMDCBinder();privateStaticMDCBinder(){}/***Currentlythismethodalwaysreturnsaninstanceof*{@linkStaticMDCBinder}.*/publicMDCAdaptergetMDCA(){returnnewLogbackMDCAdapter();}

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

publicvoidput(Stringkey,Stringval)throwsIllegalArgumentException{if(key==null){thrownewIllegalArgumentException("keycannotbenull");}Map<String,String>oldMap=copyOnThreadLocal.get();IntegerlastOp=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。

finalThreadLocal<Map<String,String>>copyOnThreadLocal=newThreadLocal<Map<String,String>>();

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

在Matrix-Web中使用MDC

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

publicclassLogFilterimplementsFilter{publicstaticfinalStringREQUEST_ID="REQUEST_ID";@OverridepublicvoiddoFilter(ServletRequestservletRequest,ServletResponseservletResponse,FilterChainfilterChain)throwsIOException,ServletException{HttpServletRequesthttpServletRequest=(HttpServletRequest)servletRequest;//REQUEST_ID由前端生成,MDC.put(REQUEST_ID,getRequestId(httpServletRequest));filterChain.doFilter(servletRequest,servletResponse);MDC.clear();}}}

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

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender"><encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-ddHH:mm:ss.SSS}%X{REQUEST_ID}[%thread]%-5level%logger{50}-%msg%n</pattern></encoder></appender><!--按照每天生成日志文件--><appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名--><FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.log</FileNamePattern><!--日志文件保留天数--><MaxHistory>30</MaxHistory></rollingPolicy><encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-ddHH:mm:ss.SSS}%X{REQUEST_ID}[%thread]%-5level%logger{50}-%msg%n</pattern></encoder><!--日志文件最大的大小--><triggeringPolicyclass="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开发后端管理系统1:概述

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

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

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

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

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

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

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

如果觉得《跟我学Springboot开发后端管理系统9:AOP+logback+MDC日志输出》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。