<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>监控 on 姚玉亮的网络日志</title><link>https://yusanwen-code.github.io/tags/%E7%9B%91%E6%8E%A7/</link><description>Recent content in 监控 on 姚玉亮的网络日志</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Sun, 19 Jul 2026 10:30:00 +0800</lastBuildDate><atom:link href="https://yusanwen-code.github.io/tags/%E7%9B%91%E6%8E%A7/index.xml" rel="self" type="application/rss+xml"/><item><title>线上问题排查方法论：从日志、链路到监控</title><link>https://yusanwen-code.github.io/posts/post-68/</link><pubDate>Sun, 19 Jul 2026 10:30:00 +0800</pubDate><guid>https://yusanwen-code.github.io/posts/post-68/</guid><description>&lt;img src="https://yusanwen-code.github.io/images/post-68-cover.jpg" alt="Featured image of post 线上问题排查方法论：从日志、链路到监控" /&gt;&lt;h2 id="一个报错横跨四五个服务"&gt;一个报错，横跨四五个服务&#10;&lt;/h2&gt;&lt;p&gt;做后端这些年，线上问题排查花掉的时间不比写新功能少。&lt;/p&gt;&#10;&lt;p&gt;在某 SaaS 公司时，宠物医疗 SaaS 系统从 fasthttp C/S 迁到 go-zero B/S，微服务一多，一个&amp;quot;医生开不了处方&amp;quot;的报错可能横跨网关、鉴权、处方、HIS 对接四五个服务。后来做 AI 数据平台，知识库问答服务和数据集管理服务又多了 LLM 调用、向量化、Temporal Worker 这些新组件，问题形态更杂。&lt;/p&gt;&#10;&lt;p&gt;坑踩得够多之后，我形成了一套固定的排障顺序：先看监控定边界，再查链路定位置，最后翻日志看细节。顺序反了，就是在大海里捞针。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="排障三步：监控定边界、链路定位置、日志看细节" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://yusanwen-code.github.io/images/post-68-triage-steps.svg"&gt;&lt;/p&gt;&#10;&lt;h2 id="先看监控把边界画出来"&gt;先看监控：把边界画出来&#10;&lt;/h2&gt;&lt;p&gt;告警来了先别急着登机器，先在 Grafana 上回答三个问题：影响面多大（单用户还是全量）、从什么时候开始（发版后还是突发）、哪个指标异常（错误率、延迟、CPU、内存、队列堆积）。&lt;/p&gt;&#10;&lt;p&gt;我们在 KubeSphere 上给每个服务配了 RED 指标（Rate、Errors、Duration），业务侧再加关键看板：统一支付平台看支付成功率和回调延迟，统一认证中心看登录失败率和各 Provider（腾讯云 SMS/SES）的错误码分布，知识库问答服务看首 token 建立延迟和各 LLM 供应商的超时率。&lt;/p&gt;&#10;&lt;p&gt;一张 dashboard，能先把&amp;quot;是不是我的问题、是我的问题大概在哪&amp;quot;筛掉八成。&lt;/p&gt;&#10;&lt;h2 id="再查链路定位到那一步"&gt;再查链路：定位到那一步&#10;&lt;/h2&gt;&lt;p&gt;确定是某个服务的问题后，用 trace_id 把整条请求链拉出来。我们在 go-zero 和 Hertz 里都接了 gRPC 拦截器和 HTTP middleware，把 trace_id 从入口一路透传到下游、MQ 消费者、Temporal Workflow。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&#10;&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 1&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 2&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 3&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 4&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 5&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 6&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 7&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 8&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 9&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;10&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;11&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;12&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;13&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;14&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;15&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;16&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;17&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;18&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;19&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;20&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;21&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;22&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// Hertz 中统一的 trace + 日志中间件&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;func&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;AccessLog&lt;/span&gt;()&lt;span style="color:#6e7681"&gt; &lt;/span&gt;app.HandlerFunc&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;return&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;func&lt;/span&gt;(ctx&lt;span style="color:#6e7681"&gt; &lt;/span&gt;context.Context,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;app.RequestContext)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;traceID&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;:=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;string(c.Request.Header.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Get&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;X-Trace-Id&amp;#34;&lt;/span&gt;))&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;traceID&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;==&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;traceID&lt;span style="color:#6e7681"&gt; &lt;/span&gt;=&lt;span style="color:#6e7681"&gt; &lt;/span&gt;snowflake.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;NextString&lt;/span&gt;()&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.Request.Header.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Set&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;X-Trace-Id&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;traceID)&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.Response.Header.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Set&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;X-Trace-Id&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;traceID)&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;start&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;:=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;time.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Now&lt;/span&gt;()&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Next&lt;/span&gt;(ctx)&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;log.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Info&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;http access&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;zap.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;String&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;trace_id&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;traceID),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;zap.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;String&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;method&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;string(c.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Method&lt;/span&gt;())),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;zap.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;String&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;string(c.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Path&lt;/span&gt;())),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;zap.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Int&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.Response.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;StatusCode&lt;/span&gt;()),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;zap.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Duration&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;cost&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;time.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Since&lt;/span&gt;(start)),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;)&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;拿到 trace_id 去 Jaeger 看瀑布图，一眼能看出哪个 span 慢、哪个 span 报错。AI 场景里我特意把对 LLM 的调用、向量检索、S3 上传都包成独立 span，不然一个&amp;quot;回答超时&amp;quot;，你根本分不清是模型慢还是检索慢。&lt;/p&gt;&#10;&lt;h2 id="最后翻日志看为什么"&gt;最后翻日志：看为什么&#10;&lt;/h2&gt;&lt;p&gt;trace 定位到具体服务和时段后，去 ELK 用 &lt;code&gt;trace_id: &amp;quot;xxx&amp;quot;&lt;/code&gt; 精确捞日志。&lt;/p&gt;&#10;&lt;p&gt;我们的日志规范是：顶层打印请求入参和最终错误，中间层只追加上下文、不重复打错误，错误用 &lt;code&gt;zap.Error(err)&lt;/code&gt; 带堆栈。一条合格的错误日志，应该能直接回答&amp;quot;什么操作、什么入参、为什么失败&amp;quot;。&lt;/p&gt;&#10;&lt;h2 id="四条规矩"&gt;四条规矩&#10;&lt;/h2&gt;&lt;p&gt;第一，日志别瞎打。早期有人在循环里打全量文档内容，一次解析任务日志几百 MB，ELK 直接被打爆。后来定了规矩：DEBUG 级别打细节，生产默认 INFO；大对象只打 ID 和长度；敏感字段（手机号、密钥）一律脱敏。统一认证中心里这是红线。&lt;/p&gt;&#10;&lt;p&gt;第二，告警要可收敛。每个错误都告警等于没有告警。按服务 + 错误类型聚合，5 分钟内同类错误只发一条，再配升级策略：错误率超阈值且持续 10 分钟才打电话。夜间告警的质量，直接决定你能不能睡个整觉。&lt;/p&gt;&#10;&lt;p&gt;第三，异步任务的 trace 要手动续上。MQ 消费者和 Temporal Workflow 是新的执行上下文，trace_id 不会自动传过去，必须生产端塞进消息体、消费端取出来注入 context。这块漏了，异步链路就是断的，排障时最痛苦。&lt;/p&gt;&#10;&lt;p&gt;第四，回滚优先于根因。KubeSphere 容器化部署后，回滚 5 分钟内搞定。发现是发版引起的问题，第一反应是回滚，不是在线上 debug。业务恢复之后再慢慢查根因，顺序不能反。&lt;/p&gt;&#10;&lt;h2 id="后来"&gt;后来&#10;&lt;/h2&gt;&lt;p&gt;排障能力拼的不是谁见过的错误多，而是有没有一套不依赖运气的检索路径。监控告诉你&amp;quot;哪里不对&amp;quot;，链路告诉你&amp;quot;在哪一步不对&amp;quot;，日志告诉你&amp;quot;具体为什么不对&amp;quot;。三件事平时建设好，告警响的时候才不会慌。&lt;/p&gt;&#10;</description></item></channel></rss>