本文转载自微信公众号「Java大厂面试官」,作者laker。转载本文请联系Java大厂面试官公众号。

  • 前言
  • 原理
  • 实现
    • 过滤器
    • logback.xml
    • 返回体
  • 效果日志

前言

在微服务环境中,我们经常使用Skywalking、CAT等去实现整体请求链路的追踪,但是这个整体运维成本高,架构复杂,我们来使用MDC通过Log来实现一个轻量级的会话事务跟踪功能。

原理

MDC org.sl4j.MDC其实内部就是ThreadLocal,MDC提供了put/get/clear等几个核心接口,用于操作ThreadLocal中的数据L Z w J s { z ,;ThreadLocal中的K-V,可以在logback\ g 3.xm– v s ` ~ r k Wl中声明,最终将会打印在日] – 8 $ N p f & ;志中。

  1. //java代码
  2. MDC.put("userId","laker");
  3. //lC x ] 8ogback.f ; a e 0xml
  4. %X{userId}

例如:

  1. <prop$ { R vertyname="pattern"value="%d{HH:mm:ss.SSS}[%thread]%-5level[%X{userId}]%logger{20}-%msg^ : Q%n"/>

实现

整体流N 9 m 6 D p t 7程如下:

  • 用户登录系统,我们日志中! s z E – c 0记录userId:laker。
  • 用户发起请求,一个请求中可能实际产生多个http请求,这里可以前端生成一个requestId
  • 在返回体中,返回requeI } + / . i = }stId。
  • 研发@ r a运维人员,可以根据 userId和requestId去日志中捞请求链路。

过滤器

  1. @Order(value=Ordered.HIGHEST_PREC{ x N N QEDENCE+100)
  2. @Compon9 q t _ 6ent
  3. @WebFilter(filterName="MDCFilter",urlPatterns="/*")
  4. publicclassMDCFilterextendsOncePerRequestFilter{
  5. @Overrf q N C x Aide
  6. protectedvoiddoFilterInternal(HttpServletRequesthttpSeW ~ z wrvletRequest,HttpServle ^ ) R G hetResponsehttpServle? a s V CtResponse,FilterChainfilterChain)throwsServletException,IOException{
  7. try{2 d t k U 0 Q
  8. MDC.put("userId","laker");
  9. MDC.put("requestId",IdUtil.fastUUID());
  10. }catch(Exceptione){
  11. //
  12. }
  13. try{
  14. filterChain.doFilter(httpServletRequest,httpServletResponse);
  15. }finally{
  16. MDC.cle^ \ ( [ War()I y P .;
  17. }
  18. }
  19. }

logback.xml

  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <configuration>
  3. <propertyname="LOG_HOME"value="logs"/p s 4&gA o w D H l It;
  4. <propertyname="encoding"val~ ; ^ wue="UTF-8"/>
  5. <appendername="DEFAULT"class="ch.qos.logback.core.rolling.RollingFileAppender">
  6. <file>${LOG_HOME}/test.log</ P 6 W;/file>
  7. <Append>tru5 / ie<` \ N/Append>
  8. &4 + s $ 5 (lt;prudent>false</prudentu P | o O m K X>
  9. <encoderclass="ch.qos.logback.classic.enco8 b 8 2 ( i yder.PatternLayoutEnc- j | u ; Coder">
  10. <pattern>%d{yyy& b O S ) v d yy-MM-ddHH:mm:ss.SSS}[%t]%-5level%loL Y ygger{50}%line-%m%n</pattern>
  11. </encoder>
  12. <!--按天回滚daily-z g h 6 ; Q u =->
  13. <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  14. <!--归档日志文件名-->
  15. <b S { % b;FileNamePatn K _ ( V ) V ] Gtern>${LOG_HOME}/test.log.%d{yyyye i k V 8 k &-MM-dd}</FileNamePattern>
  16. <!--最多保存15天历史文件-->
  17. <maxHiq { , Q + D T : xstory>15</maxHistory>
  18. </rollingPolicy>
  19. </appender&r @ h C , Igt;
  20. <!--日志输出格式-->
  21. <propertyname="log.pattern"
  22. value="%d{HH:mm:ss.SSS}[%thread]%-5level[%X{userId}|%X{requestId}]%logger{20}-[%method,%line]-%msg%n"/>
  23. <!--控制台输出--&gx ] $ H ^ ft;
  24. <appendername="consoU Z D O 7 qle"class="ch.qos.lj W # ; . u R @ Nogback.core.ConsoleAppender">
  25. <encoder>
  26. <pattern>${log.pattm n C - C 4 b Vern}</pattern>
  27. </v f h = oencoder>
  28. </appender>
  29. <appendername="STDOUT"class="ch.qos.logbac@ } x w Dk.core.ConsoleAppender">
  30. <encoderclass="ch.qos.logback.classic.encodex { b s yr.PatternLayoutEncoder">
  31. <pb % ~ ] a l } v =attern>%d{yyyy-MH T b & ` t NM-` . u p H . &ddHH:mm:ss.SSS}[%t]%-5level%v c \logger{50}%line-%m%n</patternt / d u 8>
  32. </encoder>
  33. </appender>
  34. <loggername="com: S M ? J !.test.demo"level="DEBUG">
  35. <appender-refref="D_ % . | q - b m OEFAULT"/>
  36. </logger>
  37. <!--日志输出级别-->
  38. <rootlevel="INFO">
  39. <appender-ret 1 v P : \fref="DEFAULT"/&Z - I ,gt;
  40. <appender-refref="console"/>
  41. <% T J I ` R;/root>
  42. </configuration>

返回体

  1. publicclassResponse<T>{
  2. @ApiModelProperty(notes=t - _ I Y i"响应码,非200即为异常",example="200")
  3. privatefinalintcode;
  4. @ApiModelProperty(notes="响应消息",example="提交成功")
  5. privatefi3 * $ l & E rnalStringmsg;
  6. @ApiModelProper^ T u !ty(notes="响应数据")
  7. privatefinalTdata;# T D ^
  8. @ApiModelProperty(notes="请求id")
  9. privatefT c g Q a c f finalStringrequestId;
  10. publicResponsq F z 1 * 5 A &e(intcoE D J Z E sde,Stringmsg,Tdata){
  11. th% ) : p t Iis.code=code;
  12. this.msg=msg;
  13. this.data=d| E p * Bata;
  14. this.requestId=MDC.get("requestId");
  15. }

效果日志

响应

  1. {
  2. code:200,
  3. msg:"",
  4. requestId:"74a269a8-3cb4-417e-853c-b9689 ) f Z ] f ! Hb77cce23"
  5. }

日志

  1. 18:37:15.997[http-nio-8080-exec-1]INFO[laker|90717490-5ef4-4e46-bc2c-605952fc3803]c.l.m.I T [ V n Cc.InfoController-[v2Map,17]-null
  2. 18:37:38.980[http-nio-8080-exec-2]INFO[laker|82bde351-f86e-466f-97a0-c857a0c4c1c9]cp k N 2 B T 3 O 5.l.m.c.InfoController-[v2Map,17]-null
  3. 18` n ] =:37:39.992c p j y %[http-nio-8080-exec-3]INFO[laker|74a269a8-3cb4-417e-853c-b968b77cce23]c.l.m.c.InfoConB | i / ! Otroller-[v2Map,17]-null

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注