TT Lab
はじめる
学ぶ 学習パス コース

ログから原因を見つける

障害の3分を見ようとして一日分を丸ごと読んだ

TT Labで続きを見る

目標

ローテーションまで済んだ6時間分のログから、事故の区間の3分だけを取り出します。重ならないファイルは開かず、残ったファイルでは、二分探索でバイト区間を特定して、その間だけを読み、圧縮ファイルは先頭から走査して、早めに止まります。

なぜ重要なのか

「3分だけ見せてください」という依頼に、1日分をまるごと読むことが、ログ作業で最もよくある事故です。ところが、時刻がRFC 3339の表記に固定されていて、ファイルが時間順なら、文字列の比較がそのまま時間の比較なので、ファイルを読まずに、二分探索で区間の開始位置を特定できます。難しいのは、探索ではなく、境界です。区間を半開区間で取って初めて、続く区間が重ならず、同じ時刻の行が複数あるときに、その時刻の最初の行を見つけて初めて、前の数行が黙って抜けません。そして、速いという言葉だけでは何も証明されないので、まるごと走査する遅い方法と、一度は突き合わせる必要があります。

ステップ

  1. /root/slice/gen_slice.pyを作成して実行し、/root/slice/var/log/の下にapp.log・app.log.1・app.log.2.gzを作ってください。
  2. /root/slice/index.json: ローテーションされたファイルを古いものから並べ、ファイルごとに含まれる区間を書いてください。
  3. /root/slice/plan.json: 事故の区間と重なるファイルだけを選び、残りは理由とともに飛ばしてください。
  4. /root/slice/offsets.json: 二分探索で、区間の開始バイトと終了バイトを特定してください。
  5. /root/slice/window_a.log: 特定したバイト区間だけを読んで、事故の区間を取り出してください。
  6. /root/slice/proof.json: 遅い方法と突き合わせて、結果が同じであることを証明してください。
  7. /root/slice/window_b.logと/root/slice/gz_scan.json: 圧縮ファイルの中の2つ目の区間を、早めに止まりながら取り出してください。
  8. /root/slice/slice_report.md: 方法と根拠を報告書として残してください。

参考

ローテーションまで済んだログの束を再現する

/root/slice/gen_slice.pyを作成して実行し、/root/slice/var/logディレクトリの下に、app.log(57600行)・app.log.1(57600行)・app.log.2.gz(展開すると57600行)を作ってください。

3つのファイルは、同じプログラムが書いた同じ形式で、ローテーションで分割されただけです。毎秒8行で2時間分ずつ入れ、30秒ごとに、同じミリ秒に3行が重なるようにしてください。圧縮ファイルは、Pythonのgzip.GzipFileにmtime=0を与えて書けば、作り直しても同じファイルになります。

ローテーションされたファイルを時間順に並べ、含まれる区間を測る

/root/slice/index.jsonに、files(古いものから入れた配列で、項目ごとにfile・compressed・bytes・first_ts・last_ts)とrule(そのように並べたルールを1文で、20文字以上)を書いてください。時刻は、行の先頭24文字をそのまま使います。

番号が付いたローテーション済みファイルは、数字が大きいほど過去で、拡張子がないものが今書いているファイルです。logrotateのdateextを使うと、名前が日付になって方向が逆になるので、ルールを書くときに一緒に指摘しておいてください。圧縮されていないファイルの最後の行は、ファイルの末尾から数KBだけ読めば取り出せます。まるごと読まないでください。材料は、/root/slice/var/log/の下の3つのファイルです。

重ならないファイルはそもそも開かない

/root/slice/plan.jsonに、window(startは2026-04-12T03:58:30.000Z、endは2026-04-12T04:01:30.000Z)と、open(区間と重なるファイルを古いものから入れた配列)、skip(残りのファイルをfile・reasonで入れた配列)を書いてください。

ステップ2で測ったfirst_tsとlast_tsがあれば、ファイルを開かなくても、重なるかどうかがわかります。区間が半開区間なので、重なりの判定も半開です。ファイルの開始が区間の終了より前で、ファイルの終了が区間の開始より前でなければ、重なります。材料は、/root/slice/index.jsonです。reasonは、10文字以上で書いてください。

二分探索で区間の開始と終了をバイトで特定する

/root/slice/offsets.jsonに、offsets(ステップ3で選んだ、圧縮されていないファイルごとに、file・start_offset・end_offsetを入れた配列で、古いものから)を書いてください。start_offsetは、時刻が区間の開始以上である最初の行の開始バイトで、end_offsetは、区間の終了以上である最初の行の開始バイトです(なければファイルサイズ)。

ファイルサイズを半分に折ってそのバイトにseekすると、ほぼ常に行の真ん中です。1行を読み捨てて行の境界に立ってから、その行の先頭24文字で判定してください。同じ時刻の行が3つあるので、「条件を満たすどれかの行」ではなく、「条件を最初に満たす行」を見つける必要があります。条件を満たしたら、範囲の右端を、その行の開始に引き寄せてください。材料は、/root/slice/plan.jsonです。

特定したバイト区間だけを読んで事故の区間を取り出す

/root/slice/window_a.logに、事故の区間[2026-04-12T03:58:30.000Z, 2026-04-12T04:01:30.000Z)の行を、時間順に、原文のまま書いてください。ファイルは、古いものから順につなぎます。

ステップ4で特定した2つのオフセットの間を、そのまま読み出せばよいです。end_offsetが区間の外の最初の行の開始なので、その直前までが、ちょうど半開区間です。行を再パースしたり、整形したりしないでください。原文のバイトをそのまま移す必要があり、そうしなければ、次のステップのハッシュの突き合わせが成立しません。材料は、/root/slice/offsets.jsonです。

遅い方法と突き合わせて結果を証明する

/root/slice/proof.jsonに、window・fast_lines・slow_lines・sha256_fast・sha256_slow・match(ブール値)・bytes_read_fast・bytes_read_slowを書いてください。bytes_read_fastは、ステップ4の2つのオフセットの間のバイトの合計で、bytes_read_slowは、圧縮ファイルまで展開して、3つのファイルをまるごと読んだときのバイトです。

遅い方法は、3つのファイルをすべて(圧縮ファイルは展開して)走査しながら、区間の行を集めることです。時間は測らないでください。マシンごとにぶれます。その代わり、行数とsha256ハッシュを比べます。ハッシュは、取り出した行をつなげたバイトに対して計算し、2つの方法の値が同じなら、matchが真になります。材料は、/root/slice/window_a.logと/root/slice/offsets.jsonです。

圧縮ファイルでは、早めに止まることが答え

2つ目の質問は、[2026-04-12T00:20:00.000Z, 2026-04-12T00:23:00.000Z)です。/root/slice/window_b.logに、その区間の行を原文のまま書き、/root/slice/gz_scan.jsonに、window・file・total_lines・lines_read・lines_keptを書いてください。lines_readは、止まるまでに実際に展開した行数です。

この区間は、圧縮ファイルの中にあります。gzipストリームは、前の内容に頼って展開されるので、任意の地点へ飛べず、二分探索をかけると、同じ場所を何度も展開し直すことになります。先頭から1回だけ走査し、区間の終わりを過ぎる行に出会ったら、その場で抜け出してください。total_linesは、どれだけ節約したかを書くための値なので、一度は最後まで数える必要があります。材料は、/root/slice/var/log/app.log.2.gzです。

方法と根拠をドキュメントとして残す

/root/slice/slice_report.mdに、## 무엇을 물었나、## 왜 통째로 읽지 않아도 되었나、## 압축본은 무엇이 달랐나、## 결과를 어떻게 증명했나(韓国語の見出しは、順に「何を尋ねられたか」「なぜまるごと読まなくてよかったのか」「圧縮ファイルは何が違ったか」「結果をどう証明したか」という意味です)の4つの節で書いてください。事故の区間の行数・速い方法が読んだバイト・圧縮ファイルで展開した行数を、数字で含める必要があります。

このドキュメントを読む人は、次に同じ質問を受ける人です。その人が知るべきことは、コマンドではなく前提です。なぜ二分探索が成立したのか、どのファイルをなぜ開かなかったのか、圧縮ファイルで何が違ったのか、結果を何と突き合わせたのか。数字はでっち上げず、/root/slice/proof.jsonと/root/slice/gz_scan.jsonから取り出して使ってください。