跳到内容

元数据卡

  • 前置知识:第6章·读懂栈调用、第6章·日志基础、第6章·最小复现
  • 预计时间:20 分钟
  • 完成标志:完成热身和排障练习

写在前面

理论学完了。但你不会因为"看过"就掌握 Debug——它跟编程一样,是练出来的。下面几个练习,按难度排列。

热身(5 分钟,必做)

故意写一段会报错的代码,观察栈调用的每一行。

java
// 文件: CrashMe.java
// 运行: javac CrashMe.java && java CrashMe
public class CrashMe {
    public static void main(String[] args) {
        int[] arr = new int[3];
        System.out.println(arr[5]); // 数组越界
    }
}

运行后你会看到类似这样的输出:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:
  Index 5 out of bounds for length 3
  at CrashMe.main(CrashMe.java:4)

练习步骤:

  1. 把索引改成 arr[10] 再跑,观察行号变化
  2. 把错误换成 null 对象调用(String s = null; s.length();)
  3. 换成 Python 跑一遍同样的场景,对比两种格式

关键观察:行号永远指向出问题的代码行——你改了哪一行,报错就指向哪一行。

挑战(15 分钟,选做)

在一个已有项目里加上日志库,然后故意制造错误,从日志中找到它。

步骤:

  1. 随便找一个你之前写的项目
  2. 给启动入口、关键函数入口、异常 catch 三处加日志
  3. 故意制造一个 bug(比如把配置文件名改错、把数据库密码改错)
  4. 运行,看日志文件里有没有记录这个错误
  5. grep ERROR app.log 过滤出你的错误

排障案例 1:NoClassDefFoundError

你启动一个 Java 服务,报错:

Exception in thread "main" java.lang.NoClassDefFoundError:
  com/fasterxml/jackson/core/JsonFactory

你明明在 pom.xml 里写了 Jackson 依赖,为什么还会找不到?

思路排查:

排查点方法
依赖类型是不是用了 <scope>provided</scope>
构建方式mvn compile 不会打包依赖,要用 mvn package
依赖树mvn dependency:tree 查看 Jackson-core 是否传递进来了
打包方式是不是打了 fat jar(shade/assembly)?

你用的是 mvn compile 然后直接跑?还是 mvn package 后跑 fat jar?前者不会把 Jackson 的 jar 拷过来。

排障案例 2:日志路径找不到

你部署了应用,用户说报错了。你登上去看日志——没有日志文件

查了半天发现:日志配置写的 ./logs/app.log 在 Docker 里写到了 /app/current/logs/app.log,而你一直在看 /var/log/app.log

bash
# 先确认程序在哪个目录运行
ps aux | grep myapp

# 在对应目录下找日志
cd /usr/local/myapp/
find . -name "*.log" -type f

# 如果日志太大,不要用 cat
tail -n 50 app.log   # 最后 50 行
less app.log         # 分页浏览

两个经典错误类型

  • NullPointerException / NoneType has no ...——几乎所有语言的 #1 错误:你用它的时候它不存在
  • ClassNotFoundException / ModuleNotFoundError——import 了一个东西,运行时找不到(依赖没装对)

旅人笔记

程序总会出错——但你会读栈调用、会搜错误信息、会加日志、会写最小复现。这些技能比任何一门编程语言都持久。每次 Debug 都是一次"缩小嫌疑范围"的过程。它不是天赋,是一套可以练习、优化的方法。

验收标准

本章完成后,你应该能:

  • 读懂任意语言的栈调用,找到 bug 位置和类型
  • 用关键词 + 错误信息进行有效搜索
  • 至少一种语言正确配置并写入日志文件
  • greptailless 查看和过滤日志
  • 写出最小复现隔离和报告 bug
  • 理解为什么不能用 System.out 替代日志

下一站预告

日志和报错你学会读了。但有些时候,程序不在你的机器上跑——它在 Docker 容器里。你需要一个方法,把你的项目打包起来到任何机器上都能跑。

→ 第7章:Docker(即将到来)

Built with VitePress | Software Systems Atlas