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

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

エラーはゼロだと報告したが、その時間に三十件あった

TT Labで続きを見る

目標

パーサーが取り出した名前がストリームラベルとぶつかって押しのけられる現象を自分で再現し、label_format・line_format・keep・dropで、結果を目的の形に整理します。

なぜ重要なのか

Lokiでは、ラベルは2つの場所から来ます。収集時に付けてインデックス化されたストリームラベルと、クエリ時にパーサーが本文から取り出したラベルです。名前が重なると、あとから取り出したほうに_extractedが付いて押しのけられますが、警告もエラーもありません。そのため、level="error"が0件を返し、人はそれを「エラーがない」と読みます。クエリ時点の名前の整理は、結果にだけ作用してインデックスには手を触れないので、思う存分使ってもコストは増えません。カーディナリティが心配な値を本文に置いて、クエリのときに取り出して使う設計が成り立つ理由です。

ステップ

  1. /root/lk-formatでLokiを起動し、date +%sを/root/lk-format/anchor.txtに書いたあと、python3 /opt/lab/d5/gen.py format "$(cat anchor.txt)"でデータを入れてください。そして、{app="payment"}の1時間の行数と、そのストリームに付いているストリームラベルの名前を、/root/lk-format/01-boot.txtに2行で書いてください。lines=<정수>とlabels=<이름들을 쉼표로, 사전순>です(プレースホルダーは順に、整数と、名前をカンマでつないだ辞書順の並びです)。
  2. {app="payment"} | logfmt | level="error"を1時間の区間で投げて、結果が何行かを見て、名前が押しのけられたラベルでもう一度問い合わせ、何行かを数えてください。答えは/root/lk-format/02-collision.txtに3行で書きます。naive=<정수>、extracted_name=<밀려난 라벨 이름>、correct=<정수>です(プレースホルダーは順に、整数、押しのけられたラベルの名前、整数です)。
  3. label_formatを使って、本文のレベル値がlevelという名前で来るようにしたうえで、level="error"でふるい分けてください。クエリは/root/lk-format/03-relabel.logql、答えは/root/lk-format/03-relabel.txtにlines=<정수>で書きます(プレースホルダーは整数です)。結果の行数は、ステップ2のcorrectと同じである必要があります。
  4. {app="payment"}のすべての行を、<svc>/<본문의 수준>/<dur_ms>の形(プレースホルダーは本文のレベルです)に書き直すクエリを/root/lk-format/04-line.logqlに書いてください。スラッシュ2つだけで区切り、ほかの文字は入れません。そして、結果のうち最も早い行の本文を、/root/lk-format/04-line.txtに1行で書いてください。
  5. 本文のレベル別に行数を数えるメトリクスクエリを/root/lk-format/05-bylevel.logqlに書いてください。結果はちょうど2つの系列である必要があり、各系列のラベルはlvlだけである必要があります。値は/root/lk-format/05-bylevel.txtにinfo=<정수>とerror=<정수>の2行で書きます(プレースホルダーは整数です)。
  6. ステップ5のクエリで、keepの代わりにdropを使って同じ結果を出すクエリを/root/lk-format/06-drop.logqlに書いてください。そして/root/lk-format/06-drop.txtに2行を書きます。series=<계열 수>とdropped=<버린 라벨 이름들을 쉼표로, 사전순>です(プレースホルダーは順に、系列数と、捨てたラベル名をカンマでつないだ辞書順の並びです)。
  7. ステップ3でlabel_formatでラベルを作り、ステップ5では新しい名前で系列をまとめました。そのあと、インデックスのストリーム数が変わったかを確認して、/root/lk-format/07-index.txtに2行で書いてください。streams=<정수>とchanged=<yes|no>です(プレースホルダーは整数です)。ストリーム数は、series APIで数えます。
  8. /root/lk-format/08-panel.logqlにクエリを1つ書いてください。条件は3つです。(1)本文のレベルがerrorの行だけを返してください、(2)返す本文を<dur_ms>ms <svc> <region>の形にしてください(空白2つだけで区切り、ほかの文字はなし)、(3)行フィルターを、本文の書き直しの前に置いてください。そして、結果の最も早い行を/root/lk-format/08-panel.txtに1行で書いてください。

参考

ラベルと本文がずれたデータを入れる

/root/lk-formatでLokiを起動し、date +%sを/root/lk-format/anchor.txtに書いたあと、python3 /opt/lab/d5/gen.py format "$(cat anchor.txt)"でデータを入れてください。そして、{app="payment"}の1時間の行数と、そのストリームに付いているストリームラベルの名前を、/root/lk-format/01-boot.txtに2行で書いてください。lines=<정수>とlabels=<이름들을 쉼표로, 사전순>です(プレースホルダーは順に、整数と、名前をカンマでつないだ辞書順の並びです)。

ストリームラベルは、結果のstreamオブジェクトのキーです。Loki 3.xはservice_nameを自動で付けるので、それも一覧に入ります。辞書順に並べて、カンマでつないでください(空白なしで)。

level="error"が0件を返す

{app="payment"} | logfmt | level="error"を1時間の区間で投げて、結果が何行かを見て、名前が押しのけられたラベルでもう一度問い合わせ、何行かを数えてください。答えは/root/lk-format/02-collision.txtに3行で書きます。naive=<정수>、extracted_name=<밀려난 라벨 이름>、correct=<정수>です(プレースホルダーは順に、整数、押しのけられたラベルの名前、整数です)。

結果の1つをjq '.data.result[0].stream'で展開して、キーの一覧を見てください。パーサーが取り出した名前が、すでにあるストリームラベルとぶつかると、Lokiが接尾辞を付けて押しのけます。その名前でもう一度尋ねれば、数字が出ます。

label_formatで名前を元に戻す

label_formatを使って、本文のレベル値がlevelという名前で来るようにしたうえで、level="error"でふるい分けてください。クエリは/root/lk-format/03-relabel.logql、答えは/root/lk-format/03-relabel.txtにlines=<정수>で書きます(プレースホルダーは整数です)。結果の行数は、ステップ2のcorrectと同じである必要があります。

label_formatは、새이름=기존라벨の形で書くか、テンプレートを渡せます(プレースホルダーは新しい名前と既存のラベルです)。すでにある名前に上書きすることもできます。順序が重要です。名前を変えたあとでふるい分ける必要があります。

line_formatで行を書き直す

{app="payment"}のすべての行を、<svc>/<본문의 수준>/<dur_ms>の形(プレースホルダーは本文のレベルです)に書き直すクエリを/root/lk-format/04-line.logqlに書いてください。スラッシュ2つだけで区切り、ほかの文字は入れません。そして、結果のうち最も早い行の本文を、/root/lk-format/04-line.txtに1行で書いてください。

line_formatはGoテンプレートを受け取ります。フィールド名の前にドットを付けます。レベルは、ステップ2で見つけた、押しのけられた名前を使う必要があります。最も早い行は、direction=forwardで受け取れば、先頭に来ます。

keepで系列をまとめる

本文のレベル別に行数を数えるメトリクスクエリを/root/lk-format/05-bylevel.logqlに書いてください。結果はちょうど2つの系列である必要があり、各系列のラベルはlvlだけである必要があります。値は/root/lk-format/05-bylevel.txtにinfo=<정수>とerror=<정수>の2行で書きます(プレースホルダーは整数です)。

label_formatで使う名前を作り、keepでその名前だけを残してから、外側をsum by (lvl) (count_over_time(... [1h]))で包みます。keepを外すと何が起きるかも、一度見てください。系列数ですぐにわかります。

dropは何を捨て、何を残すか

ステップ5のクエリで、keepの代わりにdropを使って同じ結果を出すクエリを/root/lk-format/06-drop.logqlに書いてください。そして/root/lk-format/06-drop.txtに2行を書きます。series=<계열 수>とdropped=<버린 라벨 이름들을 쉼표로, 사전순>です(プレースホルダーは順に、系列数と、捨てたラベル名をカンマでつないだ辞書順の並びです)。

dropは名前を1つずつ書いて捨てます。ステップ1で書いておいたストリームラベルの一覧と、ステップ2で見たパーサーラベルの一覧を合わせれば、何を捨てるべきかが出ます。残すものが少ないときはkeep、捨てるものが少ないときはdropのほうが短くなります。

応用①: ラベルを変えても、インデックスはそのまま

ステップ3でlabel_formatでラベルを作り、ステップ5では新しい名前で系列をまとめました。そのあと、インデックスのストリーム数が変わったかを確認して、/root/lk-format/07-index.txtに2行で書いてください。streams=<정수>とchanged=<yes|no>です(プレースホルダーは整数です)。ストリーム数は、series APIで数えます。

/loki/api/v1/seriesにmatch[]={app=~".+"}を指定すると、現在インデックスにあるストリームの一覧が出ます。クエリ時点で作ったラベルがそこにあるかを見てください。なければ、それがまさに「クエリ時点のラベルは保存されない」ことの証拠です。

応用②: 人が読めるエラーパネルのクエリ

/root/lk-format/08-panel.logqlにクエリを1つ書いてください。条件は3つです。(1)本文のレベルがerrorの行だけを返してください、(2)返す本文を<dur_ms>ms <svc> <region>の形にしてください(空白2つだけで区切り、ほかの文字はなし)、(3)行フィルターを、本文の書き直しの前に置いてください。そして、結果の最も早い行を/root/lk-format/08-panel.txtに1行で書いてください。

順序が意味を持ちます。line_formatの後ろのフィルターは、新しい本文を見ます。そのため、ふるい分けを先に、書き直しをあとに行う必要があります。レベルでふるい分けるときは、ステップ2で見つけた名前か、ステップ3で作った名前を使えば済みます。