level="error" が 0 件を返した理由
一言でいうと
パーサーが取り出した名前が、すでにあるストリームラベルとぶつかると、Lokiは静かに_extractedを付けて押しのけます。label_format・line_format・keep・dropは、そのあとを整理する道具であり、4つともレスポンスを変えるだけで、インデックスには手を触れません。
なぜ必要なのか
コレクターがPodごとにlevelラベルを付けていました。値はすべてinfoでした。コレクターの立場では、そのファイルはアプリケーションの標準出力なので、infoと見なしたのです。ところが、アプリケーションは、本文に自分のレベルをlevel=errorとして一緒に出力していました。
調査していた人が{app="payment"} | logfmt | level="error"を投げたところ、結果は0件でした。「エラーは1件もありません」と報告しましたが、実際にはその時間に30件ありました。パーサーが取り出したlevelが、ストリームラベルのlevelとぶつかって、level_extractedに名前が変わっていたためです。エラーがないと言った根拠は、実は「名前がぶつかった」ことでした。
どう動くのか
パーサーがラベルを作るとき、すでに同じ名前があると、Lokiはあとから取り出したほうに_extractedの接尾辞を付けます。元のラベルはそのまま残ります。警告もエラーもありません。そのため、新しいストリームを最初にクエリするときは、結果を1つ展開してmetric(またはstream)のキー一覧を目で見る習慣が必要です。
整理する道具は4つです。
| 構文 | 役割 |
|---|---|
label_format 새이름=기존라벨(プレースホルダーは新しい名前と既存のラベルです)またはlabel_format 이름=\{{.フィールド}}``(プレースホルダーは名前です) |
ラベルに別の名前を付け直すか、テンプレートで作ります |
line_format \{{.a}} {{.b}}`` |
行の本文そのものを書き直します |
keep a, b |
書いたラベルだけを残します |
drop a, b |
書いたラベルを捨てます |
テンプレートはGoのtext/templateです。{{.필드}}(プレースホルダーはフィールド名です)で値を入れ、{{.a | trim}}のようなパイプも使えます。line_formatは本文をまるごと入れ替えるので、その後ろの行フィルターは新しい本文を見ます。順序がそのまま意味になります。
4つの構文はすべて、クエリ結果にだけ作用します。インデックスに保存されたストリームは、1文字も変わりません。そのため、label_formatでラベルを増やしても、ストリーム数は増えず、コストも増えません。カーディナリティが心配な値は、ラベルとしてインデックス化せず、本文に置いておいて、クエリのときにこのように取り出して使えば済みます。これが「インデックス化しない」という設計が、実際に得になる場面です。
メトリクスクエリでは、これらの道具がさらに重要になります。sum by (...)に使いたい名前が本文にしかなければ、label_formatで先に作ってやる必要があり、値がバラバラのラベルが残っていると系列が分割されるので、keepやdropで整理する必要があります。
現場での姿
名前の衝突は、levelで最もよく起きます。コレクター・ランタイム・アプリケーションが、それぞれ「レベル」を言いたがるためです。チームの規約として、「コレクターが付けるラベルには接頭辞を付ける」(例: k8s_)と決めておけば、この事故そのものがなくなります。
2つ目は、ダッシュボードの読みにくいログパネルです。行が200文字のJSONだと、人は目で追えません。line_formatで必要な4、5項目だけを残せば、同じパネルが突然役に立つようになります。ただし、そのパネルで本文検索をしようとする人がいるなら、line_formatを行フィルターの後ろに置く必要がある、とコメントを付けておくと親切です。
3つ目は、「ラベルを追加したのに、なぜコストが変わらないのか」という質問です。正常です。クエリ時点のラベルは保存されません。
次のラボですること
ストリームラベルのlevelと、本文のlevel=が、わざとずれているデータをPodのLokiに入れ、level="error"が0件を返すことを自分で見ます。_extractedの名前を探して数え、label_formatで上書きして目的の名前でふるい分けたあと、line_formatで行を書き直し、keepで系列をまとめてレベル別の件数を出します。最後に、ラベルを変えても、インデックスのストリーム数がそのままであることを確認します。