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

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Troy Hunt's Blog
Scott Helme
Scott Helme
T
Threat Research - Cisco Blogs
T
Tenable Blog
L
LINUX DO - 热门话题
V
Visual Studio Blog
I
Intezer
Blog — PlanetScale
Blog — PlanetScale
Cisco Talos Blog
Cisco Talos Blog
A
Arctic Wolf
C
Cyber Attacks, Cyber Crime and Cyber Security
F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
N
Netflix TechBlog - Medium
SecWiki News
SecWiki News
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Project Zero
Project Zero
W
WeLiveSecurity
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
Recorded Future
Recorded Future
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
S
Securelist
Spread Privacy
Spread Privacy
L
LangChain Blog
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
MongoDB | Blog
MongoDB | Blog
Google DeepMind News
Google DeepMind News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
罗磊的独立博客
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Last Watchdog
The Last Watchdog
Attack and Defense Labs
Attack and Defense Labs
博客园 - 司徒正美
Help Net Security
Help Net Security
L
Lohrmann on Cybersecurity
人人都是产品经理
人人都是产品经理
Forbes - Security
Forbes - Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
PCI Perspectives
PCI Perspectives
博客园 - 【当耐特】
T
Tor Project blog

祝融说。

抱朴守缺。 肩吾 huan(幻) PinConsole Terminal Hole 共识并不平均,但是基本上均衡。 权力是共识切片的曲率。 存在不是「是什么」,而是一系列极小的,从分歧到共识再到分歧的,连续的构建过程。 分歧实际上是对共识往哪个方向延伸的拉力。 分歧是尚未落实的实在,是可能性基底中尚未被锁定的在共识中相互牵引的可用余量。 权力从来不是中立的,它是带有倾向性的「牵扯力」。 过程即完备,容错即自由。 第10章 逆向投喂:用真实数据让AI做出精准诊断 第11章 测试电网:用刚性指标代替肉眼审查 第12章 定期“垃圾回收”:别让系统背负AI制造的债务 第13章 防退化契约:确保每一次修改都是在进步 第14章 全生命周期演练:从零开始用“有效约束”交付一个项目 第15章 随身工具包:即查即用的约束指令库 第1章 这不是结对编程,这是“人机共生” 第2章 你必须警惕的三个陷阱 第3章 把话说死:用 Markdown 建立 AI 的“单源真相” 第4章 负空间设计:先规定“不准做什么”,再让它写代码 第5章 模块化解耦:让AI每次只面对一个小问题 第6章 上下文断头台:善用 `/clear` 斩断错误蔓延 第7章 剥夺执行权:强制 AI 像资深工程师一样“慢思考” 第8章 角色锁定:用一句话给AI戴上“思考帽” 结语:成为系统牧马人,而不是代码搬运工 第八章 金融与科技的“禁手”:现代战争的底层收敛 第二章 认知锚点:心理战中的“强制步” 第九章 历史的单行道:那些被剥夺国运的时刻 第六章 消费主义的迷宫:在货架上剥夺选择 第七章 战略逼压:没有硝烟的“切香肠战术” 第三章 构造绝杀:逻辑闭环的艺术 第十二章 绝对零度下的生机:成为“不可测”的人 第十一章 掀翻棋盘:非对称竞争与正交化打击 第十章 识破隐形控制:第一时间嗅到危险 第四章 标准的暴政:打造“不得不”的生态 第五章 锁死阀门:关系链与供应链的囚笼 第一章 降熵法则:时空维度的隐蔽控制 结语:愿你在这个被设定的世界里,永远握有掀桌的权力。 引言:自由意志的幻觉 项羽 当前的AI工程化本质上是受限于上下文长度而采用的「以提示词约束去置换确定性」的一种妥协。 即时反应是一种不假思索,它是观念通过最简单、轻易、高效的路径寻求表达。 青衫 第九章:估值的坐标:建立“内在记分牌” 第六章:资产重估的艺术:从“硬实在”到“认知折价” 第七章:预期差交易:坍缩“概率波” 第十一章:内在博弈:克服‘评估异化’ 第十章:交易的算法:逆人性的“人之道” 第五章:空间套利:高能耗产业的跨境建构 结语:构建你的“财富多重宇宙” Code-Coder 第八章:效率革命:细分赛道的“降本增效” 第二章:评估权的博弈:商业模式的权力差序 第三章:去伪存真:穿透“符号泡沫” 第四章:能源共识:黑金的物理法则 第一章:宏观观察者:借势“国家共识” 前言:从“市场囚徒”到“清醒的观察者” Code-Ledge-X Code-Lint-X
第9章 遥测驱动:帮AI长出“千里眼”和“顺风耳”
祝融 · 2026-05-16 · via 祝融说。

调试这个词,常常带有一种被动和“救火”的意味——问题已经发生了,我们焦头烂额地去寻找原因。而“遥测驱动”则是一种主动的、前置的哲学。它认为,可观测性不是一个事后添加的“补丁”,而是软件设计之初就必须内置的核心能力。

一个不具备良好可观测性的系统,对于AI来说,就是一具冰冷的“尸体”。AI只能进行静态的“尸检”,通过分析代码的结构,去猜测“死因”。而一个植入了精良遥测系统的软件,则变成了一个可以被实时监控的“活体”。当它生病时,我们可以为AI提供一份详尽的“体检报告”——心电图、血液报告、CT扫描图——让AI能够像一个高明的医生一样,通过分析动态的、连续的生命体征数据,进行精准的“活检”和诊断。

本章,我们将聚焦于遥测体系中最基础、也最强大的工具——日志。但我们将彻底抛弃那种随意的print()console.log()式的“游击战”打法,转而学习如何设计和植入一套“军用级”的结构化日志系统。这套系统,将成为你和AI之间,沟通真实世界情况的通用语言。

9.1 大多数 Bug 不在代码里,而在运行环境中

在软件开发的初级阶段,我们遇到的Bug大多是“语法错误”或“简单逻辑错误”。这些问题,AI极其擅长解决,因为它们都封闭在代码的静态世界里。

然而,随着你成为一名更资深的工程师,你会越来越深刻地体会到一个“残酷”的真相:绝大多数棘手的、耗费数天甚至数周才能解决的Bug,其根源往往不在于代码本身写错了什么,而在于代码与它所运行的复杂环境之间,发生了意料之外的、致命的“化学反应”。

这些“环境依赖型”Bug,是AI的天然克星,因为环境信息是它知识的绝对盲区。

Bug的“藏身之处”:AI看不见的五个维度

  1. 时间维度

代码的执行顺序,在多线程、异步编程中,充满了不确定性。两个事件的发生,前后顺序差几毫秒,可能就会导致截然不同的结果。这就是所谓的“竞态条件”。

  • AI的盲点:AI在阅读代码时,默认是线性的、静态的。它无法“看到”在真实世界中,一个网络回调函数可能在一个用户点击事件处理到一半时,突然插入执行,从而污染了共享状态。
  • 典型Bug:一个前端应用,在页面加载时,同时发出了两个API请求:获取用户信息(A)和获取用户购物车(B)。购物车依赖于用户信息。由于网络波动,请求B比请求A先返回了。代码因为拿不到用户信息而崩溃。这个Bug在99%的情况下不会复现,只在特定的网络时序下才会发生。
  1. 状态维度

一个长时间运行的应用,其内部状态(内存中的变量、数据库中的数据)会随着时间不断累积和演变。一个Bug可能只在系统运行了72小时、处理了100万个请求、内存占用达到某个临界值之后,才会触发。

  • AI的盲点:AI看到的,永远是代码的“初始状态”或“理想状态”。它无法想象一个变量在经历了上万次迭代后,可能会因为浮点数精度问题,累积出一个微小但致命的误差。
  • 典型Bug:一个计数器服务,在高并发下,由于没有使用原子操作,导致计数值在高负载下出现偏差,越跑越不准。这个问题在低并发的开发环境中,永远无法复现。
  1. 外部依赖维度

你的代码,只是庞大软件生态系统中的一个节点。它依赖于操作系统、第三方库、外部API、数据库、缓存服务……这个生态系统中的任何一个环节出现问题,都会波及你的代码。

  • AI的盲点:AI不知道你调用的那个支付API,今天下午3点到4点之间,因为服务商机房断电,一直返回500错误。它只会看到你的代码在处理支付逻辑时,进入了一个它无法理解的异常分支。
  • 典型Bug:一个Python应用,在开发者的macOS上运行良好。但部署到基于Linux的服务器上时,一个处理文件路径的函数,因为没有考虑\/的差异,而全盘崩溃。
  1. 配置维度

同样一套代码,在不同的配置(环境变量、功能开关、A/B测试分组)下,其行为可能天差地别。

  • AI的盲点:AI看不到你的生产环境里,有一个名为FEATURE_FLAG_NEW_CHECKOUT的环境变量被设置成了true,从而激活了一套全新的、未经充分测试的结算流程。
  • 典型Bug:代码在测试环境中完美运行。一上线,立刻引发大量订单失败。经过数小时排查,才发现是生产环境的数据库连接池大小配置得过小,在高流量下被迅速耗尽。
  1. 用户输入维度

你永远无法预料用户会如何“玩坏”你的软件。他们会输入Emoji表情、超长字符串、甚至是恶意的脚本代码。

  • AI的盲点:AI在生成代码时,总是倾向于处理“阳光路径”。它会假设用户输入了格式良好的Email和密码,而不会去主动防御一个用户在用户名里输入了'; DROP TABLE users; --
  • 典型Bug:一个图片上传功能,在处理一个用户上传的、文件名包含特殊Unicode字符(如U+202E,从右至左覆盖符)的图片时,导致整个文件系统API调用失败。

结论

面对这些“环境依赖型”Bug,仅仅把错误日志和代码片段扔给AI,是远远不够的。这就像只给医生看一张X光片,却不告诉他病人的年龄、病史、生活习惯和具体症状。

我们的任务,就是通过“遥测”,为AI提供一份包含了时间、状态、依赖、配置和输入的、立体的、全息的“病历报告”。 而这份报告的基石,就是结构化日志。

9.2 结构化日志的植入策略:让每一步操作都留下痕迹

忘掉console.log("here")print(f"var is {var}")吧。这种“非结构化”的日志,对于人类来说,勉强可读;对于AI来说,则是几乎无法解析的“垃圾文本”。它们就像一堆杂乱无章的、手写的便条,而不是一份条理清晰的报告。

结构化日志,是指将日志信息,以一种一致的、机器可读的格式(通常是JSON)进行记录。每一条日志,不再是一个简单的字符串,而是一个包含了丰富“元数据”的对象。

非结构化日志 vs. 结构化日志

  • 非结构化: [2023-11-01 10:30:15] ERROR: User login failed for user [email protected] from IP 192.168.1.10.
  • 结构化 (JSON格式):
{
    "timestamp": "2023-11-01T10:30:15.123Z",
    "level": "ERROR",
    "message": "User login failed",
    "context": {
    "event_type": "USER_LOGIN_ATTEMPT",
    "username": "[email protected]",
    "source_ip": "192.168.1.10",
    "reason": "INVALID_PASSWORD"
    }
}

看到区别了吗?结构化日志,对于AI来说,是一份可以直接“理解”和“查询”的数据库。AI可以轻易地解析出“事件类型”、“用户名”、“IP地址”等关键字段,并将它们与其他日志关联起来。

植入策略:像设计API一样设计你的日志

在植入日志时,我们不能随心所欲。我们需要一套统一的、贯穿整个应用的策略。

策略一:分级记录,明确意图

不要把所有日志都记为INFO。使用标准的日志级别,来表达每条日志的“重要性”和“意图”。

  • DEBUG: 仅在开发环境中用于诊断的、极其详细的信息。比如,一个函数的完整入参和出参。
  • INFO: 记录应用生命周期中的关键“里程碑”事件。这些事件本身不是错误,但能帮助我们串联起一个完整的操作流程。比如,“User created account”、“Order submitted”、“Payment processed”。
  • WARN: 发生了某种预期内的、可恢复的“异常”,但应用仍能继续运行。比如,“Third-party API timeout, retrying...”、“User uploaded a non-standard image format, attempting conversion.
  • ERROR: 发生了导致当前操作失败的、严重的错误。应用的核心功能受到了影响。比如,“Database connection lost”、“Failed to process payment”。
  • FATAL / CRITICAL: 发生了导致整个应用实例崩溃的、灾难性的错误。比如,“Uncaught exception, application shutting down.

AI如何使用日志级别? 当你给AI一份日志时,它会首先关注ERRORFATAL级别的日志,将其作为调查的“入口点”。然后,它会根据时间戳,向上追溯WARNINFO级别的日志,来重建错误发生前的“上下文”。

策略二:上下文注入,丰富细节

每一条日志,都应该包含尽可能多的、与当前操作相关的“上下文”信息。

  • Who? (用户): user_id, session_id, tenant_id
  • Where? (位置): service_name, module_name, function_name, hostname
  • What? (关联实体): order_id, product_id, request_id
  • How? (参数): 关键的函数参数、请求体中的重要字段(注意脱敏)。

AI如何使用上下文? 上下文信息,是AI进行“关联分析”的关键。当AI看到一条关于order_id: 123的支付失败ERROR日志时,它可以立刻在日志系统中,搜索所有同样带有order_id: 123的日志,从而构建出这个订单从“创建”到“支付失败”的完整生命周期,即使这些日志来自不同的微服务。

策略三:事件驱动,命名标准化

与其记录模糊的“做了某事”,不如将日志视为一个个离散的、有明确名称的“事件”。为这些事件,制定一套统一的命名规范。

  • 坏的message: "Saving user"
  • 好的event_type: "USER_PROFILE_UPDATE_STARTED"
  • 坏的message: "User saved"
  • 好的event_type: "USER_PROFILE_UPDATE_SUCCEEDED"
  • 坏的message: "Error saving user"
  • 好的event_type: "USER_PROFILE_UPDATE_FAILED"

使用NOUN_VERB_STATE(名词_动词_状态)的格式,是一个很好的实践。

AI如何使用事件名? 标准化的事件名,让AI可以更容易地理解一个业务流程的“状态机转换”。它可以清晰地看到一个流程是从STARTED状态,正常地转换到了SUCCEEDED状态,还是意外地跳到了FAILED状态。

策略四:日志即遥测,而非调试

这个思维转变至关重要。植入日志的目的,不是为了你将来“可能”要去调试某个问题,而是为了让系统在“任何时候”都具备自我描述其运行状态的能力。

这意味着,你应该在代码的“关键路径”和“决策点”上,主动地、慷慨地植入INFO级别的日志,即使这些路径在当下看起来“不可能出错”。

  • 在每个API请求的入口和出口,记录请求和响应信息。
  • 在每个重要的业务流程(如用户注册、下单、支付)的开始、成功和失败点,记录事件。
  • 在每次与外部系统(数据库、API、消息队列)交互前后,记录交互的意图和结果。

一开始,你可能会觉得这很“啰嗦”。但当那个隐藏极深的生产环境Bug出现时,你会感谢当初的自己,为AI提供了如此丰富、详尽的“案发现场录像”。

9.3 构建“操作-事件”全链路追踪,捕获隐藏的竞态条件

结构化日志解决了“单点”信息的丰富度问题。但要捕获那些与“时间”和“并发”相关的魔鬼——比如竞态条件——我们还需要一个更强大的武器:全链路追踪。

其核心思想非常简单:为一个完整的用户操作(比如,一次API请求),生成一个全局唯一的“追踪ID”(trace_id)。然后,将这个trace_id,作为上下文,注入到这个操作所引发的所有日志、所有跨服务的调用中。

【一个简化版的例子】

  1. 用户点击“购买”按钮,前端生成一个trace_id: "abc-123"
  2. 前端发出API请求 POST /orders,并在HTTP头中携带 X-Trace-ID: abc-123
  • 前端记录日志: {"level": "INFO", "event": "ORDER_SUBMIT_STARTED", "trace_id": "abc-123", ...}
  1. 后端订单服务收到请求,从HTTP头中解析出trace_id
  • 订单服务记录日志: {"level": "INFO", "event": "ORDER_RECEIVED", "service": "order-svc", "trace_id": "abc-123", ...}
  1. 订单服务需要调用支付服务。它在向支付服务发出的RPC请求中,继续传递trace_id
  • 订单服务记录日志: {"level": "INFO", "event": "PAYMENT_CALL_STARTED", "service": "order-svc", "trace_id": "abc-123", ...}
  1. 支付服务处理请求。
  • 支付服务记录日志: {"level": "INFO", "event": "PAYMENT_PROCESSED", "service": "payment-svc", "trace_id": "abc-123", ...}
  1. 支付服务返回结果给订单服务,订单服务最终返回结果给前端。

全链路追踪的威力

现在,当你想调查trace_id: "abc-123"这次操作到底发生了什么时,你只需要在你的日志聚合系统(如ELK, Datadog)中,简单地查询trace_id = "abc-123"

你将得到一个按照时间顺序,精确排序的、跨越了前端、订单服务、支付服务的完整“事件流”。

AI如何利用这个事件流来捕获竞态条件?

假设我们遇到了一个Bug:用户的账户被重复扣款了。这是一个典型的竞态条件问题,很可能是因为用户的快速点击,导致前端在短时间内,为同一次购买行为,发出了两个POST /orders请求。

如果没有trace_id,你会在日志里看到两条几乎一模一样的支付记录,很难区分它们是否属于同一次用户意图。

而有了trace_id,情况就完全不同了。你会看到类似这样的日志流:

10:30:15.100Z | INFO | ORDER_SUBMIT_STARTED | trace_id: a-1, ...
10:30:15.150Z | INFO | ORDER_SUBMIT_STARTED | trace_id: b-2, ... (!! 另一个trace_id)
10:30:15.200Z | INFO | ORDER_RECEIVED | trace_id: a-1, ...
10:30:15.250Z | INFO | ORDER_RECEIVED | trace_id: b-2, ...
...
10:30:15.500Z | INFO | PAYMENT_PROCESSED | trace_id: a-1, ...
10:30:15.550Z | INFO | PAYMENT_PROCESSED | trace_id: b-2, ...

当你把这段日志交给AI时,它的推理过程会是这样的:

  1. 识别异常模式:“我观察到,在50毫秒(150ms-100ms)的极短时间内,系统启动了两次独立的订单提交操作(trace_id不同)。”
  2. 关联上下文:“这两次操作,最终都导致了支付成功(PAYMENT_PROCESSED)。”
  3. 提出假设:“这极有可能是因为前端没有对提交按钮做‘防抖’或一次性锁定处理,导致用户的快速双击,被系统视为两次独立的购买请求。”
  4. 建议解决方案:“为了修复这个问题,我建议在前端的‘提交’按钮的点击事件处理函数中,增加一个状态锁。当第一次点击后,立即禁用按钮,直到API请求返回结果后,才重新启用它。”

看到这个威力了吗?trace_id就像一根魔法丝线,将散落在各个系统、各个时间点的“珍珠”(日志事件),串成了一串清晰可辨的“项链”(操作流)。 AI不再是面对一堆散乱的珠子,而是可以直接分析这条项链的结构、顺序和瑕疵。那些隐藏在时间缝隙中的“魔鬼”,在全链路追踪面前,将无所遁形。

【注入指南】三大类场景的日志埋点方案

理论已经清晰,现在让我们落地到具体的实践中。在日常开发中,我们主要会遇到三类需要精心设计日志埋点的场景。

场景一:关键业务流程

  • 目标:完整记录一个核心业务(如注册、下单、内容发布)的端到端生命周期。
  • 埋点策略:
  1. 入口点:
  • 在请求开始时,记录INFO级别的_STARTED事件。
  • 生成trace_id(如果上游没有传来)。
  • trace_id和所有关键上下文(user_id等)注入到日志记录器的全局上下文中。
  • 记录脱敏后的请求体。
  1. 服务层/业务逻辑:
  • 在关键的业务决策点,记录INFO日志。例如:“User balance sufficient, proceeding with deduction.
  • 在与外部依赖(DB, API)交互前后,记录INFO级别的_CALL_STARTED_CALL_SUCCEEDED / _CALL_FAILED事件。
  1. 出口点:
  • 在请求正常结束时,记录INFO级别的_SUCCEEDED事件。
  • 在请求因业务异常(如校验失败)结束时,记录WARN级别的_FAILED事件,并附带reason
  • 在请求因系统内部错误结束时,记录ERROR级别的_FAILED事件,并附带error堆栈。
  • 记录响应码和响应体(脱敏)。

场景二:异步与后台任务

  • 目标:追踪那些没有直接用户交互的、在后台运行的复杂任务(如数据处理、报表生成、消息队列消费者)。
  • 埋点策略:
  1. 任务触发点:
  • 当任务被加入队列或被调度时,记录INFO级别的_SCHEDULED事件。
  • 生成job_idtrace_id,并将它们作为任务的元数据,一起传递给后台工作者。
  1. 工作者:
  • 当工作者从队列中取出任务时,记录INFO级别的_PICKED_UP事件,并从元数据中恢复job_idtrace_id,注入日志上下文。
  • 在任务开始执行时,记录INFO级别的_STARTED事件。
  • 对于长任务,应在处理过程中,周期性地记录“心跳”或“进度”日志。例如:“Processed 1000 of 10000 records.
  1. 任务结束点:
  • 成功时,记录INFO级别的_SUCCEEDED事件,并附带结果摘要(如“Generated report with 5000 rows.”)。
  • 失败时,记录ERROR级别的_FAILED事件,并附带完整的错误信息。
  • 如果任务支持重试,应在每次重试前,记录WARN级别的_RETRYING事件,并附带重试次数。

场景三:前端用户交互

  • 目标:重建用户在浏览器中的完整操作路径,以复现那些难以捉摸的UI Bug。
  • 埋点策略:
  1. 会话开始:
  • 在应用加载时,生成一个唯一的session_id
  1. 核心交互事件:
  • 记录所有关键的用户行为为INFO级别的事件:PAGE_VIEW, BUTTON_CLICK, FORM_SUBMIT, ITEM_DRAGGED
  • 在事件的上下文中,附带组件名、元素的ID或data-testid属性,以及相关的业务数据。
  1. API请求:
  • 使用axios拦截器或fetch的封装,自动为所有发出的API请求,生成trace_id,并记录_API_REQUEST_STARTED事件。
  • 在收到响应后,记录_API_REQUEST_SUCCEEDED_API_REQUEST_FAILED事件,并附带状态码和trace_id
  1. 状态管理:
  • 对于像Redux这样的状态管理器,可以利用中间件,在每次action被派发时,自动记录action的类型和负载。这能提供一份完整的状态变更历史。
  1. 全局错误捕获:
  • 设置全局的window.onerrorunhandledrejection处理器。一旦捕获到未处理的JS异常,立即记录FATAL级别的_UNCAUGHT_EXCEPTION事件,并附带上整个会话期间积累的前端日志(“面包屑” - breadcrumbs),然后将它们一起发送到日志服务器。

通过在这三大场景中,系统性地植入高质量的遥测探针,你就为AI铺设了一张无处不在的“神经网络”。当问题发生时,你不再需要去猜测,你只需要从这张网络中,提取出与问题相关的、结构化的“神经信号”(日志),然后交给AI。

你不再仅仅是AI的“提问者”,你成为了它的“感官系统”,成为了它在复杂、混沌的真实世界中,赖以生存的眼睛和耳朵。这是人机协作走向更高阶、更深层次的必经之路。