LogQLは三つの部分でできている
一言でいうと
LogQLクエリは、ストリームセレクター → ログフィルター → 集計の3つの部分でできています。性能は、ほぼ最初の部分が決めます。
なぜ必要なのか
Lokiのクエリが遅いという報告の大部分は、クエリの文法ではなく、範囲を絞っていないことが原因です。前の波括弧がストリームを10万個選べば、後ろで何をしても遅くなります。
3つの部分
sum by (pod) (
rate({namespace="labhub-prod", app="labhub"} |= "error" | json | status >= 500 [5m])
)
^--집계--^ ^------- 스트림 셀렉터 -------^ ^------ 로그 필터 ------^
1)ストリームセレクター{...}: インデックス化されたラベルで候補を選びます。必ず1つ以上必要で、等号マッチャー(=)は正規表現(=~)よりはるかに安いです。
2)ログフィルター|=・!=・|~・!~: 選んだストリームの本文をスキャンします。順序が重要です。
# 나쁨 — 파싱부터 하고 거른다
{app="labhub"} | json | level = "error"
# 좋음 — 문자열로 먼저 줄이고 파싱한다
{app="labhub"} |= "error" | json | level = "error"
|=は単純な部分文字列の検査なので、とても安いです。| jsonは行ごとにパースするため高価です。安いものを先に置くと、パースする行数が大きく減ります。
3)集計: rate、count_over_time、sum byなど。ここでログがメトリクスになります。
# 초당 오류 수를 파드별로
sum by (pod) (rate({app="labhub"} |= "error" [5m]))
# 오류 메시지 상위 10개
topk(10, sum by (msg) (count_over_time({app="labhub"} | json [1h])))
パーサー3種類
| パーサー | いつ使うか |
|---|---|
| ` | json` |
| ` | logfmt` |
| ` | pattern` |
| ` | regexp` |
パースして作ったフィールドはインデックス化されません。そのため| status >= 500はスキャンであり、インデックス参照ではありません。よく使う値なら、収集時点でラベルに昇格させることを検討しますが、カーディナリティを先に計算します。
よくある勘違い
「ラベルを増やせばクエリが速くなる」という考え: ラベルを1つ増やすと、ストリーム数がそのラベルの値の個数だけ掛け算されます。statusをラベルに入れるとストリームが5倍(2xx/3xx/4xx/5xx/その他)になり、user_idを入れるとユーザー数の分になります。ストリームが多くなるとすべてのクエリが遅くなります。
「|~の正規表現が便利」という考え: |~ "error|warn"の代わりに|= "error" or |= "warn"のほうがはるかに安いです。正規表現は行ごとにエンジンを回します。
クエリが動く順序を知ればコストが見える
LogQLはパイプラインであり、前で減らすのが常に最も安いです。
{namespace="labhub-prod", app="api"} ← 1. 색인으로 스트림을 고른다 (거의 공짜)
|= "timeout" ← 2. 원문 문자열 필터 (싸다)
| json ← 3. 파싱 (비싸다)
| status >= 500 ← 4. 파싱된 필드로 필터
| line_format "{{.msg}}" ← 5. 출력 성형
順序を変えると、かかるコストが変わります。| jsonを先に行って|= "timeout"をあとに行うと、すべての行をパースしてから捨てることになります。文字列フィルターをできるだけ前に引き寄せるのが、LogQLチューニングの最初のルールです。
メトリクスに変える: ログからグラフを取り出す
ログを数えればメトリクスになります。計装のないシステムで特に便利です。
# 5분간 5xx 비율
sum(rate({app="api"} |= "HTTP/1.1" 5" [5m]))
/
sum(rate({app="api"} [5m]))
# 파싱한 필드로 p95 지연
quantile_over_time(0.95,
{app="api"} | json | unwrap duration_ms [5m]) by (route)
ただし、これを常時ダッシュボードに置いてはいけません。ログのスキャンは、時系列の参照よりはるかに高価です。値が確認できたら、アプリケーションに本物のメトリクスを入れ、ログベースのクエリは調査のときだけ使います。ログから作ったメトリクスは、「メトリクスを入れる価値があるか」を調べる偵察ツールです。
保持と圧縮をあらかじめ決める
Lokiはログをチャンクにまとめてオブジェクトストレージに入れます。ここで2つを決める必要があります。
- 保持期間: 30日がよくある出発点です。規制があれば、監査ログだけに別途長い保持ポリシーを設定します(
retention_streamでラベル別に指定)。 - チャンクサイズとアイドル時間: ストリームが静かだと、小さなチャンクがたくさんできて、参照が遅くなります。
chunk_idle_periodを延ばすとチャンクは大きくなりますが、最新のログが検索に現れる時刻が遅くなります。
アラートに使うクエリは短い範囲(5–15分)にしておき、調査用の長い範囲のクエリと分けます。同じクエリを1分ごとに24時間範囲で実行すると、コストが静かに膨らみます。
実務で本当に大切なこと
クエリが遅いときに最初に測るべきものは、選択されたストリーム数です。
count(count by (pod, container) ({namespace="labhub-prod"}))
Grafanaのクエリ統計(Query inspector)にもTotal bytes processedが出ます。その値がGB単位なら、範囲が広すぎます。時間範囲を減らすか、ラベルをもう1つ付けるほうが、クエリのチューニングより効果が大きいです。