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…

宝塔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…

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;暂停当…

Pandas实战100例-专栏介绍

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

VMware workstation安装debian-12.1.0虚拟机(最小化安装)并配置网络

VMware workstation安装debian-12.1.0虚拟机&#xff08;最小化安装&#xff09;并配置网络 Debian 是一个完全自由的操作系统&#xff01;Debian 有一个由普罗大众组成的社区&#xff01;该文档适用于在VMware workstation平台安装最小化安装debian-12.1.0虚拟机。 1.安装准…

Linux Ubuntu搭建我的世界Minecraft服务器实现好友远程联机MC游戏

文章目录 前言1. 安装JAVA2. MCSManager安装3.局域网访问MCSM4.创建我的世界服务器5.局域网联机测试6.安装cpolar内网穿透7. 配置公网访问地址8.远程联机测试9. 配置固定远程联机端口地址9.1 保留一个固定tcp地址9.2 配置固定公网TCP地址9.3 使用固定公网地址远程联机 前言 Li…

制造领域 基础概念快速入门介绍

1、基本背景知识 本定义结合国家标准文件有所发挥&#xff0c;仅供参考。 产品&#xff1a;是生产企业向用户或市场以商品形式提供的制成品&#xff1b; 成套设备&#xff1a;在生产企业一般不用装配工序连接&#xff0c;但用于完成相互联系的使用功能的两个或两个以上的产…

【快速解决】保姆级Anaconda安装教程

目录 第一步 ​编辑第二步 ​编辑第三步 第四步 第五步 第六步 ​编辑 第七步 第八步 第九步 第一步 在anaconda清华大学开源软件镜像站下载anaconda。点击这里进入 我这里选的是windows-x86_64。 第二步 下载好以后进行安装 第三步 第四步 第五步 选择…

SpringBoot集成Skywalking实现分布式链路追踪

官方网址&#xff1a; Apache SkyWalking官方文档&#xff1a; SkyWalking 极简入门 | Apache SkyWalking下载地址&#xff1a;Downloads | Apache SkyWalking Agent&#xff1a;以探针的方式进行请求链路的数据采集&#xff0c;并向管理服务上报&#xff1b; OAP-Service&am…

一文带你了解注册信息安全专业人员CISP

CISP即"注册信息安全专业人员"&#xff0c;系国家对信息安全人员资质的最高认可。英文为Certified Information Security Professional (简称CISP)&#xff0c;CISP系经中国信息安全测评中心实施国家认证。 CISP证书涵盖方向&#xff1a; “注册信息安全工程师”&a…

kafka之java客户端实战

1. kafka的客户端 Kafka提供了两套客户端API&#xff0c;HighLevel API和LowLevel API。 HighLevel API封装了kafka的运行细节&#xff0c;使用起来比较简单&#xff0c;是企业开发过程中最常用的客户端API。 而LowLevel API则需要客户端自己管理Kafka的运行细节&#xff0c;Pa…