traceback 要从下往上读
一句话总结
在Trace Bag中,最下面的行告诉了原因,而上面的一堆则告诉了它是如何到达那里的。
为什么需要这个?
第一次看到Traceback时,眼睛会往上看。因为人们被训练成从上到下阅读。但是Python Traceback的结构正好相反。
Traceback (most recent call last):
File "report.py", line 34, in <module> ← 가장 바깥 호출
sys.exit(main(*sys.argv[1:]))
File "report.py", line 26, in main ← 실제로 터진 자리
amount = int(parts[2])
ValueError: invalid literal for int() with base 10: 'abc' ← 무엇이 왜
最下面的行是例外类型和原因。就在上面是实际爆炸的代码行。上面是从那里到达的路线。
所以在实际工作中第一次看到的是三个。异常名称、异常信息中包含的实际值,以及直接上一个帧的文件和行号。这三个就能确定原因。ValueError: ... 'abc'意思是“应该是数字的位置里放入了abc”,那么下一个问题就只有一个——哪一行有那个'abc'。
怎么行动
这里有一个常见的错误。就是把输出保存为文件,但没有跟踪背包的情况。
python3 report.py > out.txt # 트레이스백이 안 담긴다
python3 report.py > out.txt 2>&1 # 담긴다
Traceback不是标准输出,而是标准错误。当客户公司听到“日志中没有任何内容”时,第一个要确认的就是这个。在排布夹的Cron设置中2>&1经常遇到这种情况,因为这个漏洞,几个月来失败的原因都没有留下任何痕迹。
修改本身一般都很简单。难的是接下来的事情。
在现场相遇的样子
在修复因为一个破损的行而死亡的脚本时,有两种方式。
第一个是让你跳过那个行。脚本会回到起点,今天的问题就结束了。
第二个是让人们在跳过的时候留下为什么跳过什么。如果下周发生同样的事情,打开一次日志文件,1分钟就能结束。
第二个是测量植入。这也是处理无法再现的问题的正工法。以再现为目标的话,花几天时间做不到就结束了,但如果把目标转移到下次观测上,即使失败也会留下一些东西。即使植入的测量不是这个错误,也会在下一个错误中使用。
还有一点需要注意。因时机和竞争而产生的问题是,插入测量行为本身会改变时机,掩盖症状。在这种情况下,需要选择不会干扰执行路径的观测。可以尝试调整已经留下的日志的视角,通过抽样减少负担,或者事后导出状态。
最后,需要确认修改后的代码是否在其他输入中也正确,然后就可以结束了。今天只针对数据进行的修改不是修改,而是偶然。
每种语言的阅读方向都不一样
Trace背包每个语言的顺序相反。如果不知道这个,就会看错线。
| 语言 | 顶部 | 底部 |
|---|---|---|
| Python | 最外层(入口点) | 出现错误的地方 |
| Java | 出现错误的位置 | 最外边 |
| Go (panic) | 出现错误的位置 | 最外边 |
| JavaScript | 出现错误的位置 | 最外面的 |
只反对Python,所以很困惑。Python是Traceback (most recent call last)说
很亲切地写着,那句话的意思就是“下面是最新的”。
一直追踪包裹的错误
框架通常掩盖原始例外。真正的原因在链条的末端。
Traceback (most recent call last):
...
psycopg.OperationalError: connection failed
The above exception was the direct cause of the following exception: ← __cause__
Traceback (most recent call last):
...
app.errors.StorageUnavailable: 저장소에 닿을 수 없습니다
Python是raise ... from e如果为“direct cause”,在处理异常时又出现异常的话
用“During handling of the above exception”进行区分。第二个通常是
错误处理代码本身的错误是这样的信号——试图处理原因又死了。
Java是Caused by:将后续连接起来,Go是errors.Unwrap按。
无论哪种方式,最里面的例外信息都是调查的起点。
用我的代码缩小的方法
框架框架几十行就滑眼了,只过滤出自己的代码。
# 스택에서 우리 패키지만
grep -E 'File "/app/' traceback.txt
# pytest — 우리 코드 프레임만 보여 준다
pytest --tb=short -p no:cacheprovider
而且最下面(以Python为标准)的我们的代码框架通常是真正的位置。 比起那个,里面是图书馆,图书馆弄错的情况很少。我们交的 价格错了。
不能再现的时候留下的东西
在运营中,我发现错误仅靠堆栈是不够的。在捕获异常的地方当时输入 一起留下。
except Exception:
log.exception("주문 처리 실패", extra={
"order_id": order.id,
"payload_hash": hashlib.sha256(raw).hexdigest()[:12], # 원문은 남기지 않는다
"trace_id": current_trace_id(),
})
raise
与其说是保留原文,不如说是留下哈希。即使不把个人信息放在日志里 可以知道是否再次输入了相同的输入。
下次实习要做的事情
从某一天开始,确保失败的配置脚本的跟踪包,确定异常类型和第一次失败行,按照规格修改后,种下记录跳过行的测量,最后确认是否完全归纳到不同的输入文件中。