LabHub
学习 学习路径 课程

从日志里找原因

调查日志已经消失的那段时间

在 LabHub 中继续学习

目标

收到“昨天下午有几次失败”的问题反馈,但该时间段的日志 不存在。 使用日志之外的其他痕迹缩小时间范围,并进一步确保下次能够留下日志。

环境

/root/nolog 下工作。现场由你亲自创建——这是准备工作,不是任务。

mkdir -p /root/nolog/logs /root/nolog/etc && cd /root/nolog
python3 - <<'PY'
import sqlite3, random, datetime
random.seed(11)
base = datetime.datetime(2026, 9, 7, 12, 0, 0)
con = sqlite3.connect('app.db'); cur = con.cursor()
cur.execute("create table orders(id integer primary key, status text, created_at text)")
rows, oid = [], 1
for m in range(240):
    t = base + datetime.timedelta(minutes=m)
    fail = random.randint(18, 26) if 123 <= m <= 126 else (1 if random.random() < 0.15 else 0)
    for _ in range(random.randint(8, 14)):
        rows.append((oid, 'PAID', (t + datetime.timedelta(seconds=random.randint(0, 59))).isoformat())); oid += 1
    for _ in range(fail):
        rows.append((oid, 'FAILED', (t + datetime.timedelta(seconds=random.randint(0, 59))).isoformat())); oid += 1
cur.executemany("insert into orders values (?,?,?)", rows); con.commit(); con.close()
with open('logs/access.log', 'w', encoding='utf-8') as f:
    for m in range(240):
        if 123 <= m <= 127: continue
        t = base + datetime.timedelta(minutes=m)
        for i in range(random.randint(5, 9)):
            f.write('%s GET /api/pay 200\n' % (t + datetime.timedelta(seconds=i * 6)).isoformat())
with open('logs/error.log', 'w', encoding='utf-8') as f:
    for m in range(130, 240):
        if random.random() < 0.2:
            f.write('%s WARN slow query 1200ms\n' % (base + datetime.timedelta(minutes=m)).isoformat())
PY
printf 'pool_size=2\ntimeout=1\n' > etc/app.conf
printf 'log_level=WARN\n' > etc/other.conf
touch -t 202609071402 etc/app.conf
touch -t 202608311000 etc/other.conf
ls -l --time-style=+%Y-%m-%dT%H:%M etc/

生成的内容如下。

app.db            주문 2,000여 건 (status, created_at)
logs/access.log   접근 로그 — 사고 구간이 비어 있다
logs/error.log    오류 로그 — 보존 기간 때문에 뒷부분만 남았다
etc/app.conf      설정 파일
etc/other.conf    설정 파일

要创建的文件

minute.txt    사고가 시작된 분과 그 근거
gap.txt       비어 있는 구간과, 로그가 사라진 범위
traces.txt    로그가 아닌 흔적 세 가지 이상
cause.txt     의심 대상과 배제한 후보
watch.sh      지금 진행 중인 문제에 붙일 사후 계측기
next.md       다음 조사를 줄이는 네 가지
report.md     정리

步骤

  1. 创建现场。
  2. 数据就是日志。created_at 按分钟分组, 找出失败集中的分钟。还必须记录日常值,才能证明“集中”。
  3. 在访问日志中找出空白区间,并写明错误日志从什么时间开始保留。 只有知道消失的范围,才能确定调查范围。
  4. 收集至少三种日志之外的痕迹。除了文件修改时间, 还必须从启动、进程、软件包、证书中至少选择一种。
  5. 对照时间缩小候选范围。还要写明已排除的候选项及其依据
  6. watch.sh——定期记录时间和观测值。评分程序会直接运行它。
  7. next.md——具体写明日志语句、保留期限、关联 ID、指标这四项。
  8. 进行总结。

参考

第 2 步中要指出的分钟不止一个。失败集中区间持续了数分钟, 因此指出其中任何一分钟都可以——但必须同时写出该分钟的数量日常值

第 5 步中,时间吻合表示的是相关性,而不是因果关系。将这一区别 写入文档,可以避免日后回滚错误对象。

创建日志消失的现场

创建现场。

原样运行说明中的准备代码块。错误日志从事故区间开始缺失,是本练习的前提。

数据就是日志

数据就是日志。created_at 按分钟分组, 找出失败集中的分钟。还必须记录日常值,才能证明“集中”。

created_at 按分钟(substr(created_at,1,16))分组并统计 FAILED。数值突增的分钟就是事故时间。还要同时记录日常值,才能进行比较。

不存在本身也是证据

在访问日志中找出空白区间,并写明错误日志从什么时间开始保留。 只有知道消失的范围,才能确定调查范围。

按分钟统计访问日志,就能看到行数为 0 的区间。还要确认错误日志第一行是什么时间——只有知道消失到什么范围,才能确定调查范围。

收集日志之外的痕迹

收集至少三种日志之外的痕迹。除了文件修改时间, 还必须从启动、进程、软件包、证书中至少选择一种。

至少收集以下三种:文件修改时间(ls -l --time-style)、启动时间(uptime -s)、进程启动时间(ps -eo lstart)、软件包安装记录(/var/log/dpkg.log)。痕迹必须连同时间一起记录,才能进行对照。

对照时间缩小范围

对照时间缩小候选范围。还要写明已排除的候选项及其依据

找出事故开始时间之前刚刚发生变化的对象。还要写明已排除的候选项及其依据,并注明时间吻合只是相关性,而不是因果关系。

如果问题仍在发生,就追加事后测量

watch.sh——定期记录时间和观测值。评分程序会直接运行它。

在问题复现期间定期记录时间和观测值。即使粗糙,也胜过完全没有数据,并可能成为下一次会议的唯一依据。评分程序会直接运行该脚本。

下次要比这次更快

next.md——具体写明日志语句、保留期限、关联 ID、指标这四项。

需要写明日志语句、保留期限、关联 ID 和指标四项。必须具体到“将什么设为多少”才能执行——还要注明当前保留期限为 24 小时。

总结

进行总结。

写明事故时间、没有日志这一事实、怀疑对象和防止复发的措施。不要遗漏本次调查的核心:“不存在本身也是证据”。