第五章 打印日志的艺术
打印是最简单但最强大的调试手段,但要打印得有智慧。
5.1 结构化日志
不要只打印字符串,使用结构化格式(JSON)方便后续分析。
import json
import time
def log(level, message, **kwargs):
record = {
"timestamp": time.time(),
"level": level,
"message": message,
**kwargs
}
print(json.dumps(record))
log("INFO", "User login", user_id=123, ip="192.168.1.1")
5.2 带上下文的调试 ID
在多线程或 Web 环境中,为每个请求生成唯一 ID,贯穿所有日志。
import contextvars
import uuid
request_id = contextvars.ContextVar('request_id')
def set_request_id():
rid = str(uuid.uuid4())
request_id.set(rid)
return rid
def debug(msg):
rid = request_id.get(None)
print(f"[{rid}] {msg}")
5.3 在循环中使用进度打印
避免打印每次迭代,而是每隔 N 次打印一次。
for i, item in enumerate(large_list):
if i % 1000 == 0:
print(f"Processed {i} items")
process(item)
第六章 断言与防御式编程
断言用于捕捉“不应该发生”的情况。
断言的最佳实践:
在函数入口检查参数合法性(防御性编程)。
在复杂算法的不变量处断言。
不要断言可能因外部输入而失败的条件(如文件是否存在),那是业务逻辑。
def complex_calculation(data):
assert len(data) > 0, "data cannot be empty"
# ...
# 循环后断言结果范围
assert 0 <= result <= 100, f"Result out of range: {result}"
return result
在生产环境,可以使用 -O 标志禁用断言,但建议在关键服务中保留(通过配置开关)。
第七章 IDE 高级调试技巧
7.1 条件断点
当循环次数很大时,只希望在特定条件下暂停。
Eclipse/IntelliJ: 右键断点 → 输入条件,如 i == 500 && list.get(i).equals("error")。
VS Code: 在断点处右键 → 编辑断点 → 表达式。
7.2 日志断点(不会停止程序的断点)
IntelliJ: 右键断点 → 取消 "Suspend",勾选 "Log evaluated expression"。
VS Code: 创建断点后,选择 "Log Message"。
7.3 字段/变量断点
在 Java/C# 中,可以对字段设置断点,当字段值被修改时暂停(即使不是直接赋值,而是通过反射)。
7.4 异常断点
在 IDE 中设置异常断点,任何抛出该类型异常的地方都会暂停(包括 catch 块之前)。这对排查被吞掉的异常极其有用。
7.5 逆向调试(Reverse Debugging)
某些高级调试器(如 GDB 7.0+、RR、UndoDB)支持反向执行,可以回到之前的某个指令重新观察。
RR (Mozilla) 示例: 录制程序运行,然后可以前后自由移动。
rr record ./my_program
rr replay
第八章 专业工具专题
8.1 内存调试三剑客
Valgrind (Linux): valgrind --leak-check=full ./my_program
AddressSanitizer (Clang/GCC): 编译时加 -fsanitize=address -g
Dr. Memory (Windows)
8.2 性能 Profiler
perf (Linux 内核级): perf record -g ./my_program → perf report
py-spy (Python, 无需改代码): py-spy record -o profile.svg --pid 1234
JProfiler (Java, 商业)
Chrome DevTools (前端)
8.3 网络调试
Wireshark + tshark: 抓包分析 HTTP、TCP 细节。
mitmproxy: 命令行中间人代理,可查看和修改请求。
Charles Proxy (GUI, 跨平台)
8.4 系统调用追踪
strace (Linux): strace -f -e trace=file,network ./my_program 追踪文件操作和网络调用。
dtruss (macOS)
Process Monitor (Windows)
第九章 远程调试与生产环境问题排查
生产环境通常无法安装开发工具或插入断点。可行方法:
增强日志:动态调整日志级别(如通过 admin 接口将某个类的日志级别改为 DEBUG)。
JMX(Java):使用 JConsole 连接到运行中的 JVM 查看线程、内存。
Arthas(阿里开源 Java 诊断工具):可在线查看方法调用参数、返回值、异常,甚至修改字节码。
btrace(动态跟踪 Java 程序)。
Python 的 py-spy 可以安全地附加到生产进程,不产生性能影响。
核心转储(core dump):程序崩溃后,保留内存快照,离线分析。
ulimit -c unlimited # 允许生成 core dump
./my_program # 崩溃后生成 core
gdb ./my_program core # 离线分析