下午三点一个同事发来消息“客户那边保存资料一直报 422前端说参数没问题后端说也没报错你帮忙看下。”我放下手头的活打开前天的排查记录翻到接口类问题那页。这种“两边都说没问题”的 Bug我见过太多次了。排查到最后百分之八十都是沟通假设出了问题要么前端和后端看的是两个版本的接口文档要么某个字段类型在序列化之后就变了样。在项目里待得越久我越觉得“Bug 排查”不是一种天赋而是一套可以复盘、可以沉淀、可以写成日记的方法论。这篇文章我就把这些年攒下来的排查思路、命令、踩坑实录整理成一份“可复现的排查手册”。如果你刚接手一个项目或者经常被线上问题追着跑这应该能帮你少走不少弯路。1. 先理清思路Bug 排查的底层逻辑1.1 一线排查的核心原则先复现再动手很多新手接到 Bug 的第一反应是“猜”然后立刻去改代码。我踩过的坑告诉我凡是不能稳定复现的问题改代码基本都是盲改。你压根不知道这次改动到底动了什么下次问题重现时也没法判断是不是自己的修复起了作用。我给自己定的第一条规矩复现是一切排查的前提。哪怕是那种看起来“偶尔才出现一次”的疑难杂症也要尽量找到触发条件。去年我碰到过一个线上服务每天凌晨三点左右报错白天一切正常。排查了三天都没头绪后来翻定时任务才发现是凌晨的日志切割把日志文件句柄换了服务还在往旧文件里写凑巧触发了磁盘空间的连锁问题。这个 Bug 之所以难查就是因为触发条件里藏着一个“时间窗口”。所以接到问题先别急着翻代码。问自己三个问题问题什么时候开始出现的最近改了什么东西有没有办法稳定触发这三个问题能解决大概一半的“查无实据”型 Bug。如果实在复现不了就想办法把现场保住把相关日志备份出来、把环境快照留着、把用户的操作路径问清楚。复现 Bug 的另一个重要作用是区分“症状”和“根因”。系统报警说磁盘满了服务挂了一堆但磁盘满只是症状真正的根因可能是日志没有配置轮转策略也可能是某个临时目录里躺着几个超大文件。如果只盯着“磁盘满”去删文件删完过两天又满了。找到让磁盘满的那条链路才叫修根因。1.2 问题分级与排查顺序性价比思维不是所有 Bug 都值得用同样的力度处理。我自己习惯先分个级P0 是核心功能完全不可用比如登录挂了、支付回调收不到这种问题第一时间要恢复业务哪怕先用临时方案兜底P1 是主要功能受影响但有规避手段比如某个报表导出失败可以先让用户走手工流程P2 是偶发、体验类问题比如某个按钮在某些分辨率下错位这种可以排期慢慢查。分级之后再决定排查顺序。我的习惯是先网络层再系统层然后应用层最后才到代码逻辑层。原因很简单越靠下的环节出问题时影响面越大也越容易被误判成上层问题。比如一个接口突然全部超时花半小时定位到代码结果发现是机房网络策略变了这个顺序就亏大了。排查最怕的是没有章法地“东一榔头西一棒子”。我一般会先在纸上列一份“可能原因清单”把想到的所有可能都写下来然后按从大概率到小概率的顺序一个一个验证。这个习惯看起来笨实际上非常高效因为它能逼着你把“感觉”变成“假设”再用证据去证实或排除。排查 Bug 不是靠灵光一现而是靠证据链。2. 前后端 Bug 的判断与实战2.1 一眼定界这个 Bug 到底归前端还是后端项目里前后端联调最常见的争执就是“这个 Bug 到底归谁”。其实定界的思路特别简单打开浏览器开发者工具的 Network 面板看请求到底发出去了没有有没有响应响应长什么样。如果点击按钮后Network 面板里根本没有任何请求发出那大概率是前端问题要么按钮没绑定事件要么路由跳转压根没触发。如果请求发出去了后端也回了但返回的 4xx/5xx那要看状态码和响应体是谁产生的通常就是后端的问题前端要配合提供请求参数。如果接口返回 200但页面上数据不显示、状态不更新那问题又回到了前端要么是渲染逻辑写错了要么是状态管理里的数据没接上。我还有一个经验先确认“是不是所有用户都受影响”。如果只有某一个人报 Bug先看他的账号、浏览器、网络环境说不定是缓存问题。如果所有人都出问题再去查服务端和发布记录。按这个顺序通常能很快把问题圈定在一个范围内。2.2 前端排查的高频坑与常用切入点前端问题三个最常用的切入点Console 面板的报错、Network 面板的请求记录、还有 Application/状态管理里的数据。九成问题都能靠这三个入口定位剩下的三成是渲染和兼容性。有一个典型的例子是用过的三维设计软件里“材料视图刷新异常”。某次切换材质库之后视图里的旧材质颜色一直不消失只有点击其他对象再切回来才刷新。这种 Bug 和接口没关系是客户端渲染层的状态没有及时失效类似前端常见的“组件缓存了旧数据没清掉”的问题。在没有源码的情况下我的排查思路是先尝试重置视图状态、清缓存再看是不是显卡驱动兼容性问题因为很多“显示不对”的 Bug 其实是 GPU 加速和渲染管线不兼容导致的。前端最容易踩的几个坑我列一下一是浏览器缓存前端发布后用户还开着旧页面报一堆根本不存在的问题所以遇到“用户说页面不对”先让他强制刷新二是跨域请求从页面发出去了但浏览器因为 CORS 拦截了响应在 Network 里能看到请求发出去拿到不到数据容易让人误判成后端问题三是字段类型不一致这个在前后端对接时特别常见前端拿字符串“123”传给后端后端用 Integer 接收解析失败四是第三方库版本更新后 API 变了项目里某个组件突然开始报错先查是不是依赖升级导致的 Breaking Change。2.3 后端排查的关键抓手与 422 通信故障实录后端排查的核心抓手永远是日志。我见过太多服务“日志都舍不得打一行”的做法出了问题只能靠重启。我自己写代码的习惯是每个关键接口入参、出参、异常堆栈都要留痕迹每一条日志尽量带上 traceId 或 requestId这样排查时可以把一次请求的全链路拼起来。后端遇到“422 通信故障”很多人会懵因为这个状态码比 400 少见一点。422 是 Unprocessable Entity意思是请求格式正确但语义上没法处理最常见的就是字段校验失败。我之前处理过一个案例小程序端提交表单数据看起来完全正常但接口一直回 422。前端的同事把 Payload 截图发过来我一眼看到问题一个日期字段传的是“2024-06-10 14:30:00”后端的 DTO 里定义的是 LocalDate字符串转不了另一个枚举字段传的是中文名称后端期待的是整型数字。这类问题的排查路径非常固定打开 Network 面板看请求的 Payload 长什么样再对照后端的接口文档和数据校验规则逐字段比对。404 和 405 是地址或方法不对400 是请求格式不对422 是“收到了但校验不过”。把这几类状态码的语义搞清楚前后端沟通就能省掉一半扯皮。2.4 构建脚本报错也有“潜台词”semantic analysis 异常排查有一种报错特别容易被当成玄学bug! exception in phase semantic analysis in source unit _buildscript_ u。这个其实是 Gradle 构建脚本在编译阶段挂了在做“语义分析”的时候发现了错误。很多人看到“semantic analysis”就以为是运行环境出问题跑去查 JVM、查依赖方向完全跑偏。这种报错的本质是构建脚本本身无法通过编译常见原因有三类Kotlin DSL 语法写错了、引用了不存在的扩展函数或者依赖、buildSrc 或 buildscript 块的版本配置和主工程冲突。排查步骤很简单先把堆栈拉到最顶部看它定位到脚本文件的哪一行再检查 Gradle Wrapper 的版本和 buildSrc 里配置的版本是否一致如果还查不出来把复杂脚本块注释掉做最小化编译验证用二分法缩小范围最后清理一下.gradle缓存排除缓存损坏的影响。这类报错给我最大的教训是看到报错里的“phase”关键字先判断它是编译期还是运行期。编译期的错误永远不要用“重启大法”去解决重启一万次也不会有结果。3. 网络与连接类故障排查实录3.1 IP 冲突排查从“网络很卡”到抓到肇事者办公室里经常有人喊“网络卡”一查发现是 IP 冲突。这个问题的经典现象是终端频繁掉线Ping 网关忽通忽断过一会儿又自动恢复。我处理过的一台电脑就是这个症状用户说“重启之后好一会儿然后又开始卡”。排查 IP 冲突最直接的办法是看 ARP 表。Windows 上用arp -aLinux 上用ip neigh如果看到同一个 IP 对应了两个不同的 MAC 地址那就基本实锤了。我当时在 Windows 上执行arp -a192.168.1.88 这个地址确实有两个 MAC 在来回横跳一台是打印机一台是电脑。打印机配了 DHCP 保留地址电脑有人手工配了静态 IP正好撞上了。处理方式很简单把电脑改成 DHCP 自动获取或者把地址改成另一段没人用的 IP再在交换机上看端口和 MAC。彻底一点的预防办法是在网络设备上开启 DHCP Snooping 和动态 ARP 检测绑定合法 DHCP 服务器过滤非法 ARP 报文。不过大多数中小办公网络没那么严格平时把“静态 IP 台账”整理好避免随手乱配就够用了。排查网络问题时有个小技巧先清理本地 ARP 缓存再测试。Windows 上用arp -d *Linux 上用ip neigh flush all。清完缓存后立刻连续 Ping观察 MAC 变化能更快抓到肇事者。3.2 连接超时与“模型请求失败”类问题的共性规律“连接超时”这四个字听起来很具体实际上覆盖的范围特别大。像 SecoClient 这类安全终端接入时报连接超时我能想到的原因就有目标地址和端口不通、服务端进程没起来、账号策略锁定、本地到服务端的路由有问题、证书过期导致 TLS 握手失败。所以我的习惯是先做排除先用telnet ip port或者nc -vz ip port测端口通不通通了再看上层再在服务器上执行netstat -tulnp或ss -tulnp确认服务进程真的在监听接着检查本机的路由表看是不是流量根本没走到对端最后才看客户端软件本身的问题比如版本、证书、配置项。这个思路放到“模型请求失败”上也成立。调用外部模型服务报错第一反应不应该是怀疑模型本身而是看它返回的错误上下文。现在的客户端工具大多提供了“展开错误信息”这类交互点开之后能看到更详细的状态码和阶段信息是鉴权失败、配额超限、网络超时还是参数非法。我之前排查过一个案例调用方只看了第一行“请求失败”怎么试都不行后来展开完整信息发现是账户余额不足充完值立刻就好了。所以在处理任何“连接超时”“请求失败”类问题时我的原则是先看完整报错再测连通性最后才怀疑服务端代码。顺序一旦搞反就会陷入“重启、重试、再重启”的死循环。4. 开发环境与系统层的 Bug 记录4.1 装 Docker Desktop 的高频报错虚拟化、WSL2 与常见坑Windows 上装 Docker Desktop报错最常见的就是虚拟化和 WSL2 相关。装完之后一启动弹窗提示“Docker Desktop requires a newer WSL kernel version”或者“Please enable virtualization in BIOS”。先说前置条件。Windows 10/11 必须是 64 位系统BIOS 里要把虚拟化Intel VT-x / AMD-V打开Windows 功能里需要启用“适用于 Linux 的 Windows 子系统”和“虚拟机平台”。这两项启用之后必须重启重启完再跑一遍wsl --status确认默认版本是 2。很多人的报错都出在“默认版本还是 1”上面执行wsl --set-default-version 2就能解决。还有一类报错是“An unexpected error occurred”这种情况十有八九和系统里其他虚拟化软件冲突。比如机器上装过 VirtualBox、VMware 之类的或者 Hyper-V 和内核隔离状态不对都会让 Docker Desktop 起不来。处理办法是先检查 Windows 功能里的 Hyper-V 是否启用然后看“内核隔离”是否正常。如果把 Docker Desktop 从 WSL2 后端切到 Hyper-V 后端报错内容会不一样但本质差不多都是虚拟化层没准备好。另外一个高频报错是“Docker Desktop is unable to detect a Hypervisor”. 这种情况通常是因为 Windows 启动时进入了“内核隔离”被禁用或虚拟机监控程序没跑。检查systeminfo输出里最后一段明确写着“Hyper-V 要求”是否都是“是”。如果这里显示“虚拟机监控程序已启动”那就说明虚拟化层是好的问题大概率出在 Docker Desktop 的安装配置上。这时候我一般会先去“控制面板-程序-启用或关闭 Windows 功能”把“虚拟机平台”和“Hyper-V”勾选状态确认一遍再重装 Docker Desktop。4.2 Ubuntu 24.04 中文残留 Bug 与语言环境清理升级到 Ubuntu 24.04 之后有的朋友会发现系统菜单变成中英混杂的“阴阳脸”有些应用界面还残留着旧版本的中文翻译甚至个别对话框显示英文。这个严格意义上不算运行逻辑 Bug而是语言环境和缓存的兼容性问题。排查思路分三步。先看当前 locale执行locale看LANG是否已经是zh_CN.UTF-8如果不对检查/etc/default/locale里是不是写成了英文或者其他值。再看语言包有没有装全Ubuntu 桌面版需要在“语言支持”里确认“简体中文”被勾选命令行则检查language-pack-zh-hans是否安装。最后清理用户级缓存。升级后旧的 GNOME Shell 扩展、图标缓存、字体缓存还停留在老状态注销再登录往往能解决大部分问题。如果注销之后还有残留那就需要手动清缓存。执行sudo dpkg-reconfigure locales生成 zh_CN.UTF-8 编码然后sudo update-locale LANGzh_CN.UTF-8再删除~/.cache下的 GNOME 和 fontconfig 缓存重启系统。大部分情况下到这里就恢复正常了。如果还有个别应用显示英文那就是应用自身没附带中文语言包和系统无关。4.3 winsxs 目录问题Windows 组件存储的“怪病”Windows 的C:\Windows\WinSxS是一个让很多人又爱又恨的目录。网上最常见的教程是“删除 winsxs 释放 C 盘空间”但我必须说一句千万别手动删这个目录。WinSxS 是 Windows 组件存储Component Store里面的文件大量使用硬链接被系统引用手动删除会直接破坏系统更新和功能开关严重的会导致系统无法启动。WinSxS 目录看起来很大是因为它里存着多个版本的系统组件方便系统更新时回滚。但“看起来占空间”不等于“可以删”。微软自己的清理工具才能正确处理硬链接引用。如果 C 盘空间确实被 WinSxS 拖累正确做法是用 DISM。先执行DISM /Online /Cleanup-Image /AnalyzeComponentStore它会告诉你组件存储里有多少是可以真正清理的很多情况下你会发现“实际可回收”和“显示的大小”相差很大。之后执行DISM /Online /Cleanup-Image /StartComponentCleanup这个命令会清理过期版本的组件。清理完再配合系统的“磁盘清理”工具勾选“Windows 更新清理”空间能释放不少。WinSxS 还有一种问题是组件存储损坏症状是系统更新失败、新功能安装不上。这种时候跑DISM /Online /Cleanup-Image /RestoreHealth再用sfc /scannow修复系统文件。修复过程可能比较慢但比重装系统成本低得多。5. 资源与性能类问题的彻底排查5.1 电脑卡顿怎么彻底排查“电脑卡顿”是一个被说滥了但永远有人问的问题。我的排查思路不是看某一个指标而是按 CPU、内存、磁盘、网络四个方向逐层排除。Windows 上按 CtrlShiftEsc 打开任务管理器先按 CPU 占用排序再看内存占用重点看磁盘那一栏是不是经常顶着 100%。很多卡顿的重灾区其实在磁盘Windows Search 索引、Defender 实时扫描、磁盘碎片整理这三个服务同时跑起来机械硬盘直接变成“假死状态”。磁盘层面还要关注“活动时间”而不是只看“占用率”。如果活动时间一直 100%但读写速度很低很可能是磁盘本身有坏道或者 SATA 线松了。再用资源监视器看哪个进程在持续读盘。Linux 上排查卡顿我会执行top看整体负载vmstat 1看 r 和 b 列r 持续大于 CPU 核数说明 CPU 排队严重b 高说明磁盘 IO 排队。再用iostat -dxk 1看每块盘的%util和awaitawait 特别高的时候基本可以断定是磁盘拖后腿。内存不足的表现也有迷惑性。Windows 上任务管理器显示“内存占用 90%”不代表现在就卡要看“已提交”和“工作集”。如果已提交内存长期大于物理内存加页面文件的总和系统就是在硬撑着交换卡顿就来了。定位内存泄漏可以用 Process Explorer 看工作集和私有字节的差值持续增长的进程往往就是嫌疑对象。5.2 Codex 磁盘 Bug 与“空间莫名被吃”的问题定位现在很多开发工具和 AI 编程助手都会在本地写日志和缓存写多了就开始吃磁盘。比如 Codex CLI 这类工具如果日志没有轮转策略跑上几个月就能吃掉几个 GB。我遇到过一次开发机 C 盘空间掉得很快排查后确认是~/.codex和~/.npm这两个目录在疯长。这种“空间被悄悄吃光”的问题定位方法其实很固定。Linux 上用du -sh /* 2/dev/null逐层往下找看哪个目录最大然后du -sh ~/.cache/* | sort -hr | head -10锁到具体文件。还有一类隐蔽问题某个进程已经删除了日志文件但句柄还握着磁盘空间被占用了却找不到文件。这时候要用lsof | grep deleted或者lsof L1把持有已删除文件句柄的进程找出来重启进程或者让它释放句柄空间立刻回来。Windows 上排查空间被吃最常用的工具是 WizTree 这类按 MFT 直接读目录树的软件比资源管理器快得多一眼就能看出哪个辣鸡目录最占地方。处理手段无非三种清理缓存、配置日志轮转、把缓存目录改到别的分区。很多时候不用“根治”只要设置一个定期清理任务就能把问题控制在可接受范围内。这也是排查或者说治理的心得有些工具型 Bug管住它比修好它的性价比高得多。6. Linux 系统排查指令大全与工具箱6.1 我的收藏级排查命令清单排查 Linux 系统问题命令不在多在精。我把自己实际用过、真正解决问题的命令整理了一张表按场景分类如下排查方向常用命令典型使用场景系统整体负载uptime、top、htop看 1/5/15 分钟负载判断系统是否过载CPU 深入mpstat -P ALL 1、pidstat -u 1看每个核心的使用率定位哪个进程吃 CPU内存free -h、vmstat 1、cat /proc/meminfo看物理内存、交换分区、缓存的分配情况磁盘空间df -h、du -sh *、find / -size 1G找大文件、确认分区使用率磁盘 IOiostat -dxk 1、iotop查%util、await定位 IO 瓶颈网络连接ss -tulnp、netstat -ano、lsof -i:8080看端口监听、连接状态、谁占用端口网络连通性ping、mtr、traceroute、curl -v测延迟、丢包、链路质量抓包分析tcpdump、tshark定位协议层的异常比如 TCP 重传、RST日志排查journalctl -u service --since 1 hour ago、grep -E ERRORException内核信息dmesg -T看内核是否报 OOM、硬件错误、磁盘 IO 错误进程追踪strace -p PID看进程在调用什么系统调用、卡在哪性能采样perf top、perf record、pidstat做热点分析找函数级性能瓶颈这里我想特别强调lsof这个命令。项目里排查“端口被占用”“文件被谁打开”“进程为什么还占着磁盘”全靠它。比如lsof -i:8080能立刻告诉你 8080 端口被哪个进程占了lsof L1能找出那些“删了还在占用磁盘空间”的文件。这两条命令绝对值得刻进肌肉记忆。strace适合另一种场景程序好像没反应但不知道卡在哪。附加到进程上看它正在读什么文件、连什么网络、等什么锁很多“假死”问题一眼就能看出来。不过不要在生产环境长时间 strace会有性能影响观测一两秒拿到关键信息就够了。6.2 排查中的记录习惯与日志定位技巧命令再熟没有好的记录习惯排查效率依然上不去。我在排查 Bug 时养成了一个“时间线记录法”把从发现问题开始每做一步验证、看到什么现象、改变了什么配置全部按时间点记录下来。比如“10:02 重启了 nginx10:05 接口恢复10:12 再次超时”有了时间线才能看出规律也方便把这些记录原样贴给同事一起判断。日志定位也有技巧。journalctl --since和--until能按时间精确切出问题窗口比翻整个日志文件高效得多。排查关键词时用grep -E ERROR|Exception|Caused by一把梭先找到异常堆栈的起点再往上翻两三行通常就能看到上下文。tail -f适合实时盯日志但在高并发日志量很大的服务上不要长时间开着可以用tail -n 500先看文件尾部再按关键字过滤。最后一条不要一上来就重启服务。重启相当于把犯罪现场抹掉了。哪怕决定重启也要先把当前状态记录下来netstat -tulnp、top、free -h、df -h、dmesg | tail -50这五条命令把网络、进程、内存、磁盘、内核信息全抓到再重启也不迟。7. 常见问题速查与实战心得7.1 常见问题速查表把这些年常见的几类问题和对应解法整理成一张速查表排查的时候可以先对着表看一遍症状可能原因最快验证手段参考解法接口报 422前端提交的数据类型/格式与后端 DTO 校验不匹配打开 Network 面板查看请求 Payload逐字段对照接口文档修正类型和格式客户端连接超时端口不通、进程未监听、账号策略异常telnet ip port、ss -tulnp按网络层到应用层的顺序逐项排除网络“卡”且时断时续IP 冲突、ARP 表异常arp -a或ip neigh观察同一 IP 是否对应多个 MAC调整 IP 地址或做 DHCP 保留/MAC 绑定Docker Desktop 无法启动WSL2 未启用、内核过旧、虚拟化冲突wsl --status、systeminfowsl --update、检查 Hyper-V 和内核隔离Ubuntu 界面中英混杂language-pack 缺失、locale 未生效执行locale检查 LANG安装 zh-hans 语言包并update-localeC 盘空间不断减少日志无轮转、缓存增长、句柄未释放du逐层定位大目录、lsof L1清理缓存、配置轮转、重启持有句柄的进程构建脚本报 semantic analysis 错误构建脚本语法错误、依赖版本冲突查看堆栈首行定位脚本行修正 Kotlin DSL 脚本、对齐 Gradle 版本7.2 几条不写进文档的实战心得第一给 Bug 写日记比写报告有用。报告是给别人看的日记是给自己攒经验的。每次排查完整理成“现象/根因/解法”三条几个月之后你就会拥有一本“症状字典”遇到新问题直接翻以前记过的能省下大量重复排查时间。第二复现成功就等于修好了一半。很多难缠的 Bug 之所以难难就难在它不听话。一旦你能稳定复现就可以放心地做二分法、加日志、改代码验证效率是蒙着头猜的十倍。所以拿到问题别先嫌弃它诡异先想方设法把它“驯服”。第三别在半夜用排除法。遇到过线上告警有人从晚上十点开始一项项排查到凌晨两点越查越困越困越乱最后按错配置把服务彻底搞挂。正确的做法是先恢复业务比如切备份节点、回滚版本、临时降级把用户影响先降到最低等精神好了再慢慢查根因。排查这事儿冲动是魔鬼。第四工具要会用但不要神化。抓包工具很强大但不能替代对 HTTP 状态码、TCP 握手、系统和日志的基本理解。命令只是放大器真正的排查能力来自对“业务链路”的完整认知。知道一个请求从浏览器到前端代码、再到网关、再到后端服务、最后到数据库的完整路径很多问题根本不需要工具推理就能看出来。这几年排查 Bug我最大的感受是这项工作和破案很像证据链比灵光一现可靠得多。再难的问题只要一五一十地记录现场、复现路径、缩小范围、验证假设最后总能找到落脚点。修完一个难缠的问题之后花十分钟写进日记里下一次再碰到类似症状你会感谢当时的自己。