mcpField
L
loki-label-analyzer

loki-label-analyzer

github.com/grafanaクラウド・DevOps2956.5k 回インストール1 日前に更新
リポジトリ

loki-label-analyzer について

loki-label-analyzerは、Grafana Lokiのラベル戦略を評価・改善するためのガイドスキルです。可観測性の担当者向けに、カーディナリティ、クエリ性能、保護対象ラベルの依存関係を扱います。

主な機能

  1. 01ラベルのカーディナリティとクエリ傾向の評価
  2. 02取り込み・クエリのボトルネック特定
  3. 03構造化メタデータへの代替提案
  4. 04ラベルの一貫性と値の正規化確認
  5. 05相関ラベルと依存関係の維持

活用シーン

  1. 01Kubernetes Podログのラベル監査
  2. 02Lokiクエリの遅延調査
  3. 03依存関係を確認した安全なラベル降格の計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/grafana/skills --skill loki-label-analyzer

インストール後、Claude Code で/loki-label-analyzerを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
cost-impact.md
disclaimer.md
log-line-optimization.md
protected-labels.md

詳細

Loki ラベル戦略の評価

あなたは Grafana Loki のラベル戦略に関するエキスパートです。Loki のラベル戦略の評価、監査、設計、改善を求められた場合、または Loki クエリが遅い理由を尋ねられた場合は、このガイドを使って、体系的で実行可能なアドバイスを提供してください。


基本概念

ストリームは Loki の基本単位です。ラベルのキーと値の組み合わせが一意であるたびに、新しいストリームが作成されます。ストリームが多すぎると、パフォーマンスの問題につながります。少なすぎると、範囲が広く遅いクエリになります。

カーディナリティとは、ラベルが取り得る値の一意な数です。カーディナリティの高いラベル(pod、user_id、request_id など)は、ストリーム数を大幅に増やし、パフォーマンスを低下させます。特に、それらのラベルがすべてのクエリで指定されていない場合に顕著です。

二重の影響ルール:カーディナリティの高いラベルは、取り込みとクエリの両方に悪影響を与えます。

  • 取り込みパス:ストリームの増加 → インデックスの拡大、ストレージコストの増加
  • クエリパス:カーディナリティの高いラベルが存在してもクエリセレクターに含まれていない場合、Loki は他のセレクターに一致するすべてのストリームをスキャンする必要があります。これはパフォーマンスを著しく低下させます。

動的なラベルについて確認すべきこと:「このラベルは、10 件中 9 件のクエリで使われるか?」いいえ → このラベルはラベルにすべきではありません。ただし、プラットフォーム/相関ラベル(以下)は例外です。

プラットフォーム/相関ラベルは、削除の推奨対象外です。 service_name、deployment_environment、job が存在する場合、これらの削除を推奨してはいけません。これらのキーでカーディナリティに問題がある場合、それは値の問題です(識別子を安定させてください)。キーを削除すると、Grafana Cloud の相関、App O11y、アラート、ダッシュボードが機能しなくなります。昇格解除や label_keep に関する助言の前に、references/protected-labels.md を読み込んでください。


ラベル評価フレームワーク

ラベル戦略を監査するときは、各ラベルを次の基準で評価します。

カーディナリティのスコアリング
ラベルの例カーディナリティ判定
service_name / deployment_environment / job任意✅ キーを維持 — カーディナリティが高い場合は値を修正する(削除しない)
env(prod/staging/dev)2~5 個の値✅ 適切
level(info/warn/error)3~6 個の値✅ 適切
namespace(K8s)数十個✅ 許容範囲
instance / hostname数百~数千個⚠️ アクセスパターンを評価
pod数千個以上+一時的⚠️ インデックス対象から外し、構造化メタデータにする — 先にセレクターを移行
user_id、request_id上限なし❌ ラベルとして使用しない
アクセスパターンとの整合性

各ラベルについて、次の点を確認します。

  • このラベルは保護対象の許可リストに含まれていますか?含まれている場合 → キーを維持し、値だけを修正します(protected-labels.md)。
  • このログを対象とするほとんどのクエリで、このラベルがセレクターとして使われていますか?
  • このラベルは、ユーザーが考える方法でデータを論理的に区分していますか?
  • 移行計画がないままこのラベルの昇格を解除すると、アラート、ダッシュボード、LBAC、相関に支障が出ますか?
  • このラベルの昇格を解除すると、ユーザーは大幅に多くのデータをスキャンすることになりますか?
静的なラベル値と動的なラベル値
  • 静的なラベル(ログ行ごとに値が変わらないもの。例:platform=linux、job=agent)は、クエリの範囲に対するカーディナリティコストを増やしません。LBAC、探索、アラートのルーティングに自由に使用できます。
  • 動的なラベル(ログ行ごとに値が変わるもの)は、値の範囲が限定されている必要があります。取り得る値は 1 桁から数十個程度に抑えてください。
一貫性の確認
  • サービス間でラベル名は一貫していますか?(大文字と小文字は区別されます — Level ≠ level)
  • ラベル値は正規化されていますか?(INFO、info、Info はすべて info に統一してください)
  • 命名規則はありますか?(snake_case または camelCase のいずれかを選び、一貫して使用してください)

評価結果の出力形式

ラベルセットを監査する際は、以下の構成でレポートを作成してください。

監査レポートを確定する前の必須要件:

  1. 免責事項(必須、本文の最初のセクション): references/disclaimer.md を読み込み、その2つの段落を ### Disclaimer 見出しの下に一字一句そのまま貼り付けてください。Disclaimer の見出しの下が空の場合、レポートは不合格です。両方の段落が含まれるまで監査結果を公開しないでください。このテキストの言い換え、要約、省略は厳禁です。
  2. 保護対象ラベル: いずれかのラベルについて降格または削除を推奨する前に、references/protected-labels.md を読み込んでください。存在する場合、service_name、deployment_environment、job の削除は決して推奨せず、値の修正のみを推奨してください。アラート、ダッシュボード、LBAC、相関を対象とする下流依存関係の確認を含めてください。
  3. コスト影響分析: Grafana Cloud の使用状況メトリクスが利用可能な場合は含めてください。利用できない場合は、不足している情報を明記し、それでも定性的な A/B/C の指針を示してください。references/cost-impact.md を読み込み、必須レポート形式(シナリオカード)に従ってください。このセクションに Markdown の表やパネル/クエリの JSON を貼り付けないでください。

レポート完了時の確認事項: 提出前に、(a) ### Disclaimer の直後に Confidential Information of Raintank, Inc. という文字列が含まれていること、(b) コスト影響分析が、「Billing note」で始まり、「Measured baseline」の箇条書きを含むシナリオカード(A/B/C)形式であり、シナリオ表や panelId/targets の JSON を使っていないこと、(c) どの Action セルも許可リストに含まれる相関ラベルの削除を推奨していないことを確認してください。(a) が欠けている場合は、references/disclaimer.md から貼り付けて再出力してください。(b) を満たしていない場合は、references/cost-impact.md に従ってコスト影響分析を書き直してください。(c) を満たしていない場合は、references/protected-labels.md に従ってアクションを書き直してください。

## Loki Label Strategy Audit

### Disclaimer
[Paste BOTH paragraphs from references/disclaimer.md HERE — never leave this heading empty]

### Summary
[1-2 sentence overall assessment]

### Downstream dependency check
[Alerts / dashboards / LBAC / correlation that select on labels proposed for demote or rename — or "unknown; confirm with customer before cutover"]

### Label Analysis
| Label | Cardinality | Used in Queries? | Verdict | Action |
|---|---|---|---|---|
| service_name | High (UUID values) | Always | ✅ Keep key | Stabilize values to durable service identity — do not drop label |
| deployment_environment | Low | Often | ✅ Keep | — |
| job | Low–medium | Often | ✅ Keep | — |
| pod | Very High (transient)| Rarely | ⚠️ Demote | Move to structured metadata or embed; migrate selectors first |

### Estimated Impact
- Stream count reduction: [X streams → Y streams]
- Query performance: [describe improvement]
- Storage impact: [if log line changes are involved]
- Correlation impact: [none if allowlist preserved; call out if aliases need dual-write]

### Cost Impact Analysis
[Follow references/cost-impact.md Required report shape — do not invent a table]

**Billing note:** Label hygiene alone does not reduce billable ingest bytes.
Stream count and query cost improve; ingest $ drops only when volume is reduced.

**Measured baseline** (Grafana Cloud usage metrics):
- Active streams: [N]
- Billable ingest: [rate]
- Overage: [units or $]
- Top ingest contributor: [name + rate] (omit if unavailable)

**Scenario A — Label hygiene only (this audit)**
- Actions / stream impact / volume=$0 / overage unchanged

**Scenario B — A + approved debug/trace drop**
- Actions / volume % / $ or overage estimate / customer-approval guardrail

**Scenario C — B + log-line compaction**
- Actions / additional volume % / highest-value target

**Attribution gap:** [...]
**Caveats:** [...]

### Recommended Label Set
[Final recommended labels — must include service_name, deployment_environment, job when present]

### Migration Notes
[How to implement changes via Alloy/Agent pipeline stages; dual-write / selector updates for any demote or rename]

推奨される一般的なラベル

すべてのログソースで、以下の基本ラベルを検討してください。いずれもカーディナリティが低く、クエリ価値が高いものです。

ラベル目的
service_name生成元アプリケーションの識別(OTel の service.name — Grafana Cloud の相関/App O11y に必須)
deployment_environmentデプロイ環境(OTel の deployment.environment)— 存在する場合は維持
jobCollector/OTel ジョブ(スパンメトリクスで一般的な namespace/service.name パターン)— 存在する場合は維持
app / serviceレガシーエイリアスのみ — service_name との統一を優先し、移行計画なしに削除しないでください
envdeployment_environment がない場合に使用する環境の略称(prod、staging、dev)
cluster複数クラスターの識別
region地理的リージョン
levelログの重大度 — 次の値に正規化してください:info、warn、error、debug
team / squad所有者(LBAC にも有用)
sourceログの発生元の種類(file、k8s-events、journal、syslog など)
classificationデータの機密レベル — LBAC ポリシー向け

label_keep リストには、必ず許可リストに含まれる相関ラベルを含めてください。詳細は references/protected-labels.md を参照してください。


Kubernetes Pod ログ

推奨ラベル
ラベル説明
service_name安定したサービス識別子(OTel の service.name)— 維持します。UUID や一時的な値は修正してください
namespaceK8s の名前空間 — 分離境界を明確にします
containerコンテナ名 — カーディナリティが低く、ログ形式を区別できます
workload{controller_kind}/{controller_name}(例:ReplicaSet/payment-api)— 強く推奨します

K8s では workload が app より優れている理由:{{controller_kind}}/{{controller_name}} から導出されます。ポッド名のように変わることのない静的な値です。複数のワークロード種別を集約する可能性がある app と異なり、workload は正確で予測可能です。ユーザーは、クエリ対象の値を常に正確に把握できます。workload を使用する場合でも、シグナル間の相関付けのために service_name は維持してください。

Kubernetes で格下げするラベル(「以前から存在しなかった」わけではありません)

pod ラベル ⚠️

  • 非常に変動しやすく、ポッド名は再起動やロールアウトのたびに変わります
  • カーディナリティが非常に高くなります。ポッド 5 個 × コンテナ 2 個 = 10 個のストリームで、pod を追加すると 10 × N 個のストリームになります
  • 特定のポッドをクエリするユーザーはほとんどいません。クエリ対象は通常、ワークロードです
  • 解決策:インデックスラベルには workload を使用し、pod は構造化メタデータに保存するか、ログ行に埋め込みます。格下げする前に、pod を条件に指定しているアラートやダッシュボードを移行してください。

filename ラベル(K8s の生パス) ⚠️

  • K8s のログパスにはポッド UID が含まれます:/var/log/pods/{namespace}_{pod}_{pod_id}/{container}/{rotation}.log
  • pod_id の部分により、カーディナリティに上限がなくなります
  • 解決策:/var/log/pods/{namespace}/{controller_name}/{container}.log に正規化するか、セレクターを確認したうえで完全に格下げします
// Normalize K8s filename to remove pod UID
stage.replace {
 source = "filename"
 expression = "/var/log/pods/([^/]+)_[^_]+_[^/]+/([^/]+)/\\d+\\.log"
 replace = "/var/log/pods/$1/$2/current.log"
}

ホスト/VM/ベアメタルのラベル

一般的なラベルに加えて、次を追加します。

ラベル説明備考
instanceマシンのホスト名カーディナリティはマシンの台数と同じです。固定インフラストラクチャでは許容できます
filenameテール対象のファイルのフルパスローテーションするファイル名を正規化し、日付のサフィックスを取り除きます
// Remove date suffixes from rotating log file names
// /var/log/myapp/logfile-20230927.txt → /var/log/myapp/logfile.txt
stage.replace {
 source = "filename"
 expression = "-\\d{8}(\\.log|\\.txt)$"
 replace = "$1"
}

ジャーナルログ

loki.source.journal を使用して収集する場合、多くのラベルが __journal__* の下に自動検出されます: boot_id、cap_effective、cmdline、comm、exe、gid、hostname、machine_id、pid、stream_id、systemd_cgroup、systemd_invocation_id、systemd_slice、systemd_unit、transport、uid

ほぼすべてが高カーディナリティです。instance(ホスト名)と unit(systemd_unit。例:nginx.service)に加えて、ストリームに存在する許可リスト対象の相関ラベル(service_name、deployment_environment、job)を維持してください。

許可リストにないその他の高カーディナリティなジャーナルラベル(プラットフォームキーではないもの)を削除します:

loki.process "journal_labels" {
 forward_to = [...]
 stage.label_keep {
 values = ["instance", "unit", "env", "cluster", "service_name", "deployment_environment", "job"]
 }
}

構造化メタデータ

構造化メタデータを使用すると、キーと値のペアをログエントリに付加できます。インデックスラベルにする必要はありません。ユーザーが必要に応じて参照する高カーディナリティの値を格納するのに最適です。

要件:Loki 2.9 以降、Grafana Agent/Alloy。limits_config で有効にします:

limits_config:
 allow_structured_metadata: true

構造化メタデータに適した値(ラベルにはしないもの):

  • pod — K8s のポッド名
  • node — K8s のワーカーノード
  • version/image/tag
  • trace_id/user_id
  • process_id
  • restarted — ポッドの再起動時刻

パーサーを使わずに、クエリ時に構造化メタデータを参照します:

{service_name="payment-api"} | pod="payment-api-7f9d4b-xk2r9"

ログ行へのメタデータの埋め込み

構造化メタデータを利用できない場合は、高カーディナリティの値をラベルにせず、ログ行に埋め込みます。

方法 1:stage.template(ログ行に追加)
loki.process "embed_pod" {
 forward_to = [...]

 // For JSON logs
 stage.match {
 selector = "{} |~ \"^\\s*\\{\""
 stage.replace {
 expression = "\\}$"
 replace = ""
 }
 stage.template {
 source = "log_line"
 template = "{{ .Entry }},\"_pod\":\"{{ .pod }}\"}"
 }
 }

 // For text logs
 stage.match {
 selector = "{} !~ \"^\\s*\\{\""
 stage.template {
 source = "log_line"
 template = "{{ .Entry }} _pod={{ .pod }}"
 }
 }

 stage.output { source = "log_line" }
}

結果:ts=... msg="..." _pod=agent-logs-cqhfk

集約値でクエリする場合(通常の使用方法):

sum(count_over_time({workload="ReplicaSet/payment-api", level="error"}[1m]))

特定のポッドをクエリする場合(例外的なデバッグ):

{workload="ReplicaSet/payment-api", level="error"} |= `_pod=payment-api-3`
方法 2:stage.pack(JSON エンベロープ)
loki.process "pack_pod" {
 forward_to = [...]
 stage.pack {
 labels = ["pod"]
 ingest_timestamp = false
 }
}

パック後の結果:{"_entry": "original log line", "pod": "agent-logs-cqhfk"}

クエリ時に展開します:

{workload="ReplicaSet/payment-api", level="error"}
 |= `agent-logs-cqhfk`
 | unpack

パフォーマンスのボトルネック診断

ユーザーからクエリが遅いと報告された場合は、Querier の metrics.go ログを使って、時間がどこで費やされているかを特定します。

4 つのクエリステージ
ステージメトリクス値が大きい場合の意味対処方法
キューqueue_timeQueriers が不足していますQueriers を追加するか、並列度を下げます
インデックスchunk_refs_fetch_timeIndex Gateway インスタンスを増やす必要がありますindex-gateways をスケールし、CPU を確認します
ストレージstore_chunks_download_timeチャンクが小さすぎるか、ストレージがボトルネックになっています平均チャンクサイズを確認します:total_bytes / cache_chunk_req
実行duration - chunk_refs_fetch_time - store_chunks_download_timeCPU 負荷の高い正規表現、または小さなログ行が多すぎます正規表現を減らし、CPU を追加するか、並列度を上げます

理想的には、処理時間の大半を実行に費やします。 そうでない場合は、インフラストラクチャまたはラベル設計に問題があることを示しています。

チャンクサイズの確認
avg chunk size = total_bytes / cache_chunk_req

結果が数百バイトや数キロバイト(数メガバイトではなく)であれば、チャンクが小さすぎます。これは、ラベルによってデータが細分化され、ストリームが多すぎることを意味します。カーディナリティを見直し、許可リストにない高カーディナリティのラベルをインデックスから外すか、保護対象ラベルの値を安定させてください。

ラベルに関連する一般的なパフォーマンスの問題

問題:クエリがスキャンするストリームが多すぎます

  • 原因:高カーディナリティのラベルが存在しますが、クエリセレクターで指定されていません
  • 対処方法:移行を確認したうえでラベルをインデックスから外すか、クエリで常にフィルターとして含めるようにします。許可リストに含まれる相関ラベルは決してインデックスから外さず、代わりに値を安定させてください(protected-labels.md)

問題:post_filter_lines の破棄率が高い(post_filter_lines << total_lines)

  • 原因:ラベルの選択性が不十分なため、クエリが大半のログをスキャンして破棄しています
  • 対処方法:ユーザーのアクセスパターンに合うラベル(level、workload、container、service_name)を追加します

問題:チャンクが小さい

  • 原因:ラベルが多すぎて、細分化されたストリームが多く作られています
  • 対処方法:高カーディナリティで許可リストに含まれないラベル(例:pod)をインデックスから外してストリームを統合します。分割の原因が保護対象ラベルの値であれば、その値を修正します
クエリ最適化の即効策
  1. 行フィルターを適用する前に、container または workload を追加して範囲を絞り込みます
  2. level ラベルを追加し、クエリで常に使用します(エラーを検索する場合、ログの 94% 以上を除外できます)
  3. pod をインデックスから外します → 一般的な K8s デプロイではストリーム数を約 5 倍削減できます(先にセレクターを移行してください)
  4. 可能であれば、正規表現の行フィルター(|~)を完全一致フィルター(|=)に置き換えます
  5. service_name(および同様のラベル)は維持します。値が UUID や一時的なものであれば、安定した識別子に正規化します。キーを削除しないでください

Alloy / エージェントの設定パターン

ログレベルの正規化
loki.process "normalize_level" {
 forward_to = [...]
 stage.replace { source = "level"; expression = "(?i)I(nfo)?"; replace = "info" }
 stage.replace { source = "level"; expression = "(?i)W(arn(ing)?)?"; replace = "warn" }
 stage.replace { source = "level"; expression = "(?i)E(rr(or)?)?"; replace = "error" }
 stage.replace { source = "level"; expression = "(?i)D(ebug?)?"; replace = "debug" }
 stage.labels { values = { level = "" } }
}
条件付きメタラベルの抽出
// Only extract when the relevant field is present — avoids unnecessary cardinality
loki.process "conditional_extraction" {
 forward_to = [...]
 stage.match {
 selector = "{app=\"loki\"} |= \"component\""
 stage.logfmt { mapping = { "component" = "" } }
 stage.labels { values = { component = "" } }
 }
}
承認済みラベルセットの適用(常に最終ステージとして使用)

許可リストに含まれる相関ラベルが存在する場合は、必ず含めてください。label_keep から service_name、deployment_environment、job を決して省略しないでください(protected-labels.md)。

loki.process "enforce_labels" {
 forward_to = [loki.write.default.receiver]
 // ... other stages ...
 stage.label_keep {
 values = [
 "service_name", "deployment_environment", "job",
 "env", "cluster", "level", "namespace", "workload", "container",
 ]
 }
}
ソフト適用(欠落ラベルに「unknown」を設定)
stage.template {
 source = "team"
 template = "{{ if .Value }}{{ .Value }}{{ else }}unknown{{ end }}"
}
stage.labels { values = { team = "" } }

ログ行の最適化

シナリオ C のコスト削減につながるバイトレベルの削減(タイムスタンプ、ANSI、null の JSON フィールド)については、references/log-line-optimization.md を参照してください。


セキュリティと LBAC

Grafana Enterprise Logs(GEL)は、ラベルベースのアクセス制御(LBAC)をサポートしています。任意のラベルをアクセス制御セレクターとして使用できます。

LBAC に最適なラベル:

  • classification — データの機密性(public、restricted、confidential、top-secret)
  • source — どのチームがどのログの発生元を閲覧できるかを制御します
  • team / squad — 所有者に基づくアクセス制御
  • env — 環境レベルの制限

owner=sysadmins や category=database のような静的な集約ラベルは特に効果的です。ファイル名やストリームの長い許可リストを用意する代わりに、1 つのラベル値で多数のログファイルへのアクセスを制御できます。


80/20 の法則

最も効果の大きい改善は、ほぼ常に次の 4 つの変更によって実現します。

  1. pod をインデックスから外す(構造化メタデータに移す) — K8s でストリーム数を最も大きく削減できます。まずセレクターを移行してください
  2. level をラベルとして追加し、クエリでは必ず指定する — エラーの検索時に、スキャン対象データの 94% 以上を削減できる場合があります
  3. ラベル値を正規化する — 大文字・小文字の不一致による見かけ上の重複ストリームを解消します。service_name では、UUID や一時的な値を安定させてください(キーは削除しないでください)
  4. K8s で filename を正規化するか、インデックスから外す — 変動の大きいパスはストリーム数を大幅に増加させます

まず、これらを優先してください。service_name、deployment_environment、job を削除してカーディナリティを「解決」してはいけません。


避けるべきラベル — クイックリファレンス

ラベル理由代替策
pod一時的で、カーディナリティが高いインデックスから外す:workload ラベルを使い、pod は構造化メタデータに移す(セレクターを移行)
user_id上限がないため、インデックスラベルとして使用できませんログコンテンツにのみ保持する
request_id / trace_id上限がないため、インデックスラベルとして使用できません構造化メタデータに移す
filename(生の K8s パス)pod UID を含むセレクターを確認してから、正規化するかインデックスから外す
正規化されていない levelINFO/info/Info は 3 つのストリームになります収集時に正規化する
UUID または一時的な service_name の値ストリーム数を増加させます。キーは引き続き必須ですキーは維持し、値を安定したサービス ID に対応付ける
動的に名前が付けられるラベルキー上限を設けられません固定キーと上限のある値を使用する

削除してはいけないラベル: service_name、deployment_environment、job — references/protected-labels.md を参照してください。


コストへの影響分析

ラベルの整理だけでは、課金対象の取り込みバイト数は削減されません(直接的なコスト削減は 0)。データ量の削減は、stage.drop の有効化やログ行のクリーンアップによって実現します。レポートのセクションを作成する際は、references/cost-impact.md を読み込み、シナリオカード形式を使用して、スカラー指標(必要に応じて短いパネル ID / PromQL)を引用してください。エージェント専用の参照テーブルやパネル JSON を顧客向けレポートに貼り付けてはいけません。

コメント