テキストのログと構造化ログ
次のテキストのログには、その下の構造化ログと同じ情報が入っています。message と order_id で絞り込めます。メッセージはどの注文でも同じなので、件数も数えられます。
ログ形式を選ぶ
JSON を使い、1行に1つのオブジェクトを書きます。すべてのサービスで同じフィールド名を使います。次の表に、基本のフィールドと、それに対応する OpenTelemetry のログデータモデルと LogBrew の名前をまとめます。
OpenTelemetry の重大度の数値は1から24までです。
INFO は9、WARN は13、ERROR は17、FATAL は21から始まります。LogBrew は OTLP のログを直接受け付けます。OpenTelemetry のデータを送信するを参照してください。
ロギングのベストプラクティス
メッセージを固定する
"payment failed" と書き、注文 ID はフィールドに入れます。メッセージが固定されていれば、1種類のイベントとしてグループ化、集計、アラート設定ができます。
1つのフィールドに1つの名前を使う
order_id か orderId のどちらかを一度だけ決め、すべてのサービスで使います。http.response.status_code のような一般的なフィールドには、OpenTelemetry のセマンティック規約の名前を使います。
必要な対応に合わせてレベルを選ぶ
error は、操作が失敗したことを示します。warning は、プログラムが回復したことを示します。info は、通常の処理が行われたことを示します。debug は、問題を調べている間だけ使います。
時刻は UTC で書く
2026-11-07T14:21:19.773+00:00 のように、ミリ秒とオフセット付きの RFC 3339 を使います。UTC とミリ秒がないと、異なるタイムゾーンのレコードや同じ秒のレコードが、間違った順序で並びます。
トレース ID とスパン ID を追加する
これらの ID があれば、1件のリクエストのすべてのログを、サービスをまたいで読めます。サービス、環境、リリースは1回だけ設定する
呼び出しごとではなく、起動時に設定します。リリースがないと、どのデプロイが変化を起こしたのかがわかりません。エラーは処理する場所で1回だけ記録する
エラーの種類とメッセージを追加します。同じエラーを各レイヤーでもう一度記録しないでください。シークレットと個人データを含めない
パスワード、トークン、Cookie、リクエストの本文、クエリ文字列付きの完全な URL をログに記録しないでください。ロガーの秘匿機能を使います。ログの秘匿を参照してください。固定のフィールド名を使う
{"ord_1001": "failed"} のように、値をフィールド名に使わないでください。新しい値が出るたびに新しいフィールドが増え、1つのフィルターですべての注文を見つけられなくなります。
本番環境では標準出力に JSON を書く
読みやすい形式は、開発中だけ使います。Python の構造化ログ
標準のlogging モジュールには JSON フォーマッターがありません。次のフォーマッターは、基本のフィールドと extra={"fields": {...}} の値を書き出します。
LogBrewLoggingHandler を追加します。このハンドラーは、メッセージ、レベル、ロガーをフィールドとして送信し、extra のプリミティブ値をメタデータとして送信します。Python logging を参照してください。
Node.js の構造化ログ
Pino はデフォルトで、レコードごとに1行の JSON を書き出します。Winston はwinston.format.json() で JSON を書き出します。どちらも、呼び出し時のフィールドと子ロガーのフィールドを保持します。両方の設定と、Express のログに付くトレース ID については、Node.js のログを参照してください。
