Claude Media
Claude Codeでlogstashパイプラインをレビュー — grok失敗とpersistent queue

Claude Codeでlogstashパイプラインをレビュー — grok失敗とpersistent queue

logstashのgrok失敗とpersistent queue設定をClaude Codeに点検させる手順。構文チェックはPostToolUseフックで自動化し、-tが見ない部分をサンプルログで補います。

logstashのパイプライン設定は、Claude Codeに編集させるたびに構文チェックを挟み、grokとpersistent queueだけは観点を決めてレビューさせると事故が減ります。構文チェックはPostToolUseフックで自動化できます。ただし-tはgrokのパターンまでは検証しないため、サンプルログを通す確認を別に用意します。

ここでは、設定ファイルを編集した直後にlogstash -tを走らせるフックと、grokの失敗・persistent queueの設定を洗うレビュー指示の置き方を順に説明します。

レビューで先に見る箇所は3つ

logstashの設定は、壊れ方によって気づくタイミングが違います。構文の誤りは起動時に分かります。grokの取りこぼしは起動後にタグとして静かに積もります。persistent queueの容量不足は、負荷がかかってから表に出ます。

見る箇所壊れたときの出方確かめる手段
構文壊れたときの出方起動できない確かめる手段logstash -t(フックで自動実行)
grok壊れたときの出方_grokparsefailureタグ付きのイベントが増える確かめる手段サンプルログを通した出力
persistent queue壊れたときの出方容量超過で新規イベントを受け付けなくなる確かめる手段設定値と実ディスク容量の突き合わせ

最初の行だけは機械的に回せるので、フックに任せます。残る2行はClaude Codeに読ませて指摘させる領域です。

構文チェックをPostToolUseフックで回す

PostToolUseはツールが成功した直後に動くイベントです。入力のJSONにはtool_inputが含まれ、EditやWriteで書き換えたファイルのパスを取れます。matcherにEdit|Writeと書くと、この2つのツールだけに反応します。

フックの定義は、チームで共有するなら.claude/settings.jsonに置きます。スクリプトのパスは${CLAUDE_PROJECT_DIR}で指すと、作業ディレクトリが変わっても外れません。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/logstash-check.sh",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

スクリプトは、パイプライン用ディレクトリ配下の.confが編集されたときだけlogstash -tを実行します。logstashはディレクトリを渡すと複数の設定ファイルを読み込むので、1ファイルでなくディレクトリ全体を検査します。input・filter・outputを別ファイルに分けている構成でも、結合後の状態で検査できます。

#!/bin/bash
# .claude/hooks/logstash-check.sh
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
  */logstash/pipeline/*.conf) ;;
  *) exit 0 ;;
esac
 
dir=$(dirname "$file")
if ! out=$(logstash --path.data "$(mktemp -d)" -t -f "$dir" 2>&1); then
  echo "logstash -t が失敗しました:" >&2
  echo "$out" | tail -n 20 >&2
  exit 2
fi
exit 0

終了コードの扱いが要点です。PostToolUseでは、フックが終了コード2で終わると、標準エラー出力がClaudeに渡ります。ツールはすでに実行済みなので編集自体は取り消されませんが、Claudeは失敗メッセージを読んで直しに動きます。終了コード0で終わったフックの標準エラー出力はClaudeに見えません。終了コード1などのそれ以外の値は非ブロッキングエラーの扱いで、公式がClaudeに渡すと案内しているのは終了コード2のときの標準エラー出力です。

--path.dataに一時ディレクトリを渡しているのは、稼働中のlogstashとデータディレクトリを共有しないための用心です。logstash本体をホストに入れていない環境では、コマンドの行をコンテナ経由の呼び出しに置き換えます。

フックの発火をifで絞る

スクリプト内のcaseで弾く代わりに、フック定義側のifで絞る方法もあります。ifは権限ルールの書式で1つだけ書け、&&や||で条件を重ねることはできません。EditとWriteの両方を対象にするなら、同じスクリプトを指すハンドラを2つ並べます。ifが効くのはツール系のイベントだけなので、PostToolUseなら使えます。

スクリプト側のcaseは残しておくのが無難です。ifの判定は最善努力とされており、厳密な制限には権限システムを使うよう公式が案内しています。構文チェックの用途なら、余計に走っても害は小さく、caseとの二重化で十分です。

構文チェックが見逃すgrokの失敗

-t(--config.test_and_exit)は設定の構文だけを確かめて終了します。公式の説明にも、grokのパターンの正しさはこのフラグでは検査されないと書かれています。つまり、構文が正しいのにログ1行もマッチしないgrokは、フックを素通りします。

マッチに失敗したイベントには、既定で_grokparsefailureタグが付きます。タグの名前はtag_on_failureで変えられます。タイムアウトしたときは別のタグ_groktimeoutが付くので、2種類を区別して数える必要があります。

サンプルログを通すスクリプトを足す

実ログを数十行抜き出したsample.logを用意し、stdin入力とstdout出力だけのテスト用設定をfilterと組み合わせます。出力に_grokparsefailureが1件でも含まれたら失敗とみなします。

#!/bin/bash
# logstash/test/check-grok.sh
tmp=$(mktemp -d)
cp logstash/pipeline/*filter*.conf "$tmp"/
cp logstash/test/00-stdin.conf logstash/test/99-stdout.conf "$tmp"/
 
out=$(logstash --path.data "$(mktemp -d)" -f "$tmp" \
  < logstash/test/sample.log 2>/dev/null)
n=$(echo "$out" | grep -c -E '_grokparsefailure|_groktimeout')
echo "grok失敗イベント: $n"
[ "$n" -eq 0 ]

テスト用の入出力は次の2ファイルです。rubydebugはタグを含むイベント全体を表示するコーデックです。

# 00-stdin.conf
input { stdin {} }
 
# 99-stdout.conf
output { stdout { codec => rubydebug } }

このスクリプトは、.confの編集のたびにフックで回すには重いので、Claudeに「grokを変えたらcheck-grok.shを実行して結果を読む」とCLAUDE.mdで指示する運用にします。実行の判断をClaudeに任せ、失敗件数と該当行を読ませて直させる反復です。

grokの設定でレビューさせる点

grokフィルタの設定値には、レビューで見落としやすいものがいくつかあります。

  • break_on_match: 複数パターンを並べたとき、最初にマッチした時点で止めるかどうかを決めます。意図せず全パターンを試す設定になっていないかを見ます
  • timeout_millis: パターンごとの打ち切り時間で、既定は30000ミリ秒です。0にするとタイムアウトが無効になります
  • timeout_scope: 既定はpatternで、パターンごとに時間を計ります。eventにするとイベント単位の1回の制限になり、複数パターンを持つ設定ではオーバーヘッドが小さくなります
  • overwrite: 既存フィールドを上書きする指定です。messageを書き換えると元のログ行が失われるため、後段で元の行が要らないかを確かめます

tag_on_failureを空配列にしている設定も要注意です。失敗が記録されなくなり、サンプルログによる検査が成り立たなくなります。

persistent queueの設定を点検する観点

persistent queueは、処理中のイベントをディスクに書いて、異常終了時のデータ損失を減らす仕組みです。既定は無効で、queue.type: persistedを設定したときだけ有効になります。

設定の置き場はlogstash.ymlで、そこでの値は全パイプラインに適用されます。パイプラインごとに変えたいときはpipelines.ymlに書きます。ここで効いてくる事実があります。容量系の値は「全体で分け合う合計」でなく、パイプラインごとの値として扱われます。

設定既定・性質レビューで見ること
queue.type既定・性質既定はmemoryレビューで見ることpersistedになっているか
path.queue既定・性質既定はpath.data/queueレビューで見ること十分な空きのあるローカルディスクか
queue.max_bytes既定・性質既定は1024mb(1gb)レビューで見ること全パイプライン分の合計がディスク容量を下回るか
queue.page_capacity既定・性質既定は64mbレビューで見ることqueue.max_bytesより大きくなっていないか
queue.checkpoint.writes既定・性質既定は1024レビューで見ること耐久性を優先して1にしていないか。1は性能に大きく響く
queue.drain既定・性質既定は無効レビューで見ること有効なら、停止時にキューが空になるまで待つ点を許容できるか

queue.max_bytesを小さくする場合、queue.page_capacityより小さくしてはいけないと公式に書かれています。queue.max_eventsとqueue.max_bytesを両方指定すると、先に到達した側が基準になります。

設定値の外にある限界

設定値が正しくても防げないものがあります。レビューでは、次の3点が前提と合っているかを見ます。

  • TCP・UDPなど、受信確認の仕組みがないinputはキューで保護できません。受信確認を持つbeatsやhttpは、保護の対象になります
  • チェックポイントが書かれる前に異常終了すると、データを失うことがあります
  • キューのデータは複製されません。ディスク障害やマシン喪失には対処できません

キューのディレクトリにNFSを使う構成は、公式がサポート外としています。path.queueがネットワークマウントを指していないかは、Claude Codeにパスを追わせれば拾えます。

キューが満杯になると、logstashは新規イベントの受け付けを止めます。上流が止まる動きなので、queue.max_bytesの値は負荷試験の結果で決める項目です。値の妥当性は設定ファイルだけからは判断できないため、レビューの出力には「前提となる流量の想定」を書かせると、人の確認が要る箇所が分かります。

レビュー指示をCLAUDE.mdに置く

観点を毎回プロンプトに書くのは手間なので、CLAUDE.mdに短い規約として書いておきます。実行する検査コマンドも合わせて載せると、Claudeが自分で回します。

## logstashパイプラインの変更ルール
- `logstash/pipeline/` を編集したら、フックの構文チェック結果を読んでから次へ進む
- grokを変えたら `logstash/test/check-grok.sh` を実行し、失敗件数が0になるまで直す
- `tag_on_failure` を空にしない。`_grokparsefailure` と `_groktimeout` を区別する
- persistent queueの設定を変えたら、`queue.max_bytes` の全パイプライン合計と
  `path.queue` の空き容量を並べて報告する
- `queue.max_bytes` は `queue.page_capacity` 以上にする

レビュー用のサブエージェントに分ける場合も、この規約を引き継ぎます。観点別にサブエージェントを分けるやり方は、design docのレビュー手順が参考になります。差分を観点ごとに点検する考え方は、helm diffでアップグレード前の差分を見る手順とも共通しています。

レビュー依頼の出し方と、返してもらう形

フックとスクリプトが機械的な検査を受け持つので、Claudeへの依頼は「読んで判断する部分」に絞れます。たとえば「pipelines.ymlとlogstash.ymlを読み、パイプラインごとのqueue.max_bytesの合計を出してください。path.queueのあるディスクの空きと並べ、足りない場合は理由と一緒に報告してください」のように、観点と出力の形を一緒に指定します。

返してもらう形も決めておくと、人の確認が速くなります。おすすめは次の3点です。

  • 指摘ごとに、ファイル名・設定キー・現在の値を並べる
  • 公式の既定値や制約から言えることと、流量などの実運用を知らないと判断できないことを分ける
  • 直した場合は、直したあとにフックとサンプルログの検査をもう一度通した結果を添える

2点目が特に効きます。たとえばqueue.max_bytes: 8gbは、公式の既定値と比べて大きいというだけでは良し悪しを決められません。ディスクの容量とイベントの流量が分かって初めて判断できる値です。Claudeが勝手に妥当と書かないよう、「確認に必要な情報」を指摘の末尾に書かせます。

よくあるつまずき

  • フックが失敗してもClaudeが気づかない: スクリプトがexit 1で終わっています。Claudeに標準エラー出力を渡すにはexit 2と標準エラー出力の組み合わせにします
  • フックが全ファイルで動いてしまう: matcherはツール名にしか効きません。パスの絞り込みはスクリプト冒頭のcaseで行います
  • -tは通るのにログがマッチしない: 構文だけを見るフラグなので、grokのパターンは検査されません。サンプルログのスクリプトで補います
  • サンプルログが実態とずれる: ログ形式を変えたのにサンプルを更新していないと、検査が通り続けます。形式の変更と同じ差分でサンプルも更新させます
  • フックが遅くて編集が詰まる: timeoutを短くしすぎると打ち切られます。logstashの起動には時間がかかるため、数十秒から数分の余裕を見ます
  • Elasticsearchに繋いで実データも見たい: 検索や集計はMCP経由で行えます。接続手順はElasticsearch MCPサーバーの使い方にあります

まとめ

logstashの構文エラーは、PostToolUseフックとlogstash -tの組み合わせで、編集のたびに潰せます。ただし-tはgrokのパターンを見ないため、サンプルログを通して_grokparsefailureを数える検査が要ります。persistent queueは設定値より、全パイプライン合計のディスク容量と入力プラグインの種類のほうが、事故の原因になりやすい領域です。この3つをCLAUDE.mdの規約とスクリプトに分けて持たせると、レビューのたびに観点を書き直さずに済みます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/hooks
  2. elastic.co/guide/en/logstash/current/running-logstash-command-line.ht…
  3. elastic.co/guide/en/logstash/current/plugins-filters-grok.html
  4. elastic.co/guide/en/logstash/current/persistent-queues.html
この記事を共有:XはてブLinkedIn