構造化ログに格納するトレースバックのフォーマット

structlogのドキュメントを読んでいると、 structured tracebacksというものが登場する。 structured logging が良いものなのだから、structured tracebacksも良いものなのだろうか?

API Reference - structlog 24.1.0 documentation

structlog.processors.dict_tracebacks(logger, name, event_dict)

Replace an exc_info field with an exception field containing structured tracebacks suitable for, e.g., JSON output.

これを使うと、次のように1フレーム1 dictの形式でスタックトレースが出力される。

"frames": [{"filename": "/Users/inada-n/notes/structlog-test/traceback_sample.py", "lineno": 17, "name": "<module>", "line": "", "locals": {"__name__": "__main__", "__doc__": "None",....

デフォルトではローカル変数が表示されてしまっているので、ローカルでデバッグするくらいならいいかもしれないが、構造化ログに入れてObservability Platformに送信するにはデカすぎる。

dict_tracebacks の代わりに ExceptionRendererExceptionDictTransformer を使って自分で設定すればローカル変数を表示しないことは可能だが、それでもプレインテキストのトレースバックに比べると大幅に大きくなってしまう。

なぜ構造化ログが使いたかったのかと言えば、SQLライクな構文でログをフィルタリングするのに便利だったり、ログから値を集計するのに便利だったりするからだ。トレースバックの中身まで構造化して、この目的に寄与するとは思えない。と言うことで、個人的に strucutred tracebacks は却下だ。

構造化ログにどのように例外情報を格納するかについては、OpenTelemetryのConventionが今後の標準になっていくだろうから、それに従うのが一番だろう。

opentelemetry.io

"exception.type" に型を、 "exception.message" にメッセージを、そして "exception.traceback" に、(1フレーム1文字列の配列などではなく)ただ1つの文字列としてスタックトレースを書くことが推奨されている。構造化ログのメリットから考えても、型とメッセージをトレースバックと分けて格納する設計には納得できる。

さらにスタックトレースのフォーマットについては "in the natural representation for the language runtime." と書かれていて、言語の標準的なフォーマットを使うことが推奨されている。

Pythonの標準のtracebackがあまり構造化ログに入れてObservability Platformに送信するのに適していない件は先の記事で書いた。とりあえず当面はその記事で言及した PYTHONNODEBUGRANGES=1 環境変数chain=False オプションを使ってトレースバックの肥大化を避けるのがいいだろう。しかしそれだけでは、他の言語の標準的なトレースバックフォーマットが most recet call first なのに対してPythonのものがmost recent call lastなのは解決できてない。Web UIでログの中のトレースバックを見る時にはスクロールが必要になるだろう。

methane.hatenablog.jp

より良いログフォーマットとして、この記事で紹介していた minitraceback も試してみてもらえると嬉しい。ドキュメントはまだないけれど、APIはほぼ標準tracebackのサブセットになっているので使い方には困らないだろう。

将来のことを考えると標準化が重要なので、 disscuss.python.org で標準の traceback モジュールに構造化ログとObservability Platformに適した形式でで出力するためのオプションを追加する提案をしているところだ。(時期的にPython 3.13に間に合わすのは厳しいけれども。)

discuss.python.org

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。