惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Simon Willison's Weblog
Simon Willison's Weblog
Google DeepMind News
Google DeepMind News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Proofpoint News Feed
Recent Announcements
Recent Announcements
MongoDB | Blog
MongoDB | Blog
U
Unit 42
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
G
Google Developers Blog
I
InfoQ
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
Jina AI
Jina AI
量子位
宝玉的分享
宝玉的分享
The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 聂微东
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
美团技术团队
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tailwind CSS Blog
博客园 - 司徒正美
博客园 - 叶小钗
Hugging Face - Blog
Hugging Face - Blog
P
Palo Alto Networks Blog
博客园_首页
阮一峰的网络日志
阮一峰的网络日志
博客园 - 【当耐特】
Spread Privacy
Spread Privacy
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
Vercel News
Vercel News
Martin Fowler
Martin Fowler
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Forbes - Security
Forbes - Security
Attack and Defense Labs
Attack and Defense Labs
Google DeepMind News
Google DeepMind News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Azure Blog
Microsoft Azure Blog
P
Privacy International News Feed
G
GRAHAM CLULEY
The Last Watchdog
The Last Watchdog
C
Cyber Attacks, Cyber Crime and Cyber Security
AI
AI
V2EX - 技术
V2EX - 技术

Nicksxs's Blog

学一下chrome的扩展开发 看下chrome的内置模型 从 app.test 到小锁:valet 本地 HTTPS 的完整链路 浅析一下jpeg图片格式及其来源 关于github拉取下载加速的另一个方式 关于适合什么模型,推荐下llmfit 看看目前本地能跑什么模型,使用llama.cpp 最近使用vibe coding的一些感悟 使用php的inotify扩展来监听文件变更 一些设计模式的记忆点 使用xiaomi mimo大模型api运行Hermes Agent 结合Obsidian的cli的一体化体验 开始尝试使用obsidian作为笔记软件 学习下大神的知识库 体验下微软开源的Markdown转换工具Markitdown 学习下git的worktree 一些架构师知识点的记录 解答一下关于traefik的一点疑惑 记录一下迁移服务器需要使用的一些命令 较早代iPhone更换新iPhone的一些小指南 如何查看mac的路由表和网关等信息 如何开启mysql的optimizer trace 浅析下mysql的索引的基数与可选择性 分享个加速github访问的方法 学习体验下Koupleless框架-浅析代码 学习体验下Koupleless框架 体验下nocodb这个神奇的系统 重新复习理解下java的类加载器 记录下ffmpeg命令 尝试学习理解下Claude Code的实现原理和细节 在Antigravity中对比体验Claude Sonnet 4.5模型 来看下我装备了5060TI显卡的gpt-oss模型表现 来看下google最新力作Antigravity的水平如何
学习下MDC的机制
2026-01-03 · via Nicksxs's Blog

之前在前司的时候研究过一些链路日志相关的,有基于pinpoint,skywalking等中间件的,也有一些基于rpc框架的自研的,不过其中很多相通的逻辑应该是基于MDC来实现的,MDC 全称是 Mapped Diagnostic Context , 可以比较粗略的想成是一个存放诊断日志的工具
可以基于简单的代码来看一下

1
2
3
4
5
6
7
public static void main(String[] args) {
MDC.put(TRACE_ID, UUID.randomUUID().toString());
logger.info("开始业务逻辑处理");
logger.info("业务逻辑处理结束");
MDC.remove(TRACE_ID);
logger.info("TRACE_ID 还有吗?{}", MDC.get(TRACE_ID) != null);
}

另外再配置下

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>[%t] [%X{TRACE_ID}] - %m%n</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

一般springboot这种框架都是自带logback了的,没有的话可以引一下
打印下看

1
2
3
[main] [d8610b0d-7db8-4926-8de0-0e14e8342018] - 开始业务逻辑处理
[main] [d8610b0d-7db8-4926-8de0-0e14e8342018] - 业务逻辑处理结束
[main] [] - TRACE_ID 还有吗?false

那么MDC里究竟是啥呢
我们可以顺着 org.slf4j.MDC#put 的逻辑看下去,里面的是调用了个接口的

1
2
3
4
5
6
7
8
9
public static void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key parameter cannot be null");
} else if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
} else {
mdcAdapter.put(key, val);
}
}

这个 org.slf4j.spi.MDCAdapter 接口我们再看下实现类
我们用的是logback,就看下这个实现 ch.qos.logback.classic.util.LogbackMDCAdapter
这里的put呢,就是基于ThreadLocal的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}

Map<String, String> oldMap = copyOnThreadLocal.get();
Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

if (wasLastOpReadOrNull(lastOp) || oldMap == null) {
Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);
newMap.put(key, val);
} else {
oldMap.put(key, val);
}
}

至于这些map的操作主要是为了父子线程之间来传递MDC信息用的
主要还是基于

1
final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();

的操作,这样也就能保持线程安全
基于这个逻辑,我们在做链路日志串联的时候就有基础功能了
比如web请求,就在拦截器里可以先给MDC里设置好当前请求的请求traceId
当然这个traceId的唯一性就需要进行一步讨论比如雪花算法这种,不过一般这种情况带上机器id就可以简化点
在微服务之间进行rpc调用的时候可以基于类似于Dubbo的filter机制,在RpcContext中写入traceId,保证服务之间调用能够带上这个traceId
接下去就是更复杂的,比如定时任务,消息队列,
还有涉及到其他中间件,以及最重要的数据库的操作,需要研究类似于数据库连接池或者分库分表中间件的能力
只是总结下来,对于这类基础设施的实现,还是离不开MDC这个日志的基础能力
否则比如我们在整个应用写入日志的时候都需要处理这个traceId
有的说可以用切面,切面其实也是要基于MDC这种存储,另外就是整个应用的切面其实用起来也没那么方便
再说回来,traceId来串联日志的重要性在一般业务排查中还是起到非常大作用的,这个还有业界的dapper这种示范理念
再细化的还会在这个traceId中再叠加树形结构,去细化一个链路中的每次子调用
后面可以再对这些内容进行展开