インフラ

構造化ログとは|JSONでログを機械可読にする仕組みとslog/structlog実装・オブザーバビリティでの使いどころを実装者目線で解説

構造化ログ(structured logging)とは、ログを人が読むための文章としてではなく、JSONのようなキー・バリュー形式で出力し、タイムスタンプ・ログレベル・メッセージ・トレースIDといった要素を個別のフィールドとして機械が検索・集計できるようにする記録手法です。この記事で扱うのは、非構造化ログとの具体的な違いと共通フィールドの設計、Goのslog(log/slogパッケージ・Go 1.21で標準ライブラリ入り)やPythonのstructlogでの実装、OpenTelemetryのログデータモデルによるトレースとの相関、rsyslogやログ基盤での収集・検索、そして導入すべき場面と過剰になる場面の見極めです。単に「JSONで吐く」で終わらせず、障害調査で効くログ設計まで落とし込みます。バージョンや日付は2026年7月時点の公式ドキュメントを基準にします。

まとめ:構造化ログはログを機械可読なフィールドにして検索・相関を効かせる記録手法

先に結論を示します。構造化ログの本質は、1行のログを「意味のわからない文字列」から「フィールドが定義されたデータ」へ変えることにあります。2026-07-26 12:00:00 ERROR user 42 login failedのような文章を、{"time":"...","level":"ERROR","msg":"login failed","user_id":42}というJSONオブジェクトにする、というのが実体です。こうするとログ基盤側でlevel=ERROR かつ user_id=42のように条件を指定して絞り込め、grepと正規表現でパースする作業から解放されます。

実装は難しくありません。Goなら標準ライブラリのslog、PythonならstructlogやloggingのJSONフォーマッタで、数行の初期設定を書くだけで済みます。効きどころは、複数のサービスにまたがる分散システムと、障害調査の頻度が高い運用です。ログにtrace_idを載せてOpenTelemetryのトレースと突き合わせれば、1リクエストの流れを横断で追えます。逆に、単一プロセスで動く小さなツールや、ログをほとんど見ない用途では、JSON化の手間が見合わないこともあります。導入判断は規模と調査頻度で決めるのが現実的です。

構造化ログの基本|非構造化ログとの違いと機械可読なフィールド設計

構造化ログを理解する近道は、「ログを文章ではなくデータとして扱う」という一点をつかむことです。まずは非構造化ログとの対比から入ります。

構造化ログとは何か|キー・バリューで意味づけされたログレコード

構造化ログは、1つのログを「キーと値の組み合わせ」として表現する記録手法です。従来のprintf的なログがユーザーaliceがAPIに3回アクセスしましたという一続きの文だったのに対し、構造化ログでは{"user":"alice","count":3}のように、それぞれの情報が名前のついたフィールドに分かれます。フォーマットはJSONが主流ですが、logfmt(key=valueを空白区切りで並べる形式)を使う現場もあります。どちらも共通するのは、ログを読むのが人だけでなく機械でもある、という前提に立っている点です。フィールドが定義されていれば、ログ基盤は値の型を保ったまま索引を張り、後から任意の条件で問い合わせられます。

非構造化ログとの違い|grep探索から検索・集計前提への転換

非構造化ログと構造化ログの差は、運用時の「探し方」に最も表れます。非構造化ログでは、障害時にサーバーへSSHで入り、grepと正規表現でパターンを組み立てて目的の行を探すのが定番でした。この方式は、ログの書式が少しでも揺れると正規表現が壊れ、複数フィールドをまたぐ集計(例:ユーザーごとのエラー率)はほぼ手作業になります。構造化ログが変えるのはこの点です。フィールドが機械可読なので、ログ基盤側でstatus_code >= 500duration_ms > 1000のような条件検索、フィールド単位の集計・可視化が成り立ちます。

観点 非構造化ログ(プレーンテキスト) 構造化ログ(JSON/logfmt)
形式 自由な文章の1行 キー・バリューのレコード
探し方 grep+正規表現 フィールド条件で検索・集計
集計 手作業・スクリプト頼み フィールド単位で自動集計
可読性(人) そのまま読みやすい 整形しないと読みにくい
相関 ID埋め込みでも突合が手間 trace_idで横断追跡が容易

人の目で1行を読む快適さでは非構造化ログに分があります。開発中にターミナルで眺めるだけなら、色付きのテキストログの方が速いこともあります。差が逆転するのは、ログ量が増え、複数サービスを横断して障害を追う段階です。

標準フィールド設計|timestamp・level・trace_idの共通化

構造化ログで最初に決めるのは、全サービスで共通させる基本フィールドです。最低限そろえたいのは、発生時刻(timestamp)、深刻度(level)、人が読む要約(message)の3つで、分散システムではこれにtrace_idspan_idを加えます。フィールド名を各サービスでばらばらにすると、ログ基盤で串刺し検索するときにlevelseveritylvlが混在して破綻します。命名を統一する土台としては、Elastic Common Schema(ECS)やOpenTelemetryのログデータモデルのように既存のスキーマに寄せる方針が堅実です。加えて、リクエスト単位で意味を持つ属性(user_idhttp.methodstatus_codeなど)を載せると、後の絞り込みが効きます。ここで決めた共通フィールドが、そのままログ基盤での検索性を左右します。

構造化ログの実装|Goのslog・Pythonのstructlog・出力先の設計

構造化ログは、専用のライブラリを入れなくても始められる言語が増えています。ここでは実装の入り口を、GoとPythonの具体で押さえます。

Goのlog/slog|標準ライブラリのJSONHandlerで出力する

Goでは、標準ライブラリのslog(log/slogパッケージ)が構造化ログの基点です。slogはGo 1.21(2023年8月)で標準ライブラリに追加され、外部依存なしにJSON構造化ログを出力できます。使い方はシンプルで、slog.New(slog.NewJSONHandler(os.Stdout, nil))でロガーを作り、logger.Info("login failed", "user_id", 42)のように呼ぶと、{"time":"...","level":"INFO","msg":"login failed","user_id":42}という1行JSONが出ます。標準キーはtimelevelmsgです。設計は3層に分かれ、LoggerRecord(Time/Level/Message/Attrs)を作り、それをHandlerJSONHandlerTextHandler)が受け取ってフォーマットします。slog.String("user","alice")slog.Group("request", ...)で型を保った属性を付けられ、WithAttrsで共通属性をあらかじめロガーに埋め込めます。開発中はTextHandler、本番はJSONHandlerと切り替える構成が定番です。

Pythonのstructlog・logging|JSONフォーマッタで構造化する

Pythonでは、標準ライブラリのloggingにJSON用のフォーマッタを差すか、専用ライブラリのstructlogを使う2通りが実務的です。loggingだけで進める場合は、logging.Formatterを継承してレコードをJSON文字列にするフォーマッタを書き、ハンドラに設定します。structlogを使うと、プロセッサ(処理を連結するパイプライン)でタイムスタンプ付与・ログレベル整形・JSONレンダリングを組み立てられ、logger.info("login failed", user_id=42)のようにキーワード引数でそのままフィールドを渡せます。既存のloggingと連携でき、標準ロガー経由のログもまとめて構造化できる点が実装上の利点です。どちらを採るかは、既存コードがloggingに深く依存しているならlogging+JSONフォーマッタ、新規で書き味を優先するならstructlog、と分けるのが素直です。

ログ出力先の設計|標準出力へ1行JSONを流すstdout方式

実装で見落とされやすいのが、ログをどこへ吐くかです。コンテナ環境では、アプリケーションはファイルではなく標準出力(stdout)へ1行JSONを流し、収集はプラットフォーム側に任せる方式が基本になります。これはThe Twelve-Factor Appが示す「ログはイベントストリームとして扱い、出力先の管理はアプリの責務にしない」という考え方に沿います。アプリはひたすらstdoutにJSONを書き、Kubernetesならノードのログエージェント、ホスト運用ならrsyslogやFluent Bitが受け取って基盤へ転送する、という分担です。ここでファイルローテーションやバッファリングをアプリに抱え込ませると、責務が肥大化します。コンテナ上でのログとメトリクスの設計をまとめて考える段階では、Kubernetes監視・モニタリングの設計の解説が出力設計の前提を補います。

構造化ログとオブザーバビリティ|トレース相関とログ基盤での検索

構造化ログの価値が最大化するのは、単体で見るときではなく、トレースやメトリクスと突き合わせるときです。オブザーバビリティの文脈で位置づけます。

OpenTelemetryログデータモデル|trace_idでトレースと相関

ログを「探せるデータ」にした次の一手が、トレースとの相関です。OpenTelemetryのログデータモデルは、ログレコードをTimestampObservedTimestampSeverityTextSeverityNumber(1〜24の数値で表し、大きいほど深刻。17〜20がERROR帯)、Body(文章または構造化データ)、Attributes、そしてTraceIdSpanIdTraceFlagsで表現します。TraceIdSpanIdはW3C Trace Contextに準拠し、これをログに載せておくと、分散トレースのどのスパンで出たログかを機械的に結びつけられます。障害調査で「このエラーログは、どのリクエストの、どの処理の途中で出たのか」を一発でたどれる状態です。ログとトレースを同じIDで束ねる計装の全体像はOpenTelemetryの3つのシグナルとCollectorの解説で整理しています。構造化ログは、この相関を成り立たせるための土台にあたります。

ログ収集・転送|rsyslog・エージェントからログ基盤への転送

アプリがstdoutへJSONを吐いた後は、収集・転送のレイヤが受け持ちます。ホスト上ではrsyslogやsyslog-ng、Fluent Bit/Fluentdといったエージェントがログを拾い、フィルタリングや軽い加工をかけてログ基盤へ送る流れです。構造化ログであれば、この段階でJSONをそのままフィールド付きで流せるため、転送先でのパースが軽くなります。逆に非構造化ログのままだと、収集エージェント側でGrok(正規表現の名前付きパターン)を書いて無理やり構造に起こす工程が挟まり、書式変更のたびに壊れがちです。Linuxでのログ収集・転送の仕組みと設定はrsyslogのログ収集・転送とrsyslog.conf設定の解説にまとめており、構造化ログの出口設計と合わせて読むと収集経路が具体化します。

ログ基盤での検索・集計|フィールド索引を効かせた条件調査と可視化

転送先のログ基盤(Elasticsearch や Loki、クラウドのマネージドサービスなど)では、構造化ログのフィールドに索引が張られ、条件検索と集計が成り立ちます。たとえばAWSのCloudWatch Logsでは、JSON形式で出力したログをCloudWatch Logs Insightsでfieldsfilterstatsのクエリにかけ、status_code=500の件数を時系列で集計する、といった調査が可能です。ここが構造化ログの投資回収点で、非構造化ログでは手作業だった集計が、保存済みクエリとダッシュボードに置き換わります。クラウド上でログ・メトリクス・トレースを1つの基盤に集める構成はAWSオブザーバビリティの実装(ADOT・CloudWatch)の解説で扱っており、構造化ログはその検索性を支える入力データです。ログを含む3種類の監視データを束ねて性能を追う全体像はAPM(アプリケーション性能監視)の仕組みと導入判断の解説が判断ハブになります。

構造化ログを導入すべき場面と過剰になる場面(規模別の採用判断)

ここは独自の観点で判断を言い切ります。構造化ログは万能ではなく、規模と調査頻度によっては素のテキストログで足りる場面もあります。

構造化ログを導入すべき場面|分散システムと障害調査が多い運用

構造化ログに倒すべきなのは、次の条件が出たときです。第一に、マイクロサービスやコンテナで複数プロセスが動き、1つのリクエストが複数サービスをまたぐ分散システム。trace_idで横断追跡できる利得が、そのままフォーマット変更のコストを上回ります。第二に、本番障害の調査が定期的に発生し、ログをフィールドで絞り込みたい運用。第三に、ログを機械で監視し、特定条件でアラートを出したい場合です。これらは要件が明確なので、該当するなら初期設計の段階から構造化ログとフィールド命名規約を決めておくのが得です。後から全ログを構造化し直すのは、命名の統一まで含めると手戻りが大きくなります。

構造化ログが過剰になる場面|単一プロセスでログをほぼ見ないとき

逆に、構造化ログが過剰投資になる場面もあります。単一プロセスで完結する小さなCLIツールやバッチで、開発者が手元のターミナルでログを目視するだけなら、色付きのテキストログの方が速く読め、JSON化はかえって視認性を落とす場面です。ログをほとんど見返さない用途や、運用フェーズに入っていない検証段階のコードでも、フィールド設計に時間をかける価値は薄くなります。現実的な折衷は、開発時はテキスト(GoならTextHandler)、本番はJSON、と出力を環境で切り替える構成です。これなら人の可読性と機械可読性を両取りできます。ログ基盤やオブザーバビリティ環境をどこまで作り込むか、内製と外注のどちらで運用体制を組むかで迷う段階なら、ログ設計から監視基盤の運用・内製化支援まで保守運用・内製化支援の受託で相談を受け付けています。判断の起点は、あくまで「そのログを機械で検索・集計・相関する必要があるか」に置くのが筋です。

構造化ログの実装・JSON化・移行・メリットに関するよくある質問

構造化ログの実装方法やJSONとの関係、導入の得失など、実務でよく挙がる疑問に答えます。

構造化ログとJSONログの違いは何ですか?

構造化ログは「ログをキー・バリューのフィールドで表現する」という考え方の総称で、JSONログはその表現形式の1つです。構造化ログのフォーマットにはJSONのほか、key=valueを並べるlogfmtもあります。実務ではJSONが主流のため、構造化ログとJSONログがほぼ同義で使われる場面も多いですが、厳密にはJSONは実現手段の1つ、という関係です。要点は形式そのものより、フィールドが機械可読で検索・集計できるかどうかにあります。

構造化ログのデメリットは何ですか?

主なデメリットは2つです。1つは人の可読性で、JSONの1行は整形しないと目で追いにくく、ターミナルで直接読むにはビューアやログ基盤の整形機能が要ります。もう1つはフィールド設計のコストで、命名規約を決めずに各サービスが好き勝手なキーを使うと、後で串刺し検索が破綻しかねません。加えて、JSONシリアライズのぶんだけログ出力の処理量はわずかに増えます。いずれも、開発時はテキスト出力に切り替える、共通スキーマ(ECSやOpenTelemetry)に寄せる、といった対処で実務上は吸収できます。

既存のテキストログから構造化ログへどう移行しますか?

一度に全部を書き換えず、出力の入り口から段階的に進めるのが安全です。まずロギングライブラリを構造化対応のもの(Goならslog、Pythonならstructlogやlogging+JSONフォーマッタ)に差し替え、新規のログからtimestamplevelmessageの共通フィールドで出します。既存のテキストログが残る間は、収集エージェント側でGrokパターンを使ってフィールドに起こし、順次アプリ側の出力を構造化へ寄せる形が現実的です。フィールド命名規約を先に決めておくと、移行後の串刺し検索が成り立ちます。

構造化ログにはどんなフィールドを含めるべきですか?

最低限は、発生時刻(timestamp)、深刻度(level)、人が読む要約(message)の3つです。分散システムではtrace_idspan_idを加え、トレースと相関できるようにします。さらにリクエスト単位で意味を持つ属性として、user_idhttp.methodpathstatus_codeduration_msなどを載せると、後の絞り込みと集計が効きます。フィールド名はECSやOpenTelemetryのスキーマに寄せ、全サービスで統一しておくのが調査を楽にする鍵です。

構造化ログはパフォーマンスに影響しますか?

JSONへのシリアライズが加わるぶん、テキスト出力よりわずかに処理量は増えますが、多くのアプリでは支配的なコストにはなりません。影響が出やすいのは、極端に高頻度なログをホットパスで大量に出す場合です。対策としては、出力するログレベルを本番でINFO以上に絞る、デバッグログをサンプリングする、ログ出力を非同期にする、といった手が定石です。ログ量そのものを絞る設計が効き、フォーマットがJSONかテキストかによる差は、通常その次の論点になります。

関連記事

資料請求

RELATED POSTS 関連記事