TT Lab
开始
学习 学习路径 课程

从日志里找原因

事故当天的日志已经被清掉了

在 TT Lab 中继续学习

目标

测出已轮转的日志中实际保留的区间,用数字查明事故区间在这个范围之外,实测压缩率,计算保留天数和容量,并写成轮转策略。最后统计因触碰速率限制而从一开始就没有被记录的行。

为什么重要

调查第一天最常碰到的墙,不是难题,而是空目录。这时需要的不是叹息,而是三个数字——少了几天、还应再多留多少、这个值是否符合磁盘预算。这三个数字全都可以从现有的文件中测出来。而且“日志没有”有两种含义:要么被轮转删除了,要么触碰速率限制而从一开始就没有被记录。两者的对策完全不同,所以必须区分。

步骤

  1. 创建并运行 /root/keep/gen_keep.py,在 /root/keep/var/log/ 下复现保留状态。
  2. 在 /root/keep/order.json 中写下把轮转文件从最旧开始排好的列表及其规则。
  3. 在 /root/keep/coverage.json 中写下每个文件的行数,以及首行和末行的时间。
  4. 在 /root/keep/window.json 中写下事故区间是否在保留范围之内,以及缺了多少。
  5. 在 /root/keep/budget.json 中写下压缩率的实测值,以及保留天数和容量的计算。
  6. 在 /root/keep/logrotate.conf 中写下轮转策略。rotate 的值必须与第 5 步的计算相同。
  7. 在 /root/keep/ratelimit.json 中统计并写下因触碰速率限制而会被丢弃的行数。
  8. 在 /root/keep/keep_report.md 中分四节留下报告。

参考

复现客户的保留状态

创建并运行 /root/keep/gen_keep.py,在 /root/keep/var/log/ 下生成 payments.log(720 行)、payments.log.1(2000 行)、payments.log.2.gz、payments.log.3.gz、old/payments.log-20260407.gz、old/payments.log-20260408.gz、burst.ndjson(360 行)。

做出轮转名称混用两种规则的状态。带序号的和带日期的,压缩的和未压缩的,必须同时存在,这个实验才能成立。压缩文件用 Python 的 gzip.GzipFile,并指定 mtime=0 来写入,重新生成时得到的也是同样的文件。

把轮转文件从最旧的开始排好

在 /root/keep/order.json 中写入 order(按从旧到新的顺序存放文件路径的数组,为去掉 /root/keep/var/log/ 之后的相对路径)和 rule(用一句话写出这样排序的规则)。

带序号的名称和带日期的名称,排序方向相反。一种是数字越大越旧,另一种是名称越小越旧。当前正在写入的文件最新。先不打开文件,只凭名称排一遍,下一步再用内容来确认。

测出每个文件实际包含的区间

在 /root/keep/coverage.json 中,把为每个文件存放 file、lines、first_ts、last_ts 的对象,按从旧到新的顺序写成数组。时间直接使用日志行的第一列。

文件名可能说谎——如果轮转失败,或混入了人手动移动的文件,名称顺序和内容顺序就会错位。所以要用内容重新测量。压缩文件用 zcat 读取,或在 Python 中用 gzip.open(path, 'rt') 打开。

用数字查明事故区间在范围之外

在 /root/keep/window.json 中写入 oldest_retained、newest_retained、incident_start、incident_end、incident_covered(真/假)、short_by_seconds(整数)、extra_rotations_needed(整数)。extra_rotations_needed 是把缺少的秒数除以一天后向上取整的值。

事故区间写在说明的参考一节中。缺少的秒数,是保留下来的最旧时间减去事故开始时间所得的值。因为是按天轮转的,所以应当多留几天,用向上取整来求。

实测压缩率,计算保留天数

在 /root/keep/budget.json 中,按说明中的计算规则写入 sample_raw_bytes_per_day、sample_gz_bytes_per_day、compression_ratio、prod_raw_bytes_per_day、prod_gz_bytes_per_day、required_bytes_for_30_days、fits_in_budget、max_days_in_budget、rotate_value。

压缩率不要靠估算,要用这位客户的文件来测量——访问日志和 stack trace 缩小的程度不同。由于 delaycompress,最近两天必须按原始大小算,而 logrotate 的 rotate 是不含当前文件的个数,这是这个计算的陷阱。

用计算出的值写出轮转策略

在 /root/keep/logrotate.conf 中写入 /root/keep/var/log/payments.log 块。必须包含 daily、rotate <5단계의 rotate_value>(占位符为第 5 步中的 rotate_value)、compress、delaycompress、missingok、notifempty、dateext、create 0640 root adm,并且不要加入 copytruncate。

rotate 的值,就是第 5 步中计算出的那个数字——它与保留天数并不是同一个值。去掉 copytruncate 的理由写在手册里:在复制与清空之间的短暂空隙中写入的行会消失,而且它消失在调查中最令人遗憾的时刻。

统计从一开始就没有被记录的行

在 /root/keep/ratelimit.json 中写入 interval_sec、burst、windows_over_limit、dropped_total、dropped_by_service(以服务名称为键的对象)、worst_window(window_start、service、dropped)。一个区间内被丢弃的行数是 count - burst,为负数时取 0。

journald 的速率限制按服务分别适用,在一个区间内超过限制,该区间内的剩余部分会全部被丢弃。无论把保留时间延长多久,这种损失都仍然存在。worst_window 是被丢弃的行最多的那一个区间。

把要改变的内容写成文字

在 /root/keep/keep_report.md 中分四节书写:## 무엇이 없었나、## 지금 보관 정책은 무엇인가、## 얼마를 남겨야 하는가、## 무엇을 바꾸기로 했나(韩文,依次意为“缺了什么”“当前的保留策略是什么”“要保留多少”“决定改变什么”)。必须以数字形式包含缺少的秒数、rotate 值、预算之内可能的天数、因速率限制而被丢弃的行数。

读这份报告的人,是掌握磁盘预算的人。不是说“请多保留些日志”,而是要说“每天 N 字节,30 天 M 字节,在预算之内”,才能作出决定。速率限制造成的损失需要与保留不同的对策,这一点也要一并写明。