springboot怎样设置全局的traceId(包括MQ)

一、Controller打印TraceId

1、拦截所有的controller,输入输出将traceId放入MDC中:

package com.perkins.ebicycle.mobile.trace;import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;@Component
@Aspect
public class TraceIdAspect {private static final Logger logger = LoggerFactory.getLogger(TraceIdAspect.class);/*** 日志跟踪标识*/private static final String TRACE_ID = "TraceId";/*** * @Title: controllerPointCut* @Description: 拦截所有controller入口下所有的 public方法* @throws*/@Pointcut("execution(public * com.perkins..*.controller..*(..))")public void controllerPointCut() {}/*** * @Title: consumerPointcut* @Description: 拦截listener入口下所有的 public方法,用于mq的traceId* @throws*/@Pointcut("execution(public * com.perkins..*.listener..*(..))")public void consumerPointcut() {}/*** * @Title: controllerAround* @Description: 拦截controller方法处理* @param point* @return* @throws Throwable* @throws*/@Around("controllerPointCut()")public Object controllerAround(ProceedingJoinPoint point) throws Throwable {long startTime = System.currentTimeMillis();ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//设置traceIdsetTraceId(request);// 开始打印请求日志printRequestLog(point,request);//执行方法Object result = point.proceed();//打印出参printResponseLog(result);logger.info("------------- controllerAround 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);// 移除 MDCMDC.remove(TRACE_ID);return result;}/*** * @Title: consumerAround* @Description: 拦截消费者方法处理* @param point* @return* @throws Throwable* @throws*/@Around("consumerPointcut()")public void consumerAround(ProceedingJoinPoint point) throws Throwable {long startTime = System.currentTimeMillis();Object[] args = point.getArgs();String traceId=null;if(args!=null && args.length>0) {Object arg=args[0];JSONObject obj=JSONObject.parseObject(arg.toString());if(obj.containsKey("ext")) {JSONObject extJson=obj.getJSONObject("ext");if(extJson.containsKey(TRACE_ID)) {traceId=extJson.getString(TRACE_ID);}}}if(StringUtils.isBlank(traceId)) {traceId=UUID.randomUUID().toString();}// 添加 MDCMDC.put(TRACE_ID, traceId);logger.info("========================================== Start ==========================================");logger.info("====Request Args====: {}", JSONObject.toJSONString(args));point.proceed();// 移除 MDCMDC.remove(TRACE_ID);logger.info("========================================== End ==========================================");logger.info("------------- consumerAround 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);}/*** * @Title: printResponseLog* @Description:打印出参* @param result* @throws*/private void printResponseLog(Object result) {logger.info("====Response Args====: {}", JSONObject.toJSONString(result));logger.info("========================================== End ==========================================");}/*** * @Title: setTraceId* @Description: 设置traceId* @param request* @throws*/private void setTraceId(HttpServletRequest request) {String traceId = null;if (request != null && request.getHeader(TRACE_ID) != null) {traceId = request.getHeader(TRACE_ID);} else {traceId = UUID.randomUUID().toString();}// 添加 MDCMDC.put(TRACE_ID, traceId);}/*** * @Title: printRequestLog* @Description: 打印入参* @param point* @param request* @throws*/private void printRequestLog(ProceedingJoinPoint point, HttpServletRequest request) {// 打印请求相关参数logger.info("========================================== Start ==========================================");// 打印请求 urllogger.info("====URL====: {}", request.getRequestURL().toString());// 打印 Http methodlogger.info("====HTTP Method====: {}", request.getMethod());// 打印调用 controller 的全路径以及执行方法logger.info("====Class Method====: {}.{}", point.getSignature().getDeclaringTypeName(),point.getSignature().getName());// 打印请求的 IPlogger.info("====IP====: {}", request.getRemoteAddr());// 打印请求入参Object[] args = point.getArgs();List<Object> stream = ArrayUtils.isEmpty(args) ? Lists.newArrayList() : Arrays.asList(args);List<Object> logArgs = stream.stream().filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse))).collect(Collectors.toList());// 过滤后序列化无异常logger.info("====Request Args====: {}", JSONObject.toJSONString(logArgs));}
}

2、将traceId放入header通过feign往下个服务传值

package com.perkins.ebicycle.mobile.trace;import org.slf4j.MDC;
import org.springframework.stereotype.Component;import com.perkins.ebicycle.common.util.StringUtils;import feign.RequestInterceptor;@Component
public class FeignLogInterceptor implements RequestInterceptor {/*** 日志跟踪标识*/private static final String TRACE_ID = "TraceId";@Overridepublic void apply(feign.RequestTemplate template) {String traceId = MDC.get(TRACE_ID);if (StringUtils.isEmpty(traceId)) {traceId = StringUtils.uuid();}template.header(TRACE_ID, traceId);}
}

二、将traceId在多线程之间打印

1、拿到主线程上下文

package com.perkins.ebicycle.mobile.trace;import java.util.Map;import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;public class MdcTaskDecorator implements TaskDecorator {/*** 使异步线程池获得主线程的上下文* * @param runnable* @return*/@Overridepublic Runnable decorate(Runnable runnable) {Map<String, String> map = MDC.getCopyOfContextMap();return () -> {try {MDC.setContextMap(map);runnable.run();} finally {MDC.clear();}};}
}

2、将traceId放入多线程中

package com.perkins.ebicycle.mobile.trace;import java.util.concurrent.ThreadPoolExecutor;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;/*** 
* @ClassName: ThreadPoolConfig
* @Description: 线程池配置
* @author dingjy
* @date 2024年1月2日 上午11:23:57*/
@EnableAsync
@Configuration
public class ThreadPoolConfig {private int corePoolSize = 50;private int maxPoolSize = 200;private int queueCapacity = 1000;private int keepAliveSeconds = 300;@Bean(name = "threadPoolTaskExecutor")public ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setMaxPoolSize(maxPoolSize);executor.setCorePoolSize(corePoolSize);executor.setQueueCapacity(queueCapacity);executor.setKeepAliveSeconds(keepAliveSeconds);executor.setTaskDecorator(new MdcTaskDecorator());// 线程池对拒绝任务(无线程可用)的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;}
}

三、设置logback统一日志格式

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{TraceId}] [%thread] %-5level %logger{50} %L - %msg%n</pattern>

四、在MQ中设置TraceId

1、将traceI放入mq的ext扩展对象中传递(MQ),也可以做mq拦截,放在拦截器中实现,由生产者实现

package com.perkins.notice.common.vo;import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;import io.swagger.annotations.ApiModelProperty;/***/
public class MqVO implements Serializable {@NotBlank(message = "topic不能为空")@ApiModelProperty(name = "topic", notes = "topic")private String topic;@NotNull(message = "tag不能为空")@ApiModelProperty(name = "tag", notes = "tag")private String tag;@ApiModelProperty(name = "mq消息体", notes = "mq消息体")private String body;@ApiModelProperty("msgId")private String msgId;@ApiModelProperty("key")private String key;@NotBlank(message = "appName不能为空")private String appName;public String getAppName() {return appName;}public void setAppName(String appName) {this.appName = appName;}/*** 扩展属性json, 供其他组件加入*/private Map<Object, Object> ext =new HashMap();public String getKey() {return key;}public void setKey(String key) {this.key = key;}public String getTopic() {return topic;}public void setTopic(String topic) {this.topic = topic;}public String getTag() {return tag;}public void setTag(String tag) {this.tag = tag;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}public String getMsgId() {return msgId;}public void setMsgId(String msgId) {this.msgId = msgId;}public Map<Object, Object> getExt() {return ext;}public void setExt(Map<Object, Object> ext) {this.ext = ext;}
}
package com.perkins.notice.api.web.controller;import java.util.UUID;import javax.servlet.http.HttpServletRequest;import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import com.perkins.notice.common.vo.MqVO;/*** @ClassName: BaseController* @Description: 基础控制器* @Author: Xiaoqiuping* @Date: 2023-12-21 11:24**/
public class BaseController {Logger logger = LoggerFactory.getLogger(BaseController.class);/*** 日志跟踪标识*/private static final String TRACE_ID = "TraceId";public void setTraceId(MqVO mqVo) {ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = servletRequestAttributes.getRequest();String traceId = request.getHeader(TRACE_ID);logger.info("traceId:{}", traceId);if (StringUtils.isBlank(traceId)) {traceId = UUID.randomUUID().toString();}mqVo.getExt().put(TRACE_ID, traceId);}}
package com.perkins.notice.api.web.controller;import javax.validation.Valid;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;import com.perkins.notice.biz.service.IProxySmsService;
import com.perkins.notice.biz.service.MQSenderService;
import com.perkins.notice.biz.service.RobotSenderService;
import com.perkins.notice.common.dto.SmsInfoDTO;
import com.perkins.notice.common.util.CommonResult;
import com.perkins.notice.common.vo.MqVO;
import com.perkins.notice.common.vo.RobotSendVO;
import com.perkins.notice.common.vo.SmsInfoVO;import io.swagger.annotations.ApiOperation;/*** 
* @ClassName: ProxyController
* @Description: 通知controller
* @author dingjy
* @date 2024年1月13日 上午9:59:41*/
@RestController
@RequestMapping("/proxy/api")
public class ProxyController extends BaseController{protected  final Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate IProxySmsService smsService;@Autowiredprivate MQSenderService mqSenderService;@Autowiredprivate RobotSenderService robotSenderService;@ApiOperation(value="短信发送接口", notes=" 普通短信发送 -> SmsInfo")@PostMapping("/send/sms")@ResponseBodypublic CommonResult sendSms(@Valid @RequestBody SmsInfoVO smsInfo){SmsInfoDTO dto = new SmsInfoDTO();BeanUtils.copyProperties(smsInfo, dto);this.smsService.sendSms(dto);return CommonResult.success("短信发送成功");}@ApiOperation(value="MQ发送接口", notes=" MQ发送接口 -> MQInfo")@PostMapping("/send/mq")@ResponseBodypublic CommonResult sendMq(@Valid @RequestBody MqVO mqVo){//此处是关键==============================================super.setTraceId(mqVo);mqSenderService.send(mqVo);return CommonResult.success("MQ发送成功");}@ApiOperation(value="机器人发送接口", notes=" 机器人发送接口")@PostMapping("/send/robot")@ResponseBodypublic CommonResult sendRobot(@Valid @RequestBody RobotSendVO robotSendVo){robotSenderService.send(robotSendVo);return CommonResult.success("机器人发送成功");}}

2、消费者拦截器,将生产者发送的msg中ext的TraceId打印出来,代码同1.1中的如图所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/620976.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为设备登录安全配置案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​ https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle O…

Python+甘特图及标签设置

图示 甘特图代码 import matplotlib.pyplot as plt import numpy as npclass ProjectEmement:def __init__(self, name_, starttime_: float, endtime_: float, fact_endtime_: float, grade_, rootlist_: list, keylist_: list, isover_=-1):self.name = name_self.starttime…

开发你的第一个 Python3 程序

简介 “Hello, World!” 程序是一个经典的&#xff0c;确立已久的传统电脑程序。"Hello, World!"因为能展示一个语言的基本句法&#xff0c;因此常被用作初学者的第一个"简单但完整"的程序&#xff0c;并可以被用来测试编程环境。 这个教程将指导你写出第…

宝塔nginx部署前端页面刷新报404

问题&#xff1a; 当我们使用脚手架打包前端项目的时候&#xff0c;如果前端项目并没有静态化的配置&#xff0c;如以下 当我们刷新页面&#xff0c;或进行路由配置访问的时候就会报404的错误 原因&#xff1a; 这是因为通常我们做的vue项目属于单页面开发。所以只有index.html…

【教程】华为数据恢复的5个简单方法

您刚刚不小心从华为手机中删除了一些重要文件&#xff0c;现在您迫切希望将它们找回来。如果是这样&#xff0c;那么您现在可能会感到沮丧和无助。您可能已向您的朋友寻求帮助或在互联网上搜索答案&#xff0c;但似乎无济于事。 华为数据恢复的5个简单方法 好吧&#xff0c;别…

MyBatis第二课,灰度发布,@Results注解,使用xml书写mysql

目录 打印MyBatis的日志配置&#xff1a; 灰度发布:指发布环境&#xff0c;比如发布环境有200台机器&#xff0c;发布的时候是一批一批的机器的发布 2.删除与修改 使用Results注解&#xff0c;这样就和上面的别名一个意思&#xff0c;column是数据库的列 自动转驼峰&#…

学习资料: uni-app HBuilderX

编译器&#xff1a;HBuilderX HBuilderX-高效极客技巧 uni-app介绍&#xff1a;uni-app官网 uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#…

根据gbt81702008数值修约的C#函数

#region 修约函数/// </summary>/// <param name"data_val">输入数值</param>/// <param name"len">保留几位小数</param>/// <returns></returns>public static decimal round_gbt8170(decimal data_val,int l…

投手最核心的能力是什么?

巨量广告的本质是用户体验和流量成本 广告投放系统升级的越智能说明三点&#xff1a; 1: 流量顶到天了 2: 系统对用户人群价值计算的准确度提高 3&#xff1a;素材越来越卷 而对于投手来说&#xff1a; 入行门槛降低&#xff0c;操作层面简单&#xff0c;系统能用算法集合…

Unity组件开发--UI管理器

1.Canvas组件&#xff1a; 注意属性&#xff1a; &#xff08;1&#xff09;渲染模式是&#xff1a;屏幕空间相机 &#xff08;2&#xff09;创建一个UICamera节点&#xff0c;管理相机 &#xff08;3&#xff09;屏幕画布缩放模式 &#xff08;4&#xff09;画布下挂载两…

Android-基础

Activity生命周期 1.启动Activity&#xff1a;系统会先调用onCreate方法&#xff0c;然后调用onStart方法&#xff0c;最后调用onResume&#xff0c;Activity进入运行状态。 2.当前Activity被其他Activity覆盖其上或被锁屏&#xff1a;系统会调用onPause方法&#xff0c;暂停当…

linux批量查杀进程

linux批量查杀进程 方案1&#xff1a;killall killall 命令来杀掉所有指定名称的进程。 killall -9 uwsgi这将会杀掉所有名称为 uwsgi 的进程&#xff0c;包括在后台运行的进程。 注意&#xff0c;使用 killall 命令要小心&#xff0c;因为它可能会误杀其他不相关的进程。在…

WiFi7: MLO操作之AP MLD的发现—AP的行为

原文:如果AP是AP MLD的transmitted BSSID,那么Beacon帧和Probe Response 帧必须在Reduced Neighbor Report element中包含对应各AP的TBTT Information域,并将TBTT Information Length域设置为16或者更高(见9.4.2.169.2(Neighbor AP Information field))。FILS Discovery帧…

华为HCIE课堂笔记第十五章 IPv6过渡技术

第十五章 IPv6过渡技术 15.1 简介 1、隧道技术 2、双栈技术 3、地址转换技术 15.2 隧道技术 15.2.1 ipv6 over ipv4 隧道 手工隧道&#xff1a;IPv6 Over IPv4隧道 Ipv6 Over IPv4&#xff1a;IPv6孤岛通过IPv4网络之间进行通信&#xff0c;IPv6的报文封装IPv4报文之上…

测试人员必备基本功(3)

容易被忽视的bug 第三章 查询列表容易被忽视的bug 文章目录 容易被忽视的bug第三章 查询列表容易被忽视的bug 前言1.查询角色2.接口设计 三、测试设计1.测试点2.容易发现bug的测试点如下&#xff1a; 总结 前言 一个WEB系统的所有功能模块&#xff0c;其实都是围绕“增、删、…

Pandas实战100例-专栏介绍

Pandas&#xff0c;Python数据科学的心脏&#xff0c;是探索和分析数据世界的强大工具。想象一下&#xff0c;用几行代码就能洞察庞大数据集的秘密&#xff0c;无论是金融市场趋势还是社交媒体动态。 通过Pandas&#xff0c;你可以轻松地整理、清洗、转换数据&#xff0c;将杂…

Neo4j Cypher (1):使用Cypher删除边

在Neo4j中&#xff0c;删除边&#xff08;即关系&#xff09;可以通过使用Cypher查询语言来实现。关系可以基于其类型、属性或者它连接的节点来指定和删除。以下是一些删除关系的常见方法&#xff1a; 1. 删除特定类型的关系 如果您知道关系的类型&#xff0c;可以使用以下查…

山西电力市场日前价格预测【2024-01-15】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-01-15&#xff09;山西电力市场全天平均日前电价为399.10元/MWh。其中&#xff0c;最高日前电价为583.33元/MWh&#xff0c;预计出现在18:15。最低日前电价为275.09元/MWh&#xff0c;预计…

【MySQL】:探秘主流关系型数据库管理系统及SQL语言

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. MySQL概述1.1 数据库相关概念1.2 主流数据库1.3 数据模型1.3.1 关系型数据库…

MyBatis第三课

目录 回顾 #和$区别 #&#xff08;预编译SQL&#xff09;和$&#xff08;即时SQL&#xff0c;它是进行的字符串拼接&#xff09;的区别&#xff0c;其中之一就是预编译SQL和即时SQL的区别 原因&#xff1a; 回顾 两者的共同点 MaBits可以看作是Java程序和Mysql的沟通桥梁&…