log-analyzer について
サービスやエージェント実行環境のログを分析する Python スキルです。繰り返し発生するエラーをまとめ、構造化された異常レポートを生成するため、ログ調査や定期ダイジェストに適しています。
主な機能
- 01反復エラーの正規化と分類
- 02レート制限・タイムアウト・ツール失敗の検出
- 03コンポーネント別のエラー集計
- 04JSON・Markdownレポートの生成
- 05定期スキャン向けの無出力モード
活用シーン
- 01エージェント実行時の問題調査
- 02定期ジョブのログ問題の要約
- 03デプロイ後のエラー変化の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ moonlight-lupin/ agent-skills --skill log-analyzerインストール後、Claude Code で/log-analyzerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/anomaly-types.mdhermes-profile-audit.mdlog-formats.mdstate-failure-monitor.mdscripts/analyze_logs.pyhermes_log_window.pystate_failures.pytests/test_analyze_logs.pytest_state_failures.py詳細
ログアナライザー
概要
ログビューアーは行をフィルタリングします。このスキルはパターンを検出します。
エージェントのランタイム、ツールのプロセス、ゲートウェイ、スケジューラー、その他のサービスによって十分な量のログが出力され、個々の grep の結果だけではシステムの動作を説明できなくなったときに使用します。アナライザーは標準的なログ行を解析し、繰り返されるメッセージを正規化して、コンポーネント別およびツール別にエラーをグループ化し、デバッグセッションや cron のダイジェストに適した構造化された異常レポートを作成します。
このスクリプトは軽量で移植性に優れるよう意図されています。Python の標準ライブラリのみを使用し、タイムスタンプ、レベル、省略可能なコンポーネント、メッセージの各フィールドを含むテキストログであれば動作します。また、構造化されていないログに対しても、ベストエフォートのフォールバックを備えています。
クイックスタート
cd agent-ops/log-analyzer
python scripts/analyze_logs.py scan --log-file agent.log --since 24h
Hermes プロファイルのセッション DB からツールごとの失敗率を取得する場合(コンテンツに対する正規表現ではなく、構造化された exit_code シグナルを使用します):
python scripts/state_failures.py # last 7 days, dashboard
python scripts/state_failures.py --days 30 --json
python scripts/state_failures.py --quiet # cron: silent when healthy
由来(hermes-dojo から着想を得て適応)、構造化シグナルが重要である理由を示す急増の証拠、および解釈に関する注記については、references/state-failure-monitor.md を参照してください。
後で Markdown に変換できるように JSON を書き出すには:
python scripts/analyze_logs.py scan --log-file agent.log --since 24h --output scan.json
python scripts/analyze_logs.py report --scan scan.json --output report.md
cron と互換性のある異常検出を行うには:
python scripts/analyze_logs.py scan --log-file agent.log --since 24h --quiet
--quiet は、異常が見つからない場合、何も出力せずに 0 で終了します。異常がある場合は JSON レポートを出力し、スケジューラーがダイジェストを配信できるようにします。
検出対象
- エラーのクラスタ — 選択した時間枠内で、正規化された同一のエラーメッセージが 3 回以上繰り返されている状態です。グループ化の前に URL、IP アドレス、数値をプレースホルダーに置き換えるため、ID が変わっても繰り返される失敗をクラスタ化できます。
- レート制限への到達 — HTTP
429、rate limit、rate_limit、too many requests、quota exceededのパターンです。プロバイダー名を検出できる場合、アナライザーはプロバイダーごとにグループ化します。 - タイムアウトのパターン —
timeout、timed out、deadline exceeded、connection timeoutです。検出されたツール名ごとに結果をグループ化し、URL がある場合は例として含めます。 - ツールの失敗 —
tool: terminal、tool_call: web_search、tool terminal failedのようなパターンから抽出したツール名ごとに、エラー行をグループ化します。また、toolsコンポーネントが出力した行も対象です。 - セッションのクラッシュ — 致命的なエラー、未処理の例外、スタックトレース、
Traceback、Exception、segfaultのマーカーです。複数行にわたるスタックトレースは、前後のコンテキストを含めて 1 件のクラッシュとしてグループ化します。 - コンポーネント別の内訳 —
gateway、agent、tools、cron、unknownなどのコンポーネントごとのエラー数と警告数です。 - エラーのタイムライン — エラー数を時間単位で集計し、デプロイやスケジュール済みジョブの後に発生する急増やリグレッションを明らかにします。
検出基準と解釈については、references/anomaly-types.md を参照してください。
ログ形式のサポート
パーサーは、次の形式の標準的なログ行に対応しています。
2026-07-06 12:30:45 ERROR [gateway] Connection refused
2026-07-06T12:30:45Z ERROR gateway: Connection refused
12:30:45 ERROR Connection refused
ERROR、WARN、WARNING、INFO、DEBUG、FATAL、CRITICAL の各レベルを認識します。コンポーネントは、レベルの後に角括弧で記述するか、レベルの後に component: として記述できます。時刻のみの行は現在の日付(ライブラリとして呼び出された場合は、指定されたデフォルトの日付)に対応付けられます。構造化されていない行は、ベストエフォートで行ごとにスキャンするため、タイムスタンプを解析できない場合でも、明らかな ERROR/WARN の文字列は引き続きカウントされます。
例やカスタムパターンの追加方法については、references/log-formats.md を参照してください。
CLI コマンド
scan — ログファイルをパターン分析する
python scripts/analyze_logs.py scan --log-file LOGFILE [--since TIME] [--output report.json] [--quiet]
オプション:
--log-file LOGFILE— ログファイルへの必須パスです。--since TIME— 省略可能な時間枠です。分/時間/日/週で指定します(例:30m、1h、24h、7d、2w)。デフォルトではすべての行が対象です。--output report.json— 標準出力の代わりに、JSON をファイルに書き込みます。--quiet— cron モードです。異常が見つからない場合は出力を抑制します。
report — スキャン結果の JSON から Markdown を生成する
python scripts/analyze_logs.py report --scan scan.json [--output report.md]
レポートには、概要の件数、異常の種類ごとのセクション、コンポーネント別の内訳表、時間単位のエラータイムラインが含まれます。
tail — 最近の行をスマートに表示
python scripts/analyze_logs.py tail --log-file LOGFILE [--lines N] [--since TIME]
スマートテールは、チャットへの配信に適したマーカー付きのプレーンテキストを出力します。
- 通常の行:マーカーなし
- 警告:
⚠️ - エラー:
❌ - 最近繰り返し発生したエラー:正規化された同じエラーが3回以上検出された場合は
🔥
出力形式
scan は、次のトップレベルフィールドを含むJSONを出力します。
log_file、lines_analyzed、time_windowanomalies.error_clusters、anomalies.rate_limits、anomalies.timeouts、anomalies.tool_failures、anomalies.crashescomponent_breakdownerror_timelinetotal_errors、total_warnings、has_anomalies
report は、そのJSONをMarkdownに変換します。
# Log Analysis Report
## Overview
- Log file: agent.log
- Lines analyzed: 1542
- Time window: 24h
- Total errors: 12 | warnings: 20
- Anomalies detected: 5 types
スケジュールされたサマリーとの連携
スケジュールされたダイジェストでは、scanを静音モードで実行し、出力が空でない場合にのみ含めます。
python scripts/analyze_logs.py scan --log-file /var/log/agent.log --since 24h --quiet --output /tmp/log-scan.json
if [ -s /tmp/log-scan.json ]; then
python scripts/analyze_logs.py report --scan /tmp/log-scan.json
fi
scheduled-summaryジョブでは、Markdown出力を「ログの異常」という見出しの下に追加できます。カウントの重複や消失を防ぐため、分析期間をサマリーの期間に合わせてください(たとえば、日次ダイジェストなら24時間)。
state.dbの失敗率ダイジェストの場合(セッション数を考慮すると、週次が妥当な頻度です)。
python scripts/state_failures.py --quiet --days 7
このコマンドは、期間内の失敗がゼロの場合、何も出力せずに終了コード0で終了します。そのため、問題がある場合にのみ通知するno_agent cronジョブとして使用できます。各カテゴリの意味については、references/state-failure-monitor.mdを参照してください(実際のデータでは、タイムアウトが最も対処につながりやすい項目です)。
よくある落とし穴
- ログローテーションによって時間枠がずれます。 昨日のファイルがローテーションで置き換えられている場合、現在のファイルだけを対象にした
--since 24hのスキャンでは、時間枠の初期に発生した障害を見落とすことがあります。 スケジューラーの対象に稼働中のファイルとローテーション済みファイルの両方を指定するか、スキャン前に 該当ファイルを連結してください。 - 複数行のスタックトレースには先頭行が必要です。 パーサーは、インデントされた
スタックトレースの継続行を、直前に解析したログ行の下にまとめます。ログコレクターが最初の
TracebackまたはERROR行を削除すると、残りのスタックフレームは構造化されていないコンテキストになります。 - 想定内のエラーが誤検知になることがあります。 リトライ、プローブ、ヘルスチェックによって、意図的に警告や接続失敗が出力される場合があります。クラスターは自動的にインシデントと判断せず、「調査が必要」な兆候として扱ってください。
- 大きな過去ログを初回にスキャンすると、出力が膨大になることがあります。 まず
--since 24hで開始し、レポートを確認してから、必要に応じて時間枠を広げてください。 - メッセージ形式を変更すると、クラスターが分かれることがあります。 デプロイ中にアプリケーションのエラー文字列が変わると、根本原因が同じでも、デプロイ前後の障害が別々のクラスターとして表示されることがあります。
- 時刻のみのログはスキャン日付に依存します。
12:30:45 ERROR ...の行には日付が含まれていません。過去のファイルには完全なタイムスタンプを使用してください。 - ミリ秒をカンマで区切るPythonロギング形式の行では、
--sinceが機能しないことがあります。2026-07-06 12:30:45,123 INFO module: ...の形式の行(Hermes のエージェント/ゲートウェイ/エラーログ)は、組み込みのタイムスタンプ正規表現に一致しません。これらの行はタイムスタンプがnullの非構造化フォールバックに回るため、--sinceで絞り込めず、ローテーションされていないファイル全体がスキャンされます。症状として、エラー数やトレースバック数が実際の時間枠内の数を大幅に上回る、すべてのクラスターで「First: unknown / Last: unknown」と表示される、エラータイムラインに「No timestamped errors detected」と表示される、クラッシュ数が膨らむ(スタックトレースの継続行がそれぞれ個別のクラッシュとして数えられる)ことがあります。対処法: Hermes のプロファイルログでは、時間枠に対応したカウンターscripts/hermes_log_window.pyを使用してください(カンマ区切りのミリ秒形式に対応し、Traceback (most recent call last)の先頭行のみを数えることで複数行のトレースバックを重複排除します)。プロファイルのstate.dbからスキルの使用数やツール呼び出し数を取得する方法については、references/hermes-profile-audit.mdを参照してください。 - スキルの使用数は agent.log に含まれていません。
tool skill_view completed (0.05s, 13890 chars)の行にはスキル名の引数が含まれていません。プロファイルのstate.dbにあるmessagesテーブルを照会してください。tool_callsJSON 列に完全な引数が格納されています。messages.timestampは ISO 形式ではなく、Unix エポック(REAL)です。すぐに実行できるスニペットについては、references/hermes-profile-audit.mdを参照してください。 - エラータイムラインは日付ではなく時刻で集計します。 複数日の時間枠(
--since 7d)では、異なる日の 08:00 に発生したエラーが、1 つの08:00バーにまとめられます。複数日にわたるフォレンジックには、完全なタイムスタンプを保持するクラスターを使用し、タイムラインは時刻別の傾向として扱ってください。 - クラッシュ検出は部分文字列に基づきます。 クラッシュの正規表現は行内のどこにあっても
fatalやExceptionなどの語句に一致するため、INFO 行内の言及(例:「TimeoutException の後にリトライが成功した」「致命的ではない警告」)もクラッシュの兆候として数えられ、has_anomaliesが設定されることがあります。クラッシュ数は目視確認の手がかりとして扱い、INFO レベルで例外について通常どおり記録するログでは、正規表現を調整してください。
このスキルでできないこと
- ログビューアーではありません。生のログ行を確認するには、
tail、less、またはログ UI を使用してください。 - ログシッパーではありません。ログをストレージや可観測性システムに転送することはありません。
- SIEM ではありません。ID、ネットワーク、セキュリティイベントを相関分析しません。
- リアルタイム監視ではありません。
tailは直近の行を分析するものであり、デーモンやアラートサービスではありません。 - 根本原因を特定する万能ツールではありません。パターンを示し、エージェントや運用担当者がより迅速に調査できるようにします。
検証チェックリスト
-
python scripts/analyze_logs.py --helpを実行し、サブコマンドが読み込まれることを確認します。 - エラーが繰り返し発生する合成ログをスキャンし、
has_anomaliesが true になることを確認します。 - スキャン JSON から Markdown をレンダリングし、すべての異常セクションが表示されることを確認します。
- このスキルのディレクトリから
python -m pytest tests/test_analyze_logs.py -vを実行します。 - このスキルのディレクトリから
python -m pytest tests/test_state_failures.py -vを実行します(state.db の失敗率モニター用です。急増に関するリグレッションテスト — エラーを表す単語に言及するだけのコンテンツは失敗としてカウントしてはならない、というテスト — はここにあります)。 - cron で使用する場合、
--quietを指定して異常のないログをテストし、stdout が空で終了コードが 0 になることを確認します。 - state.db を cron で使用する場合、稼働中のプロファイル DB に対して
python scripts/state_failures.py --quietを実行し、対象期間に問題がなければ終了コードが 0 で、何も出力されないことを確認します。