MDC 日志跟踪笔记
一、前言
本文主要解析应用中比较实在的一个log4j的链路应用,在经过一段时间的应用后发现还是很稳的,就记录一下这个MDC 代表Mapped Diagnostic Context(映射式诊断上下文)的情况。
Tisp: 它是一个线程安全的存放诊断日志的容器
Tisp: 它是一个线程安全的存放诊断日志的容器
Tisp: 它是一个线程安全的存放诊断日志的容器
二、接口相关内容
相关slf4j 官方的接口文档: https://www.slf4j.org/api/org/slf4j/MDC.html
1、常用直接调用接口
-
clear() :移除所有MDC
-
get (String key) :获取当前线程MDC中指定key的值
-
getContext() :获取当前线程MDC的MDC
-
put(String key, Object o) :往当前线程的MDC中存入指定的键值对
-
remove(String key) :删除当前线程MDC中指定的键值对
2、调试用接口
- pushByKey(String key, String value) :将指定的键值对推入 MDC 上下文信息的栈
可以在之后 在之后通过 popByKey 恢复原始值
- popByKey(String key) :从 MDC 上下文信息的栈中弹出指定键的值
如果在没有先前推送的情况下调用 popByKey,则会将键从 MDC 中移除。
3、配置用接口
-
getCopyOfContextMap() : 一个包含当前线程的 MDC 上下文信息的 不可修改的 映射副本
-
getMDCAdapter() :获取当前线程的 MDC 适配器,可进行 MDC 上下文信息的设置和清除
-
setContextMap(Map<String,String> contextMap) : 设置整个 MDC 上下文信息的映射
三、应用配置
下属内容中
TraceUtils仅为生成traceId
TraceConstant对应枚举类
logging:pattern:console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{traceId} - %msg%n"
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{traceId} - %msg%n
<?xml version="1.0" encoding="UTF-8"?>
<configuration><property name="logDirectory" value="${LOG_DIRECTORY:-logs}"/><appender name="Console" class="ch.qos.logback.core.ConsoleAppender"><layout><Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{traceId} - %msg%n</Pattern></layout></appender><appender name="File" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logDirectory}/app.log</file><!-- 其他 RollingFileAppender 配置省略 --></appender><root level="info"><appender-ref ref="Console"/><appender-ref ref="File"/></root></configuration>
1、异步任务线程配置
针对独立线程修饰
TaskDecorator接口允许在任务执行之前和之后对执行线程进行修改或装饰
public class MdcTaskDecorator implements TaskDecorator {@Overridepublic Runnable decorate(Runnable runnable) {// 获取当前线程的 MDC 上下文信息//final var contextMap = MDC.getCopyOfContextMap();Map<String, String> contextMap = MDC.getCopyOfContextMap();return () -> {try {// 设置新的执行线程的 MDC 上下文信息if (contextMap != null) {MDC.setContextMap(contextMap);}// 执行任务runnable.run();} finally {// 清除 MDC 上下文信息MDC.clear();}};}
}
针对线程工厂修饰
利用
ThreadFactory创建线程针对内容修饰MDC
public static class MdcThreadFactory implements ThreadFactory {@Overridepublic Thread newThread(Runnable r) {// 在创建线程时设置 MDC 上下文信息return new Thread(() -> {// 获取当前线程的 MDC 上下文信息final Map<String, String> contextMap = MDC.getCopyOfContextMap();try {// 在新线程中设置 MDC 上下文信息if (contextMap != null) {MDC.setContextMap(contextMap);}// 执行任务r.run();} finally {// 清除 MDC 上下文信息MDC.clear();}});}
}
1.1、设置注解线程池的配置
1、利用配置异步方法的执行器
AsyncConfigurer来配置注解线程池2、
setTaskDecorator和setThreadFactory会以最后一个配置的为准
@Component
public class TraceAsyncConfigurer implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(8);executor.setMaxPoolSize(16);executor.setQueueCapacity(100);executor.setThreadNamePrefix("async-pool-");//①用来直接修改装饰器executor.setTaskDecorator(new MdcTaskDecorator());//② 也可以直接用ThreadFactoryexecutor.setThreadFactory(new MdcThreadFactory());executor.setWaitForTasksToCompleteOnShutdown(true);executor.initialize();return executor;}}
1.2、设置直接使用的线程配置
在工厂类直接配置MDC即可
ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 16, 15000, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),new MdcThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
2、针对web上下文配置
利用
HandlerInterceptor直接拦截
public class TraceHandlerInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String traceId = request.getHeader(TraceConstant.TRACE_ID);if (!StringUtils.hasText(traceId)) {traceId = TraceUtils.traceIdGenerator();}MDC.put(TraceConstant.TRACE_ID, traceId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {MDC.remove(TraceConstant.TRACE_ID);HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
3、Feign客户端的配置
RequestInterceptor是Feign客户端库中的一个接口,用于拦截Feign客户端发出的HTTP请求,利用这个特性包装
@Component
public class FeignRequestInterceptor implements RequestInterceptor {private final Logger logger = LoggerFactory.getLogger(FeignRequestInterceptor.class);public FeignRequestInterceptor(){logger.info("Initializing feign trace interceptor");}@Overridepublic void apply(RequestTemplate requestTemplate) {requestTemplate.header(TraceConstant.TRACE_ID, TraceContextUtils.getTraceId());}
}
4、利用javaagent的思路配置
这里可以看做是相对无侵入式的一种模式把,偷懒是要慢慢来
其实这里存在 静态注入 和 动态注入 ,但是走静态把
动态注入的方式: 实际上需要改造一下代码,使得可以后续动态java-agent内容。
**静态注入的方式:**只需要变更jvm,增加
-javaagent: /路径即可
4.1、基础环境内容
1、全限定类名: org.examlpe.MDCAgent
2、 测试端点包名 :org.example.controller
3、 打包后路径地址 :/opt/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar
<!-- https://mvnrepository.com/artifact/org.javassist/javassist当然这里需要引入一个依赖方便下面对类的魔改
-->
<dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.29.1-GA</version>
</dependency>
构建打包插件
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>3.1.1</version><configuration><descriptorRefs><!-- 这个为最终输出的名称--><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><archive><!-- 设置manifest配置文件--><manifestEntries><!--Premain-Class: 代表 Agent 静态加载时会调用的类全路径名。--><Premain-Class>org.example.MDCAgent</Premain-Class><!--Agent-Class: 代表 Agent 动态加载时会调用的类全路径名。--><Agent-Class>org.example.MDCAgent</Agent-Class><!--Can-Redefine-Classes: 是否可进行类定义。--><Can-Redefine-Classes>true</Can-Redefine-Classes><!--Can-Retransform-Classes: 是否可进行类转换。--><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration><executions><execution><!--绑定到package生命周期阶段上--><phase>package</phase><goals><!--绑定到package生命周期阶段上--><goal>single</goal></goals></execution></executions></plugin></plugins></build>
4.2、利用 premain 预加载
利用
premain的特性提前于主方法前加载Instrumentation使得我们可以方便操作类的一些内容(悄悄改)
public class MDCAgent {private static final Logger logger = LoggerFactory.getLogger(MDCAgent.class);/*** @todo 这里实际上我们只需要针对控制层进行补充即可*/private static final String TRANSFORM_PREIFX = "org/example/controller";public static void premain(String args, Instrumentation instrumentation) {System.out.println("premain start!");addTransformer(instrumentation);System.out.println("premain end!");}private static void addTransformer(Instrumentation instrumentation) {.... }
}
4.3、利用 Instrumentation 对类加载前魔改
1、 Modifier.isNative(method.getModifiers()) 这部分是搜罗的经验之谈
2、主要目的是针对
测试端点包名内容进行注入
instrumentation.addTransformer(new ClassFileTransformer() {public byte[] transform(ClassLoader l, String className, Class<?> c, ProtectionDomain pd, byte[] b) {try {//判断是否为目标包下的类if (className != null && className.startsWith(TRANSFORM_PREIFX)) {// 类属于目标包名下的处理逻辑System.out.println("Class " + className + " belongs to target package.");// 其他的字节码转换逻辑final ClassPool classPool = ClassPool.getDefault();final CtClass clazz = classPool.get(className.replace("/", "."));for (CtMethod method : clazz.getMethods()) {/** Modifier.isNative(methods[i].getModifiers())过滤本地方法,否则会报* javassist.CannotCompileException: no method body at javassist.CtBehavior.addLocalVariable()*/if (Modifier.isNative(method.getModifiers())) {continue;}String traceId = MDC.get(TraceConstant.TRACE_ID);if (!StringUtils.hasText(traceId)) {traceId = TraceUtils.traceIdGenerator();}MDC.put(TraceConstant.TRACE_ID, traceId);
// 这里正常不需要,仅用于查看效果
// method.insertBefore("System.out.println(\"" + clazz.getSimpleName() + "."
// + method.getName() + "-" + traceId + " start.\");");
//
// method.insertAfter("System.out.println(\"" + clazz.getSimpleName() + "."
// + method.getName() + "-" + MDC.get("haha") + " end.\");", false);}return clazz.toBytecode();}} catch (Exception e) {e.printStackTrace();}return b;}}, true);}
4.4、构建测试
目标vm参数 :-javaagent: /opt/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar
@GetMapping("ping")
public String ping() throws InterruptedException {return "pong";
}//样例测试结果
//DemoController.ping1-ab83c3c3-08aa-4e9d-9d6f-513967eb28ff start.
//DemoController.ping1-ab83c3c3-08aa-4e9d-9d6f-513967eb28ff end.
相关文章:
MDC 日志跟踪笔记
一、前言 本文主要解析应用中比较实在的一个log4j的链路应用,在经过一段时间的应用后发现还是很稳的,就记录一下这个MDC 代表Mapped Diagnostic Context(映射式诊断上下文)的情况。 Tisp: 它是一个线程安全的存放诊断日志的容器 T…...
MySQL错误-this is incompatible with sql_mode=only_full_group_by完美解决方案
项目场景 有时候,遇到数据库重复数据,需要将数据进行分组,并取出其中一条来展示,这时就需要用到group by语句。 但是,如果mysql是高版本,当执行group by时,select的字段不属于group by的字段的…...
人工智能|机器学习——基于机器学习的舌苔检测
代码下载: 基于深度学习的舌苔检测毕设留档.zip资源-CSDN文库 1 研究背景 1.1.研究背景与意义 目前随着人们生活水平的不断提高,对于中医主张的理念越来越认可,对中医的需求也越来越多。在诊断中,中医通过观察人的舌头的舌质、苔…...
SQL查询转化为 Elasticsearch 查询
使用SQL 转化为查询 Elasticsearch 支持 sql 语句转化为 elasticsearch 的 查询语句 第一步: 打开在线转换工具的网页,进入工具页面 第二步:在指定的输入框中输入需要转换的 sql 语句。 您学会了这么简单的办法...
目标检测教程视频指南大全
魔鬼面具-哔哩哔哩视频指南 必看干货系列(建议搞深度学习的小伙伴都看看,特别是图像相关) 深度学习常见实验问题与实验技巧(适用于所有模型,小白初学者必看!)还在迷茫深度学习中的改进实验应该从哪里开始改起的同学,一定要进来看看了!用自身…...
【Linux取经路】文件系统之重定向的实现原理
文章目录 一、再来理解重定向1.1 输出重定向效果演示1.2 重定向的原理1.3 dup21.4 输入重定向效果演示1.5 输入重定向代码实现 二、再来理解标准输出和标准错误2.1 同时对标准输出和标准错误进行重定向2.2 将标准输出和标准错误重定向到同一个文件 三、再看一切皆文件四、结语 …...
JAVA设计模式结构型模式
一、前言 java设计模式主要分为创建型模式,结构型模式和行为型模式。上一篇主要总结了行为型设计模式,本章总结,结构型模式。像创建型模式就不写了,比较简单。大概知道是工厂模式和建造者模式,原型模式就行࿰…...
第4讲引入JWT前后端交互
引入JWT前后端交互 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519); JWT就是一段字符串,用来进行用户身份认证的凭证,该字符串分成三段【头部、载荷、签证】 后端接口测试&…...
基于Java的车辆租赁管理平台/租车系统
功能介绍 平台采用B/S结构,后端采用主流的Springboot框架进行开发,前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括:首页、车辆详情、车辆预订、用户中心模块。后台功能包括:车辆管理、分类管理…...
如何升级至ChatGPT Plus:快速指南,ChatGPT的秘密武器GPT4.0是什么?
提到 ChatGPT。想必大家都有所耳闻。自从 2022 年上线以来,就受到国内外狂热的追捧和青睐,上线2个月,月活突破1个亿!!! 而且还在持续上涨中。因为有很多人都在使用 ChatGPT 。无论是各大头条、抖音等 App、…...
【天衍系列 05】Flink集成KafkaSink组件:实现流式数据的可靠传输 高效协同
文章目录 01 KafkaSink 版本&导言02 KafkaSink 基本概念03 KafkaSink 工作原理1.初始化连接2.定义序列化模式3.创建KafkaSink算子4.创建数据源5.将数据流添加到KafkaSink6.内部工作机制 04 KafkaSink参数配置05 KafkaSink 应用依赖06 KafkaSink 快速入门6.1 包结构6.2 项目…...
深度学习之pytorch实现逻辑斯蒂回归
深度学习之pytorch实现逻辑斯蒂回归 解决的问题数学公式logiatic函数损失值 代码与线性回归代码的区别数据损失值构造回归的函数 结果分析 解决的问题 logistic 适用于分类问题,这里案例( y为0和1 ,0和 1 分别代表一类) 于解决二分类…...
有事休假店铺无人看守怎么办?智能远程视频监控系统保卫店铺安全
在春节期间,很多自营店主也得到了久违的假期,虽然很多店主都是长期在店铺中看守,但遇到春节这样的日子,多数人还是选择回乡休假。面对店主休假或有事不能管理店铺时,传统的监控虽然可以做到单一的监控,却仍…...
酷开科技 | 酷开系统壁纸模式,让过年更有氛围感!
在阵阵爆竹声中,家家户户都沉浸在浓浓的年味中。过年,是团圆,是温暖。团团圆圆的日子里,仪式感不可少,换上一张喜气洋洋的电视壁纸吧,寓意幸福一年又一年。打开酷开系统壁纸模式挑选一张年味十足的壁纸&…...
Docker中部署flink集群的两种方式
文章目录 一、概述二、准备工作三、方式一四、方式二1、准备配置文件2、执行 docker 命令 一、概述 本文将通过 2 种方式在 docker 中部署 flink standalone 集群,集群中共有 4 个节点,分别是 1 个 jobManager 节点和 3 个 taskManager 节点。方式一能快…...
八、计算机视觉-边界填充
文章目录 前言一、原理二、具体的实现 前言 在Python中使用OpenCV进行边界填充(也称为zero padding)是一种常见的图像处理操作,通常用于在图像周围添加额外的像素以便进行卷积或其他操作。下面是使用OpenCV进行边界填充的基本原理和方法 一…...
ffmpeg 硬件加速介绍
基于OS的硬件加速 Windows 参考[2],基于windows的硬件加速都是基于DirectX API,我们可以用ffmpeg -hwaccels查看当前环境支持的硬件加速接口,如下为windows上的执行ffmpeg --hwaccels的结果。 在linux上执行ffmpeg -hwaccels的结果如下: 可以看到windows上支持的硬件加速…...
【QT+QGIS跨平台编译】之三十九:【Exiv2+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、Exiv2介绍二、文件下载三、文件分析四、pro文件4.1 exiv2-xmp4.2 exiv2lib_int4.3 exiv2lib五、编译实践一、Exiv2介绍 Exiv2是一个开源的C++库,用于读取、编辑和写入图片和视频文件的元数据。它可以处理各种类型的元数据,包括EXIF、IPTC、XMP等。 元数据是与…...
术业有专攻!三防加固平板助力工业起飞
在日常使用中的商业电脑比较追求时效性,以市场定位做标准,内部元件只需满足一般要求就行,使用寿命比较短。而三防平板电脑是主要运用在复杂、恶劣的环境下所以在需求方面较高,需要保证产品在恶劣条件下正常使用,满足行业领域的需求…...
适合tiktok运营的云手机需要满足什么条件?
TikTok作为一款全球热门的社交媒体平台,具有无限的市场潜力。然而,卖家在运营过程中常常会面临到视频0播、账号被降权、限流等问题,甚至可能因为多人同时使用一个IP而导致封号的风险。为了规避这些问题,越来越多的卖家将目光投向了…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
