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

Loki — ログを索引しないログストア

エラーは一つもないのに 5xx の件数が実際の三分の一だった

TT Labで続きを見る

目標

Pod内の本物のLokiで4種類のLogQLパーサーを自分で付けてみて、パースエラーが出た行と、エラーなしで静かに値を取り出せなかった行をそれぞれ数え、本当の5xx件数を求めます。

なぜ重要なのか

Lokiは本文をインデックス化しません。そのため、本文の中の値で絞り込むには、クエリのたびにパーサーで取り出す必要があり、パーサーの選択を誤ると、数字が静かに減ります。jsonは失敗を__error__ラベルで知らせますが、logfmtは知らせません。形式が異なる行に出会うと、何のラベルも作らずに通り過ぎ、ラベルがなければ、後に続くラベルフィルターはその行を静かに捨てます。ダッシュボードにはエラーも警告も出ません。そのため、新しいストリームをクエリする前に、常に2つの数字を先に測る必要があります。パースが失敗した行数と、失敗もしていないのに値が出なかった行数です。

ステップ

  1. /root/lk-parsersでLokiを起動し、date +%sを/root/lk-parsers/anchor.txtに書いたあと、python3 /opt/lab/d5/gen.py parsers "$(cat anchor.txt)"でデータを入れてください。そして、基準時刻から過去1時間の間に、{app="mixed"}と{app="orders"}がそれぞれ何行あるかを数えて、/root/lk-parsers/01-boot.txtにmixed=<정수>とorders=<정수>の2行で書いてください(プレースホルダーは整数です)。
  2. ordersストリームでstatusが500の行が何行あるかを、logfmtパーサーで数えてください。クエリは/root/lk-parsers/02-logfmt.logql、答えは/root/lk-parsers/02-logfmt.txtにlines=<정수>で書きます(プレースホルダーは整数です)。
  3. mixedストリームにjsonパーサーを付けて、パースに成功した行と、__error__ラベルが付いた行をそれぞれ数えてください。答えは/root/lk-parsers/03-json.txtにok=<정수>とerr=<정수>の2行で書き、__error__の実際の値を1つ、/root/lk-parsers/03-json-name.txtに1行で書いてください(プレースホルダーは整数です)。
  4. mixedストリームにlogfmtパーサーを付けたとき、エラーは出なかったのにstatusラベルが作られなかった行が何行あるかを数えてください。クエリは/root/lk-parsers/04-silent.logql、答えは/root/lk-parsers/04-silent.txtにlines=<정수>で書きます(プレースホルダーは整数です)。
  5. 本文にlegacyが入っている行だけを選んで、patternパーサーでステータスコードを取り出し、その値が500の行を数えてください。クエリは/root/lk-parsers/05-pattern.logql、答えは/root/lk-parsers/05-pattern.txtにlines=<정수>で書きます(プレースホルダーは整数です)。クエリにはpatternパーサーが必ず入っている必要があります。
  6. 同じレガシーの行から、regexpパーサーで所要時間(ミリ秒)を取り出し、1000より大きい行を数えてください。クエリは/root/lk-parsers/06-regexp.logql、答えは/root/lk-parsers/06-regexp.txtにlines=<정수>で書きます(プレースホルダーは整数です)。クエリにはregexpパーサーが必ず入っている必要があります。
  7. mixedストリームの行を3つの形式に分けて数え、/root/lk-parsers/tally.tsvを作成してください。ヘッダーなしで3行で、各行はタブで区切った2つの欄<형식><탭><줄수>(プレースホルダーは形式、タブ、行数です)です。形式名は順にjson、legacy、logfmtで、3行の合計は、ステップ1で数えたmixedの全体の行数と同じである必要があります。
  8. mixedストリームで、ステータスコードが500の行の本当の合計件数を求めて、/root/lk-parsers/08-total.txtにtotal=<정수>で書き(プレースホルダーは整数です)、その次の行に、reason=で始まる1文(空白を除いて40文字以上)を書いてください。なぜパーサー1つではこの数字を数えられないのかを、自分の言葉で書きます。

参考

形式が混ざったストリームを手元に用意する

/root/lk-parsersでLokiを起動し、date +%sを/root/lk-parsers/anchor.txtに書いたあと、python3 /opt/lab/d5/gen.py parsers "$(cat anchor.txt)"でデータを入れてください。そして、基準時刻から過去1時間の間に、{app="mixed"}と{app="orders"}がそれぞれ何行あるかを数えて、/root/lk-parsers/01-boot.txtにmixed=<정수>とorders=<정수>の2行で書いてください(プレースホルダーは整数です)。

/readyがreadyを返すまで20秒ほどかかります。区間はanchor.txtの値でstart・endをナノ秒で指定してください。パーサーなしで、セレクターだけで数えれば済みます。

形式がきれいなときは、logfmt1行で済む

ordersストリームでstatusが500の行が何行あるかを、logfmtパーサーで数えてください。クエリは/root/lk-parsers/02-logfmt.logql、答えは/root/lk-parsers/02-logfmt.txtにlines=<정수>で書きます(プレースホルダーは整数です)。

パーサーはセレクターの後ろにパイプで付けます。パーサーが作ったラベルは、その後ろでラベルフィルターとして使えます。行フィルターとして|= "status=500"のように文字列を探すのとは違います。今回は、パーサーで取り出した値を使ってください。

jsonパーサーは失敗を知らせる

mixedストリームにjsonパーサーを付けて、パースに成功した行と、__error__ラベルが付いた行をそれぞれ数えてください。答えは/root/lk-parsers/03-json.txtにok=<정수>とerr=<정수>の2行で書き、__error__の実際の値を1つ、/root/lk-parsers/03-json-name.txtに1行で書いてください(プレースホルダーは整数です)。

パーサーが失敗すると、__error__ラベルが付きます。そのラベルが空の行と、空でない行を、それぞれ選べば済みます。ラベルの値が何かは、結果のstreamをそのまま見ると見えます。__error_details__も一緒に付きます。

エラーもなく何も取り出せなかった行を数える

mixedストリームにlogfmtパーサーを付けたとき、エラーは出なかったのにstatusラベルが作られなかった行が何行あるかを数えてください。クエリは/root/lk-parsers/04-silent.logql、答えは/root/lk-parsers/04-silent.txtにlines=<정수>で書きます(プレースホルダーは整数です)。

LogQLでは、「存在しないラベル」は、空文字列と同じように比較されます。その性質を使えば、「パーサーが値を作れなかった行」を選べます。この数字が、このラボで最も恐ろしい数字です。ダッシュボードのどこにも出ないのに、合計を削っていくからです。

位置が固定された行にはpatternが合う

本文にlegacyが入っている行だけを選んで、patternパーサーでステータスコードを取り出し、その値が500の行を数えてください。クエリは/root/lk-parsers/05-pattern.logql、答えは/root/lk-parsers/05-pattern.txtにlines=<정수>で書きます(プレースホルダーは整数です)。クエリにはpatternパーサーが必ず入っている必要があります。

レガシーの行は、legacy handler finished code 500 in 123msの形です。patternは、取り出す位置に山括弧で名前を書き、残りは文字どおりに書きます。パーサーの前に行フィルターを置いて、レガシーの行だけを渡すことが重要です。ほかの形式の行には、この形がありません。

正規表現パーサーで数値を取り出して比較する

同じレガシーの行から、regexpパーサーで所要時間(ミリ秒)を取り出し、1000より大きい行を数えてください。クエリは/root/lk-parsers/06-regexp.logql、答えは/root/lk-parsers/06-regexp.txtにlines=<정수>で書きます(プレースホルダーは整数です)。クエリにはregexpパーサーが必ず入っている必要があります。

regexpは名前付きキャプチャグループだけをラベルにします。名前のない括弧は捨てられます。取り出した値は文字列ですが、不等号で比較すると、LogQLが数値に変換してくれます。同じ答えをpatternでも出せます。どちらが読みやすいか、比べてみてください。

応用①: 形式別に何行かを表にする

mixedストリームの行を3つの形式に分けて数え、/root/lk-parsers/tally.tsvを作成してください。ヘッダーなしで3行で、各行はタブで区切った2つの欄<형식><탭><줄수>(プレースホルダーは形式、タブ、行数です)です。形式名は順にjson、legacy、logfmtで、3行の合計は、ステップ1で数えたmixedの全体の行数と同じである必要があります。

JSONの行は、jsonパーサーがエラーを出さない行です。レガシーの行は、本文にlegacyがあります。残りがlogfmtの行で、これらの行は、logfmtでstatusが作られます。3つの数字の合計が全体と合っているかを、必ず確認してください。合っていなければ、どこかを2回数えたか、数え漏らしています。

応用②: 本当の5xx件数と、その事実を残す

mixedストリームで、ステータスコードが500の行の本当の合計件数を求めて、/root/lk-parsers/08-total.txtにtotal=<정수>で書き(プレースホルダーは整数です)、その次の行に、reason=で始まる1文(空白を除いて40文字以上)を書いてください。なぜパーサー1つではこの数字を数えられないのかを、自分の言葉で書きます。

1つのクエリにパーサーを2つつなげても、2つの形式を一緒に読めるわけではありません。形式ごとに別々に数えて足してください。ステップ4で数えた「静かに抜けた行」がどこへ行ったかを考えれば、何方向に分ける必要があるかが見えます。答えを書くときは、3方向の数字がそれぞれいくつだったかも確認しておいてください。