Skip to content

ERROR 以上のログに分類と dedup 用の fingerprint を任意で付けられるようにする (#18) - #25

Merged
akm merged 7 commits into
mainfrom
features/error-fingerprint-18
Sep 10, 2026
Merged

akm merged 7 commits into
mainfrom
features/error-fingerprint-18

Conversation

@akm

@akm akm commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

概要

Issue #18 の提案 1 + 3 を実装します。base は #24 のブランチです (#19 → #23 → #24 → 本 PR の順にマージしてください。前の PR がマージされると base は自動で切り替わります)。

使い方

Initializer(error_event="app_error", surface="worker")
logger.error("order %s not found", 123, extra={"error_type": "validation", "operation": "orders.get"})
# {"severity": "ERROR", "event": "app_error", "error_type": "validation",
#  "operation": "orders.get", "fingerprint": "…", ...}

error_event を指定したときだけ、JSON 形式で severity ERROR 以上のログに次を付けます (既定では付けません)。

フィールド 内容
event error_event の固定値
error_type extra の分類。未指定なら unknown
operation extra の操作名。未指定ならロガー名
fingerprint 同種のエラーで同じ値になる 16 文字のハッシュ

extra で event / fingerprint を明示した場合は上書きしません。

設計上の判断

  • 付与は formatter で行います (提案 2 の Filter 方式ではなく)。Filter の中で record.getMessage() を呼ぶと書式ミスが呼び出し側の例外になりますが、formatter なら Handler.handleError で扱われるためです。
  • fingerprint の規則は他言語の実装との契約として gnlog.fingerprint の docstring と README に明記しました。
    1. UUID → <uuid>、引用文字列 (改行を含まない) → <str>、単語境界の数値 → <num> の順に置き換え
    2. 先頭 300 文字に切り詰め
    3. surface|operation|error_type|正規化メッセージ の UTF-8 SHA-1 の先頭 16 文字
  • normalize_message() / build_fingerprint() を公開関数として提供します。テストには他言語の実装と突き合わせるための参照値を 1 件入れています。
  • surface は labels と別の専用引数にしました (labels は Cloud Logging 上の表示用で、fingerprint の入力に流用すると値の変更が dedup キーに影響するため)。

レビュー周回の記録

マージ前に review-triage-loop (code-review / Opus 5 / effort high) を回しました。記録はリポジトリにはコミットしていません。全量の基点は base ブランチ (#24) の先端です。

回 範囲 採択 保留 却下
1 全量 4 1 0
2 増分 1 0 0
3 増分 2 0 0
4 増分 0 0 0

採択した指摘と修正は次のとおりです。回 1 の修正で fingerprint の規則 (他言語実装との契約) が変わっています。 現在の規則は README の「fingerprint の規則」を参照してください。

  • 正規化で can't / won't のアポストロフィを引用符と誤認し、別種のエラーが同じ fingerprint になっていた。桁区切り (1,234) や指数表記 (1.5e10) の数値が部分的にしか置き換わらず、同種のエラーがまとまらなかった。正規表現を直し、\b と \d は ASCII に限定した。
  • 連結の区切り文字 | を値から escape していなかったため、別の組み合わせが同じ fingerprint になりえた。\ と | を escape してから連結する。
  • extra で文字列以外の operation / error_type を渡すと、JSON の出力と fingerprint の入力が別の表現になっていた。同じ文字列を使う。
  • アポストロフィ除外の修正で足した閉じ引用符側の lookahead が 'bob's のような所有格を除外する退行を起こしたので、lookahead を外した。
  • 単一引用符は引用の区切りとアポストロフィを同じ文字で兼ねるため、判定の境界をどこに置いても反例が作れます。回 3 の指摘 (アポストロフィで始まる語や対になっていない引用符の後ろが <str> になる) は、挙動を変えずに限界として規則に明記する判断にしました。
  • 正規表現のエスケープが重なって読みにくかったので、引用符を定数にして f-string で組み立てるリファクタを別コミットで行いました (pattern は同一)。

保留 1 件 (切り詰め「300 文字」の単位が Python と JavaScript で違う) は #26 に先送りしています。修正後の最終確認の全量レビューは行っていません。

確認

ruff (check / format)、mypy、pytest (107 件) がすべて成功します。

Closes #18

🤖 Generated with Claude Code

@akm
akm force-pushed the features/error-fingerprint-18 branch from 1c78ef6 to e8d54e4 Compare September 10, 2026 01:32
@akm
akm force-pushed the features/error-fingerprint-18 branch from e8d54e4 to 3cea007 Compare September 10, 2026 01:34
Base automatically changed from features/log-context-15 to main September 10, 2026 01:34
akm and others added 7 commits September 10, 2026 10:34
Cloud Error Reporting のグループ化は stack_trace のある ERROR にしか効かず、
例外を伴わない ERROR やメッセージに可変部の多いエラーは同種のものが
散らばる。利用側ごとに formatter を持つのではなく、py-gn-log の任意機能
(既定では付けない) として提供する。

- gnlog.fingerprint: normalize_message() (UUID / 引用文字列 / 数値を
  <uuid> / <str> / <num> に置き換えて 300 文字に切り詰める) と
  build_fingerprint() (surface|operation|error_type|正規化メッセージ の
  UTF-8 SHA-1 先頭 16 文字)。規則は他言語の実装との契約として README に明記
- JsonFormatter / Initializer に error_event と surface を追加。指定時のみ
  ERROR 以上に event / error_type (既定 unknown) / operation (既定ロガー名) /
  fingerprint を付ける。extra で明示された値は上書きしない
- 付与は Filter ではなく formatter で行う (getMessage() の書式ミスが
  Handler.handleError で扱われるようにするため)

Closes #18

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
単一引用符の正規表現が can't / won't のアポストロフィを引用符の開始と
終了とみなし、その間の語句を <str> に置き換えていたため、別種のエラーが
同じ fingerprint になっていた。また数値の正規表現が桁区切りのカンマや
指数部を扱わず、件数が変わるたびに別の fingerprint になっていた。

単一引用符は前後が ASCII の英数字・下線でないときだけ区切りとみなし、
数値は桁区切り・小数部・指数部を含めて 1 つにまとめる。\b と \d は
ASCII に限定して他言語実装と意味を揃える。規則の記述を更新した。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
surface / operation / error_type / メッセージを | で連結する際に値を
escape していなかったため、値に | が含まれると区切り位置が曖昧になり、
別の組み合わせ (worker|orders / create と worker / orders|create) が
同じ fingerprint になっていた。

各要素の \ を \\ に、| を \| に escape してから連結する。規則の記述を更新した。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
extra で文字列以外の operation / error_type を渡すと、JSON には元の値
(配列など) が出力される一方、fingerprint の入力には str() の結果が使われ、
出力された JSON から fingerprint を再計算しても一致しなかった。

値を決めた時点で str() に変換し、出力と hash の入力に同じ文字列を使う。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
アポストロフィを除外する修正で閉じ引用符側にも lookahead を足したため、
user 'bob's account のように閉じ引用符の直後に英数字が続く引用文字列が
置き換えられなくなっていた (修正前は置き換えていたので退行)。

開き引用符側の lookbehind だけでアポストロフィの除外は保たれるので、
lookahead を外す。規則の記述を更新した。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
単一引用符は引用の区切りとアポストロフィを同じ文字で兼ねるため、
判定の境界をどこに置いても反例が作れる。挙動は変えず、アポストロフィで
始まる語や対になっていない単一引用符の後ろが <str> になることと、
厳密さが必要なら二重引用符を使うことを docstring と README に明記する。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
二重引用符と単一引用符のエスケープが重なって、どこが区切りでどこが
内容か読み取りにくかった。区切り文字を DOUBLE_QUOTE / APOSTROPHE の
定数にし、f-string で組み立てる。正規表現の文字列は変わらない
(組み立て後の pattern が同一であることを確認)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@akm
akm force-pushed the features/error-fingerprint-18 branch from 3cea007 to de1fb03 Compare September 10, 2026 01:34
@akm
akm merged commit 338fbd4 into main Sep 10, 2026
1 check passed
@akm
akm deleted the features/error-fingerprint-18 branch September 10, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ERROR 以上のログに分類 (種別 / 操作名) と dedup 用の fingerprint を任意で付けられるようにしてほしい