面对完全陌生的线上应用,我靠这套“找日志“方法论,10 分钟摸清家底

面对完全陌生的线上应用,我靠这套“找日志“方法论,10 分钟摸清家底
为什么陌生应用排障这么让人崩溃我以前遇到一些项目发现他们遇到对自己的应用了解很少点开服务器一看进程名看不懂目录结构乱七八糟日志文件几十个不知道该看哪个。然后是乱。上来就 tail -f 各种日志grep 一堆关键字越查越焦虑恨不得把整个 /var/log 翻个底朝天。最后是放弃。折腾半天找不到根因开始到处问人“这个服务谁写的”“以前出过类似问题吗”“有文档吗”但你发现没这样做的结果往往是 —— 故障时间越拖越长最后要么草草重启了事治标不治本要么领导亲自下场盯着你查压力直接拉满。后来我才想明白一个道理排障不是靠经验碾压是靠套路。经验只能让你排查得快一点但真正让你在陌生系统面前不慌的是一套可以复用的方法论。今天我就把这套东西掏出来给你。排障的核心心法先摸家底再动手很多人一上来就埋头看日志这是大忌。你连这个服务是干嘛的、用什么语言写的、部署在哪儿、依赖了谁都不知道就去翻日志那不叫排障那叫算命。第一步永远是摸家底。怎么摸通过几个问题快速建立认知框架这个服务是谁部署的、用什么语言Java、Go、Python、Node 还是 PHP是单实例还是集群部署跑在物理机、虚拟机还是容器里了解这些不是为了装专家是为了知道接下来该用什么工具去查。比如 Java 应用你得会看 jstack、jmapGo 应用你得会看 pprofPython 应用你得知道常见的框架日志格式。举个最真实的例子有一次我接手一个 Python 写的定时任务服务告警说任务卡住了不跑。新人上去就 grep “error”翻了半小时日志一无所获。我过去一看先问了一句这个服务用的是什么调度框架答案是 Celery。然后我直接去看 Redis 里的任务队列Celery 默认用 Redis 做 broker发现任务确实堆在队列里没被消费。接着看 worker 进程日志发现是连接数据库超时。整个过程不到 5 分钟。你看不了解这个服务的家底你根本不知道该看哪里。所以面对陌生应用我一般会先做这几件事看部署方式systemd 管理看/etc/systemd/system/下的 unit 文件里面有启动命令、工作目录、环境变量。容器跑的看docker inspect或者kubectl describe deployment里面能拿到镜像、挂载、环境变量、命令行参数。进程直接拉的看ps aux | grep对应的进程重点看启动命令那一串。看进程信息ps -ef能告诉你进程 PID、启动时间、启动命令。pstree能看到这个进程有没有 fork 子进程子进程在干嘛。lsof -p PID是神器能看到这个进程打开了哪些文件、监听了哪些端口、连接了哪些远端 —— 这一条命令能告诉你 80% 的关键信息。看资源占用top或者htop看 CPU、内存占用有没有进程在疯狂吃资源。iostat、vmstat、netstat或者现在更推荐用ss -s能看网络连接状态、磁盘 IO。这几个工具一组合起来这个服务的大致画像就有了跑在哪儿、用什么语言、连了哪些外部服务、最近有没有重启过。找入口摸清请求从哪里进、数据往哪里去摸完家底下一步是搞清楚请求的流转路径。任何线上应用本质上都是一个数据流转系统流量从某个入口进来经过一系列处理可能调数据库、调缓存、调下游服务、调消息队列最后返回结果或者写出去。排障的本质就是找到哪一环断了。所以你得先找到入口。对于 HTTP 服务入口就是监听端口。看ss -tlnp或者netstat -tlnp找到进程监听的端口然后顺着端口找进程顺着进程找配置。拿到端口和 IP 之后自己写个简单的 curl 模拟请求看返回curl -v http://127.0.0.1:8080/health curl -v -X POST http://127.0.0.1:8080/api/xxx -d {key:value}curl -v非常关键它会显示完整的请求和响应头有时候问题就藏在 header 里比如跨域、Content-Type 不对、认证失败。对于定时任务入口就是调度器。看 crontab、看调度框架的配置Celery beat、Airflow、xxl-job 这些找到任务的执行命令和触发周期。然后手动触发一次任务如果框架支持的话看任务能不能正常跑起来。对于消费 MQ 的服务入口就是消息队列。看连接的是哪个 MQKafka、RabbitMQ、RocketMQ用什么 consumer group消费的是哪个 topic/queue。用对应的客户端工具kafka-console-consumer、rabbitmqctl 等看看队列里有没有积压的消息。找到入口之后下一步是追调用链。这个调用链可能是代码层面的你得会看代码至少能看懂流程也可能是通过网络层面去追踪。如果你公司接入了分布式追踪系统Jaeger、Zipkin、SkyWalking 这些那就太幸福了。直接去 Tracing 系统里搜这个服务的 traceID能看到完整的调用链路、每个环节的耗时、哪一步报错。如果没接那就要靠网络包分析了。tcpdump抓包是个办法但生产环境用要谨慎。更好的方式是看应用自己打印的日志找到请求的 traceID 或者 requestID然后 grep 这个 ID 把所有相关日志捞出来。grep traceIDabc123 /var/log/app/*.log这一招极其好用。哪怕你完全不懂这个应用的代码也能通过日志把整个请求的足迹串起来。找日志排障的主战场好重点来了 ——怎么找日志。这是我今天最想跟你唠的。很多新手找日志的方式是cd /var/log然后ls看到一个文件就tail -f看完没东西再换下一个。这种方式效率极低而且经常错过关键信息。找日志的套路是先定位范围再深入细节。第一步日志在哪里不同部署方式日志位置不一样systemd 管理的服务看 unit 文件里的StandardOutputjournal还是StandardErrorjournal如果是 journal 就要用journalctl -u 服务名来看如果重定向到了文件就在 unit 文件里找路径。容器跑的看docker logs container_id或者kubectl logs pod_name。如果用了 sidecar 日志收集那日志可能不在容器本地而是被收集到了 ELK、Loki 这些系统里。进程直接拉的找启动脚本一般在 /opt、/home、/srv 这些目录下脚本里通常会有日志路径的硬编码。或者直接lsof -p PID | grep log能看到进程当前打开的日志文件。还有一招特别管用ls -l /proc/PID/fd。这个命令会列出进程打开的所有文件描述符里面一眼就能看到日志文件在哪个路径。第二步日志格式是什么样的找到日志文件之后先别急着 grep。先花两分钟看几条日志了解它的格式。日志格式一般包含这些要素时间戳日志级别INFO、WARN、ERROR、FATAL线程名 / 协程名类名 / 函数名 / 代码行号业务相关字段订单号、用户ID、traceID 等日志内容为什么要先看格式因为你要知道这个应用用什么字段做唯一标识。找到了这个标识你就能精准捞出某一次请求的所有日志。举几个常见场景Java 应用Log4j/Logback一般会有%X{traceId}这种 MDC 字段。Go 应用zap/logrus通常会有request_id或者trace_id这种字段。Python 应用logging格式比较自由但一般也会有自定义的 request_id。Nginxaccess.log 和 error.log 是分开的两份access.log 里能看到 status、upstream_addr、request_time 这些关键信息。第三步按需捞日志格式搞清楚之后就可以精准捞日志了。几个高频命令你一定要熟# 实时跟踪某个服务的日志 tail -f /var/log/app/service.log # 看最近 1000 行日志比 tail 更灵活 tail -n 1000 /var/log/app/service.log | less # 按时间范围捞这个真救命 sed -n /2024-01-15 14:00:00/,/2024-01-15 14:30:00/p /var/log/app/service.log # 按关键字捞上下文-A 后几行 -B 前几行 -C 前后各几行 grep -A 20 -B 5 OutOfMemoryError /var/log/app/service.log # 多个关键字或关系 grep -E ERROR|Exception /var/log/app/service.log # 排除干扰信息 grep -v 健康检查 /var/log/app/service.log | grep ERROR # 按业务字段捞比如捞某个订单的所有日志 grep order_id123456 /var/log/app/service.log # 统计某个错误出现的次数 grep -c NullPointerException /var/log/app/service.log如果你公司有 ELK、Loki、Graylog 这种集中式日志平台那上面的命令可以换成 Kibana 里的 KQL 语法或者 LogQL思路完全一样。第四步注意日志的坑找日志不是一帆风顺的我踩过太多坑了给你提几个醒日志被截断或者丢失。有些应用没有正确处理日志的 rotation老日志被覆盖有些容器日志没配持久化容器一重启就全没了还有些应用为了性能把 ERROR 以上的日志直接吞了。多实例日志分散在不同的机器上。集群部署的服务每个实例的日志都在不同的服务器上。排查时要把所有实例的日志都捞出来对比因为问题可能只出在某一个实例上比如某个实例的 JVM 内存泄漏。日志时间和系统时间对不上。容器里时区没配、服务器时间漂移、跨时区部署这些都会导致日志时间错位。看日志前先date一下服务器时间确保时区一致。敏感信息被脱敏了。有些应用为了合规会把日志里的手机号、身份证号、银行卡号这些敏感字段脱敏成***。如果你排查的是业务问题这种脱敏反而会给你带来麻烦 —— 看不到完整数据。日志量太大机器卡死。一个高 QPS 的服务一天能产生几十 G 日志。直接cat整个文件可能会把服务器 IO 打爆。这种情况下要用less、head、tail这种流式工具或者直接到日志平台搜。真实案例我怎么用这套方法论搞定那次陌生故障讲理论讲了这么多给你讲个真实案例你感受下。场景某天上午 10 点业务反馈用户支付后状态一直不更新订单服务群里炸了。第一步看监控先看监控大盘Grafana发现退款回调服务的 HTTP 5xx 错误率从 0.1% 飙升到 8%但 CPU、内存、网络都没异常机器没崩。第二步摸家底服务器上ps -ef | grep refund发现这个服务是用 Java 写的跑在 K8s 上2 个副本。kubectl describe pod refund-callback-xxx看到镜像名是refund-callback:v3.2.1启动命令里带了-Xmx2g有个环境变量PAYMENT_GATEWAY_URLhttps://pay.xxx.com。第三步找入口kubectl port-forward转发端口本地curl -v测了一下能通但返回 500。看到返回内容是upstream timeout。第四步看日志kubectl logs refund-callback-xxx --tail200看到大量这样的日志[ERROR] 2024-01-15 10:05:23 [http-nio-8080-exec-12] c.b.r.c.PaymentClient - 调用支付通道失败: SSLHandshakeException: PKIX path building failed第五步定位根因SSLHandshakeException、PKIX path building failed看到这两个关键字我就知道是证书问题。接着看日志里具体的异常信息unable to find valid certification path to requested target这是典型的 Java SSL 证书信任问题。继续往下翻日志发现第一次出现这个错误的时间是 09:58跟监控大盘上错误率飙升的时间点完全吻合。第六步验证猜想openssl s_client -connect pay.xxx.com:443 -showcerts手动去拉支付通道的证书发现证书链里缺了中间证书只有叶子证书没有 CA 证书。这十有八九是支付通道那边运维换证书的时候配置出问题了。第七步临时止血 根治临时方案在 Java 应用的信任库里手动补上缺失的中间证书重启服务恢复正常。根治方案联系支付通道的运维让他们补全证书链。整个过程10 分钟搞定。复盘一下我做了什么监控发现异常现象摸清服务家底语言、部署方式、配置找到入口并模拟请求精准定位日志从日志中提取关键异常信息用工具验证猜想临时止血 根治这就是方法论的力量。你不用懂这个服务的代码不用认识写这个服务的人不用看过任何文档照样能定位到根因。排障前的救命清单建议你收藏我再给你总结一份排障前的 checklist下次遇到陌生应用按这个清单一步步走摸家底阶段服务部署在物理机/虚拟机/容器什么语言写的什么框架进程名是什么PID 是多少启动命令和配置文件在哪监听了哪些端口连接了哪些外部服务找入口阶段HTTP 服务监听端口 域名定时任务调度器配置 执行周期MQ 消费topic/queue consumer groupRPC 服务注册中心 服务名看日志阶段日志文件路径在哪日志格式是什么用什么字段做唯一标识有没有集中式日志平台ERROR 级别的日志说了什么异常堆栈的第一行最关键的异常出现的时间点异常和监控告警的时间点是否吻合深入排查阶段是资源问题CPU/内存/磁盘/网络是依赖问题下游服务/数据库/缓存/消息队列是配置问题环境变量/配置文件/证书是代码问题看异常堆栈和代码行号是数据问题脏数据/并发竞争/边界值复盘阶段根因是什么为什么之前没发现监控/告警是否合理如何避免下次再出工具和命令速查表最后再给你列一份我常用的工具清单建议保存系统层面ps、top、htop、iostat、vmstat、ss、netstat、lsof、strace、tcpdump进程层面Javajps、jstack、jmap、jstat、arthas强烈推荐线上排障神器Gopprof、go tool tracePythonpy-spy、pyflame日志层面tail、grep、less、awk、sed、journalctl网络层面curl、telnet、nc、dig、nslookup、openssl s_client、mtrK8s 层面kubectl get/describe/logs/exec、kubectl port-forward集中式日志ELKElasticsearch Logstash KibanaLoki GrafanaGraylog阿里云/腾讯云的日志服务SLS/CLSAPM 和 TracingSkyWalkingJaegerZipkin阿里云 ARMS、腾讯云 CAT写在最后说到底陌生应用排障这件事拼的不是你会不会写代码也不是你经验有多丰富。拼的是你面对未知时的拆解能力和套路储备。把一个陌生的系统一层层剥开来看 —— 它怎么部署的、入口在哪、数据怎么流、依赖了谁、日志在哪 —— 这些问题回答完了根因就呼之欲出了。这套方法论我用了好几年不管是接手新业务、临时救场、还是面试的时候被问到你排查过最难的问题是什么我都能很自信地说出整个流程。因为我知道故障是排不完的但方法论可以复用一辈子。希望今天这篇能帮到你。如果你身边有刚入行的运维兄弟把这篇文章转给他能少走很多弯路。