当前位置: 首页>资讯 >

为程序日志添加唯一标识|焦点要闻

来源: 程序猿阿嘴 | 时间: 2023-05-14 04:00:12 |

最近看一个工程中将UUID打印在日志中、看到那个时候我想到的就是唯一请求流水编号、什么意思呢、你可以理解为我调用一个接口他就会生成一个编号、这个编号就代表我之前请求的唯一标识、后续出现问题能够快速定位日志信息。


(资料图片仅供参考)

开始-改造

我看别人改程中的打印很繁琐、每个log.xxx()的时候都要传这个编号、所以肯定是要优化一下的!哈哈哈哈!

这边封装了一个工具类、主要还是要懂 ThreadLocal 线程本地变量 !简单理解每个线程都有一份、能做到独立互不干涉。

java复制代码 package com.stall.config;  import java.util.HashMap; import java.util.Map; import java.util.UUID;  /**  * 日志请求流水、用日志追踪  *  * @Author 突突突突突  * @blog https://juejin.cn/user/844892408381735  * @Date 2023/3/24 13:24  */ public class RequestLogManagement {     public static ThreadLocal>threadLocal = new ThreadLocal<>();        /**      *  初始入口、后续打印调用      * @param describe 入口描述      */     public static void init(String describe) {         MapthreadLocalMap = new HashMap<>();         String requestUUID = UUID.randomUUID().toString();         threadLocalMap.put(\"describe\", describe);         threadLocalMap.put(\"uuid\", requestUUID);         threadLocal.set(threadLocalMap);     }      public static String getRequestUUID() {         return threadLocal.get() == null            ? \"\" : String.valueOf(threadLocal.get().get(\"uuid\"));     }      public static String getRequestDescribe() {         return threadLocal.get() == null            ? \"\" : String.valueOf(threadLocal.get().get(\"describe\"));     }     public static void remove() {         threadLocal.remove();     } }

死方式-每个log都手动打印

java复制代码 /**  * 登录认证  *  * @Author 突突突突突  * @blog https://juejin.cn/user/844892408381735  * @Date 2023/3/24 13:49  */ @Slf4j @RestController @RequestMapping(\"/auth\") public class WxLoginController {      @Resource     private AuthService authService;      @PostMapping(\"/wx/login\")     public RwxLogin(String code) {         RequestLogManagement.init(\"微信登录接口\");         try {             log.info(\"{}、开始调用微信登录接口\",RequestLogManagement.getRequestUUID());             authService.wxLogin(code);             return R.success();         } catch (InterfaceException e) {             log.error(\"{}、收到请求异常信息\",RequestLogManagement.getRequestUUID(), e);             return R.custom(e.getCode(), e.getMessage());         } catch (Exception e) {             log.error(\"{}、收到请求异常信息\",RequestLogManagement.getRequestUUID(), e);             return R.failed();         }finally {             RequestLogManagement.remove();         }     } }

从上面的日志打印就能发现问题一些问题吧、如果我很多接口这个 RequestLogManagement.init(\"微信登录接口\"); 、 log.info(\"{}、xxxxxx调用\",RequestLogManagement.getRequestUUID()); 和 RequestLogManagement.remove(); 这些内容中很多重复的操作、首先我们解决 入口开始描述/入口结束清除数据 、用眼睛一看就知道用什么解决这个问题、那就是AOP的方式、在Controller接口请求的方法中的前后进行增强处理。

就是说知道用AOP的方式后、在写牛点自定义一个注解用于AOP能够准确的切入到对应方法。

java复制代码 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequestLog {     /**      * 日志描述      */     String value(); }
java复制代码 @Slf4j @Aspect @Component public class RequestLogOperationAspect {     /**      * 准备环绕的方法      */     @Pointcut(\"@annotation(com.stall.config.aop.RequestLog)\")     public void execRequestLogService() {     }      @Around(\"execRequestLogService()\")     public Object RequestLogAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {         //目标对象         Classclazz = proceedingJoinPoint.getTarget().getClass();         //方法签名         String method = proceedingJoinPoint.getSignature().getName();         //方法参数         Object[] thisArgs = proceedingJoinPoint.getArgs();         //方法参数类型         Class[] parameterTypes = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod().getParameterTypes();         //方法         Method thisMethod = clazz.getMethod(method, parameterTypes);         //自定义日志接口         RequestLog methodAnnotation = Objects.requireNonNull(AnnotationUtils.findAnnotation(thisMethod, RequestLog.class));         //  通用日志打印         RequestLogManagement.init(methodAnnotation.value());         log.info(\"[{}][{}]请求开始、请求参数:{}\",RequestLogManagement.getRequestUUID(), methodAnnotation.value(), Arrays.toString(thisArgs));         Object proceed = null;         try {             proceed = proceedingJoinPoint.proceed();         } finally {             log.info(\"[{}][{}]请求结束、请求参数:{}\",RequestLogManagement.getRequestUUID(), methodAnnotation.value(), proceed);             // 清除数据             RequestLogManagement.remove();         }         return proceed;     } }

然后改造好后的代码、我们在入口上加一个注解就ok了。

java复制代码 @RequestLog(value = \"微信登录接口\") @PostMapping(\"/wx/login\") public RwxLogin(String code) {   try {     log.info(\"{}、开始调用微信登录接口\",RequestLogManagement.getRequestUUID());     authService.wxLogin(code);     return R.success();   } catch (InterfaceException e) {     log.error(\"{}、收到请求异常信息\",RequestLogManagement.getRequestUUID(), e);     return R.custom(e.getCode(), e.getMessage());   } catch (Exception e) {     log.error(\"{}、收到请求异常信息\",RequestLogManagement.getRequestUUID(), e);     return R.failed();   } }

MDC-不需要每个log都手动打印

但是现在解决了那个问题还有这个 log.info(\"{}、xxxxxx调用\",RequestLogManagement.getRequestUUID()); 我总不能说我每次打印日志我都要加一个 RequestLogManagement.getRequestUUID() 。

所以身为大聪明的我又想到AOP的方式、去增强log对象中的所有方法、于是我打开百度找阿找!!!我就发现一个牛很多的写法、就是 MDC 类对象中可能放入参数、而这个参数能够被日志底层使用、相当于在我们打印日志的时候可以向日志中塞入一个值、类似插槽一样的概念、用就加、不用就不加!!!

MDC 底层也是靠 ThreadLocal 来实现的、他泛型是Map类型、就相当于能放键值对的形式的数据、而 MDC 就相当于是我们刚刚写 RequestLogManagement 的一个工具类、提供外部直接调用、要注意的就是一个 MDC 是 org.slf4j.MDC 一个是 org.jboss.logging.MDC 虽然说都能使用、但是里面的方法不一样、最后使用 org.slf4j.MDC 这个就可以。

来先把 RequestLogOperationAspect.RequestLogAround(.) 这个方法改造了、这个是我们写的Controller切入执行的入口。

java复制代码 //自定义日志接口 RequestLog methodAnnotation = Objects.requireNonNull(AnnotationUtils.findAnnotation(thisMethod, RequestLog.class)); //  通用日志打印 RequestLogManagement.init(methodAnnotation.value()); // 将UUID放入到MDC对象中 MDC.put(\"requestId\", RequestLogManagement.getRequestUUID()); log.info(\"[{}]请求开始、请求参数:{}\", methodAnnotation.value(), Arrays.toString(thisArgs)); Object proceed = null; try {   proceed = proceedingJoinPoint.proceed(); } finally {   log.info(\"[{}]请求结束、请求参数:{}\", methodAnnotation.value(), proceed);   RequestLogManagement.remove();   // 执行完成后清除。   MDC.clear(); }
yml复制代码 logging:   pattern:     console: \"${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr([%X{requestId}]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}\"

修改日志的打印格式、主要看 %X{requestId} 、当前的name就是MDC.put中的key的名称。

默认打印日志

修改后的打印日志

不管我们自己写的 RequestLogManagement 还是 MDC 这两种方式都不能在子线程中获取到、解决方法就是在线程外将值赋值出去、然后由子线程重新塞入到自己线程副本的 ThreadLocal 中。

typescript复制代码 MapcopyOfContextMap = MDC.getCopyOfContextMap(); new Thread(new Runnable() {   @Override   public void run() {     MDC.setContextMap(copyOfContextMap);     for (int i = 0; i < 10; i++) {       log.info(\">>>>>>>>>i={}\", i);     }     MDC.clear();   } }).start();

小结

以上方式主要适用单机环境、如分布式服务之间的调用、肯定有其他的更好更牛的链路的方式。

把上面方式集成到你的单机项目中再配合之前写的 linux下查看项目日志的方式就能快速找到请求流水对应的日志信息。

原文链接:https://juejin.cn/post/7215640327633141819

关键词:

 

热文推荐

为程序日志添加唯一标识|焦点要闻

最近看一个工程中将UUID打印在日志中、看到那个时候我想到的就是唯一请求流水编号、什么意思呢、你可以理解

2023-05-14

全球看热讯:学者回应“给生孩子的人发薪”争议:初衷是缓解生育率过低

红星新闻消息,5月12日,一条名为“专家建议给生孩子的人发薪水”的短视频登上热搜榜。视频中,全球化智库

2023-05-14

14.54万人直接受益!甘肃中部这项供水工程全线贯通

甘肃中部生态移民扶贫开发供水工程日前全线贯通,具备全线通水条件。项目总干含17座泵站、25座隧洞、323公

2023-05-13

汉阳造艺术区官网_汉阳造艺术区

1、走进汉阳造艺术区,随处可见这样充满创意和灵魂的街头创作和艺术结构。下图是某广告公司“独自穿墙”的

2023-05-13

下周两市解禁市值环比大减近5成 3家公司解禁比例超40%(附表)|每日看点

下周两市解禁市值环比大减近5成3家公司解禁比例超40%(附表)

2023-05-13

中科院科普室落户鄂西土家小山村|环球关注

近日,由中科院武汉分院支持的恩施市首个村级科普室在龙凤镇古场坝村正式建成。据了解,该科普室是中科院武

2023-05-13

今日最美的时光大结局剧情(最美的时光大结局剧情)_全球微头条

最美的时光大结局剧情,最美的时光大结局剧情很多人还不知道,现在让我们一起来看看吧!1、这是电视上的结

2023-05-13

宽带账号查询_电信宽带账号密码查询_每日热点

你们好,最近小信发现有诸多的小伙伴们对于宽带账号查询,电信宽带账号密码查询这个问题都颇为感兴趣的,今

2023-05-13

全球关注:瑞丰村_关于瑞丰村介绍

瑞丰村,关于瑞丰村介绍这个很多人还不知道,我们一起来看看!1、瑞丰村,四川省泸州市合江县甘雨镇下辖村。

2023-05-13

天天精选!张店区马尚街道开展防范灾害风险知识讲座

中国山东网-感知山东5月13日讯在全国防灾减灾日来临之际,淄博市张店区马尚街道联合张店区疾控中心在九级村

2023-05-13

chinanet怎么连接不上网_chinanet怎么连接-世界新动态

1、在手机主屏幕上打开appstore。2、我们搜索天翼WiFi,下载安装。3、转到手机设置,打开WiFi。4、找到搜

2023-05-13

当前热文:桥墩垫石塑料模板的详细介绍

桥墩垫石塑料模板的详细介绍桥墩垫石塑料模板,顾名思义,是一种用于桥墩造型的建筑模板。相比于传统的木质

2023-05-13

地球什么时候毁灭它什么时候就恢复了_地球什么时候毁灭 每日消息

1、往长了说,数十亿年后,当太阳燃烧殆尽的时候,它会膨胀,吞噬地球。2、那时候,就是地球的末日。3、往

2023-05-13

每日讯息!各地精心组织防灾减灾宣传活动 增强全民防灾意识

点击图片观看视频央视网消息(新闻联播):今天(5月12日)是全国防灾减灾日,今年的主题是防范灾害风险,

2023-05-13

东方证券:五一地产销售表现平淡!央国企优势持续凸显

第18周新房和二手房销售量均较上周下降!详情

2023-05-13

防灾减灾宣传走到群众身边

雷州5月12日,是第15个全国防灾减灾日,雷州市在雷湖文化广场开展主题为“防范灾害风险护航高质量发展”防

2023-05-13

长春市星东文化传媒有限公司(吉林省星华文化传播有限公司)

当前大家对于吉林省星华文化传播有限公司都是颇为感兴趣的,大家都想要了解一下吉林省星华文化传播有限公司

2023-05-13

热点!国际原子能机构总干事:扎波罗热核电站可能面临工作人员短缺状况

国际原子能机构总干事格罗西12日表示,自俄乌冲突开始以来,扎波罗热核电站的人员编制大幅减少。当前该地区

2023-05-13

播报:PLC实时数据采集如何实现?

数据采集传输对于后续企业进行分析和决策是十分重要的,而实时数据采集更能提升整体生产的认识度,从而采取

2023-05-13

新能源汽车市场占有率达27% 全球时快讯

本报北京5月12日电(记者徐佩玉)11日,中国汽车工业协会发布数据显示,今年1至4月,我国汽车产销累计完成835

2023-05-13