構造化ロギング

このドキュメントでは、構造化ロギングのコンセプトと、ログエントリのペイロード フィールドに構造を追加する方法について説明します。ログ ペイロードが JSON オブジェクトとしてフォーマットされ、そのオブジェクトが jsonPayload フィールドに保存される場合、ログエントリは構造化ログと呼ばれます。これらのログについて、特定の JSON パスを検索するクエリを作成し、ログペイロード内の特定のフィールドのインデックスを作成できます。一方、ログ ペイロードが文字列としてフォーマットされ、textPayload フィールドに格納されている場合、ログエントリは非構造化になります。 テキスト フィールドを検索することはできますが、そのコンテンツをインデックスに登録することはできません。

構造化ログは、いくつかの方法で Logging に書き込めます。

  • Cloud Logging API を使用してログエントリを書き込む
  • Google Cloud CLI を使用してログエントリを書き込む
  • BindPlane サービスを使用してログを取り込む
  • Logging エージェントに対してシリアル化された JSON オブジェクトを指定する

各方法については、以降のセクションで説明します。

Logging API を使用する

Cloud Logging API を使用してログエントリを書き込む場合、jsonPayload を使用して完全な LogEntry 構造を Cloud Logging API に送信してペイロードの構造を制御できます。

詳細については、entries.write リファレンスをご覧ください。 コード例については、構造化ログの書き込みをご覧ください。

gcloud CLI の使用

gcloud CLI を使用してログエントリを書き込む場合、完全な LogEntry 構造を jsonPayload で Cloud Logging API に追加します。

コード例については、gcloud logging write リファレンスをご覧ください。

BindPlane を使用する

BindPlane サービスを使用してログを取り込むと、ペイロードは JSON 形式となり、ソースシステムに従って構造化されます。BindPlane 経由で取り込まれたログの検索と表示の詳細については、ログデータの検索に関する BindPlane のドキュメントをご覧ください。

Logging エージェントを使用する

Cloud Logging エージェントを使用してログエントリを取得する場合は、Logging エージェントがペイロードを JSON 形式に変換するように指定できます。

Google Kubernetes Engine または App Engine フレキシブル環境を使用している場合は、構造化されたログを 1 行でシリアル化された JSON オブジェクトとして stdout または stderr に書き込めます。すると、構造化ログは、Logging エージェントにより、LogEntry 構造の jsonPayload として Cloud Logging に送信されます。

JSON オブジェクトのフィールドには、Logging エージェントによって特別なものと認識され、LogEntry 構造に取り出されるものがあります。これらの特別な JSON フィールドは、次に挙げる LogEntry のフィールドを設定するために使用されます。

  • severity
  • spanId
  • ユーザー定義の labels
  • httpRequest

JSON はテキスト行よりも厳密で汎用性が高いため、JSON オブジェクトは、複数行メッセージの記述や、メタデータの追加に使用できます。

簡略化された形式を使用してアプリケーションの構造化ログエントリを作成するには、JSON の各フィールドと値を列挙した次の表をご覧ください。

JSON ログフィールド LogEntry フィールド Cloud Logging エージェントの機能 値の例
severity severity Logging エージェントは、Logging API によって認識される LogSeverity 文字列のリストを含む、さまざまな共通の重大度文字列の照合を試みます。 "severity":"ERROR"
message textPayload(または jsonPayload の一部) ログ エクスプローラのログエントリに表示されるメッセージ。 "message":"There was an error in the application."

: Logging エージェントが他の特殊フィールドを移動した後に唯一残ったフィールドであり、かつ detect_json が有効でない場合、messagetextPayload として保存されます。これに該当しない場合、messagejsonPayload に残ります。detect_json は、Google Kubernetes Engine などのマネージド ロギング環境には適用されません。ログエントリに例外スタック トレースが含まれている場合、例外スタック トレースをこの message JSON ログフィールドに設定する必要があります。設定すると、例外スタック トレースが解析され Error Reporting に保存されるようになります。
log(以前の Google Kubernetes Engine のみ) textPayload 以前の Google Kubernetes Engine にのみ適用されます。特殊フィールドを移動した後に log フィールドだけが残った場合、そのフィールドは textPayload として保存されます。
httpRequest httpRequest LogEntry HttpRequest フィールド形式の構造化レコード。 "httpRequest":{"requestMethod":"GET"}
時間関連フィールド timetimestamp、など。 詳細については、時間関連フィールドをご覧ください。 "time":"2020-10-12T07:20:50.52Z"
logging.googleapis.com/insertId insertId 詳細については、LogEntry ページの insertId をご覧ください。 "logging.googleapis.com/insertId":"42"
logging.googleapis.com/labels labels このフィールドの値は構造化レコードにする必要があります。詳細については、LogEntry ページの labels をご覧ください。 "logging.googleapis.com/labels": {"user_label_1":"value_1","user_label_2":"value_2"}
logging.googleapis.com/operation operation このフィールドの値は、関連するログエントリをグループ化するためにログ エクスプローラによっても使用されます。詳細については、LogEntry ページの operation をご覧ください。 "logging.googleapis.com/operation": {"id":"get_data","producer":"github.com/MyProject/MyApplication", "first":"true"}
logging.googleapis.com/sourceLocation sourceLocation ログエントリに関連するソースコードの位置情報(存在する場合)。詳細については、LogEntry ページの LogEntrySourceLocation をご覧ください。 "logging.googleapis.com/sourceLocation": {"file":"get_data.py","line":"142","function":"getData"}
logging.googleapis.com/spanId spanId ログエントリに関連付けられているトレース内のスパン ID。詳細については、LogEntry ページの spanId をご覧ください。 "logging.googleapis.com/spanId":"000000000000004a"
logging.googleapis.com/trace trace ログエントリに関連するリソースの名前(存在する場合)。詳細については、LogEntry ページの trace をご覧ください。 "logging.googleapis.com/trace":"projects/my-projectid/traces/0679686673a"

: stdoutstderr に書き込まない場合、このフィールドの値は、projects/[PROJECT-ID]/traces/[TRACE-ID] 形式にする必要があります。これにより、ログ エクスプローラとトレース ビューアで、ログエントリをグループ化してトレースと一緒に表示できます。autoformat_stackdriver_trace が true で、[V]ResourceTracetraceId の形式と一致する場合、LogEntry の trace フィールドの値は projects/[PROJECT-ID]/traces/[V] になります。
logging.googleapis.com/trace_sampled traceSampled このフィールドの値は true または false のいずれかにする必要があります。詳細については、LogEntry ページの traceSampled をご覧ください。 "logging.googleapis.com/trace_sampled": false

簡略化された形式でログエントリを作成するには、上記のフィールドを使用してエントリの JSON 表現を作成します。すべてのフィールドは省略可能です。

簡略化された JSON ログエントリの例を、次に示します。

{
  "severity":"ERROR",
  "message":"There was an error in the application.",
  "httpRequest":{
    "requestMethod":"GET"
  },
  "times":"2020-10-12T07:20:50.52Z",
  "logging.googleapis.com/insertId":"42",
  "logging.googleapis.com/labels":{
    "user_label_1":"value_1",
    "user_label_2":"value_2"
  },
  "logging.googleapis.com/operation":{
    "id":"get_data",
    "producer":"github.com/MyProject/MyApplication",
     "first":"true"
  },
  "logging.googleapis.com/sourceLocation":{
    "file":"get_data.py",
    "line":"142",
    "function":"getData"
  },
  "logging.googleapis.com/spanId":"000000000000004a",
  "logging.googleapis.com/trace":"projects/my-projectid/traces/06796866738c859f2f19b7cfb3214824",
  "logging.googleapis.com/trace_sampled":false
}

結果のログエントリの例を、次に示します。

{
  "insertId": "42",
  "jsonPayload": {
    "message": "There was an error in the application",
    "times": "2019-10-12T07:20:50.52Z"
  },
  "httpRequest": {
    "requestMethod": "GET"
  },
  "resource": {
    "type": "k8s_container",
    "labels": {
      "container_name": "hello-app",
      "pod_name": "helloworld-gke-6cfd6f4599-9wff8",
      "project_id": "stackdriver-sandbox-92334288",
      "namespace_name": "default",
      "location": "us-west4",
      "cluster_name": "helloworld-gke"
    }
  },
  "timestamp": "2020-11-07T15:57:35.945508391Z",
  "severity": "ERROR",
  "labels": {
    "user_label_2": "value_2",
    "user_label_1": "value_1"
  },
  "logName": "projects/stackdriver-sandbox-92334288/logs/stdout",
  "operation": {
    "id": "get_data",
    "producer": "github.com/MyProject/MyApplication",
    "first": true
  },
  "trace": "projects/my-projectid/traces/06796866738c859f2f19b7cfb3214824",
  "sourceLocation": {
    "file": "get_data.py",
    "line": "142",
    "function": "getData"
  },
  "receiveTimestamp": "2020-11-07T15:57:42.411414059Z",
  "spanId": "000000000000004a"
}

Logging エージェントの構成

Logging エージェント google-fluentd は、Fluentd ログデータ コレクタの Cloud Logging 専用パッケージです。Logging エージェントにはデフォルトの Fluentd 構成があります。Logging エージェントは、Fluentd 入力プラグインを使用して外部ソース(ディスク上のファイルなど)からイベントログを pull するか、受信ログレコードを解析します。

Fluentd にはサポートされるパーサーのリストがあります。これらのパーサーはログを抽出し、構造化ペイロード(JSON)に変換します。

ログソースに format [PARSER_NAME] を構成することで、Fluentd が提供する組み込みのパーサーを利用できます。

次のコードサンプルは、Fluentd の構成、入力されるログレコード、出力される構造化ペイロード(Cloud Logging のログエントリの一部)を示しています。

  • Fluentd の構成:

      <source>
        @type tail
    
        format syslog # <--- This uses a predefined log format regex named
                      # `syslog`. See details at https://docs.fluentd.org/parser/syslog.
    
        path /var/log/syslog
        pos_file /var/lib/google-fluentd/pos/syslog.pos
        read_from_head true
        tag syslog
      </source>
    
  • ログレコード(入力):

      <6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test
    
  • 構造化ペイロード(出力):

        jsonPayload: {
            "pri": "6",
            "host": "192.168.0.1",
            "ident": "fluentd",
            "pid": "11111",
            "message": "[error] Syslog test"
        }
    

syslog パーサーの仕組みの詳細については、詳細な Fluentd のドキュメントをご覧ください。

デフォルトで有効になっている標準パーサー

次の表に、構造化ロギングを有効にした場合にエージェントに含まれる標準パーサーを示します。

パーサー名 構成ファイル
syslog /etc/google-fluentd/config.d/syslog.conf
nginx /etc/google-fluentd/config.d/nginx.conf
apache2 /etc/google-fluentd/config.d/apache.conf
apache_error /etc/google-fluentd/config.d/apache.conf

Logging エージェントのインストール時に構造化ロギングを有効にする方法については、インストールをご覧ください。

インストール

構造化ロギングを有効にするには、Logging エージェントをインストール(または再インストール)するときに、Logging エージェントのデフォルト構成を変更する必要があります。構造化ロギングを有効にすると、以前の構成ファイルは置換されますが、エージェント自体のオペレーションは変わりません。

構造化ロギングを有効にすると、構造化ログを有効にする前と異なる形式にログエントリが変換されます。ログが Logging の外部の宛先に転送されている場合は、この変更が後処理のアプリケーションに影響を及ぼす可能性があります。たとえば、ログを BigQuery に転送する場合、BigQuery は変更を行った時刻以降の当日の残りの期間について、新しいログエントリを誤ったスキーマを有するものとして拒否します。

Logging エージェントをインストールして構造化ロギングを有効にする手順については、Logging エージェントのインストールをご覧ください。

Logging エージェントの構成ファイル(/etc/google-fluentd/config.d/)に、デフォルトで有効になっている標準パーサーが追加されています。

Apache アクセスログの形式の構成

Logging エージェントでは、Apache アクセスログのデータはデフォルトで jsonPayload フィールドに保存されます。次に例を示します。

{
  "logName": ...,
  "resource": ...,
  "httpRequest": ...,
  "jsonPayload": {
    "user"   : "some-user",
    "method" : "GET",
    "code"   : 200,
    "size"   : 777,
    "host"   : "192.168.0.1",
    "path"   : "/some-path",
    "referer": "some-referer",
    "agent"  : "Opera/12.0"
  },
  ...
}

また、特定のフィールドを httpRequest フィールドに抽出するように Logging エージェントを構成することもできます。例:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "/some-path",
    "requestSize": "777",
    "status": "200",
    "userAgent": "Opera/12.0",
    "serverIp": "192.168.0.1",
    "referrer":"some-referrer",
  },
  "jsonPayload": {
    "user":"some-user"
  },
  ...
}

前のサンプルで示されたように httpRequest フィールドを構成すると、トレースが支援されます。Google Cloud コンソールでは、特定の HTTP リクエストのすべてのログが親子階層で表示されます。

この抽出を構成するには、/etc/google-fluentd/config.d/apache.conf の末尾に、次の内容を追加します。

  <filter apache-access>
    @type record_transformer
    enable_ruby true
    <record>
      httpRequest ${ {"requestMethod" => record['method'], "requestUrl" => record['path'], "requestSize" => record['size'], "status" => record['code'], "userAgent" => record['agent'], "serverIp" => record['host'],
      "referer" => record['referer']} }
    </record>
    remove_keys method, path, size, code, agent, host, referer
  </filter>

ログエントリの構成方法については、ログレコードの変更をご覧ください。

nginx アクセスログの形式の構成

Logging エージェントでは、nginx アクセスログのデータはデフォルトで jsonPayload フィールドに保存されます。次に例を示します。

  {
    "logName": ...,
    "resource": ...,
    "httpRequest": ...,
    "jsonPayload": {
      "remote":"127.0.0.1",
      "host":"192.168.0.1",
      "user":"some-user",
      "method":"GET",
      "path":"/some-path",
      "code":"200",
      "size":"777",
      "referrer":"some-referrer",
      "agent":"Opera/12.0",
      "http_x_forwarded_for":"192.168.3.3"
    },
    ...
  }

また、特定のフィールドを httpRequest フィールドに抽出するように Logging エージェントを構成することもできます。例:

  {
    "logName": ...,
    "resource": ...,
    "httpRequest": {
      "requestMethod": "GET",
      "requestUrl": "/some-path",
      "requestSize": "777",
      "status": "200",
      "userAgent": "Opera/12.0",
      "remoteIp": "127.0.0.1",
      "serverIp": "192.168.0.1",
      "referrer":"some-referrer",
    },
    "jsonPayload": {
      "user":"some-user",
      "http_x_forwarded_for":"192.168.3.3"
    },
    ...
  }

前のサンプルで示されたように httpRequest フィールドを構成すると、トレースが支援されます。Google Cloud コンソールでは、特定の HTTP リクエストのすべてのログが親子階層で表示されます。

この抽出を構成するには、/etc/google-fluentd/config.d/nginx.conf の末尾に、次の内容を追加します。

<filter nginx-access>
  @type record_transformer
  enable_ruby true
  <record>
    httpRequest ${ {"requestMethod" => record['method'], "requestUrl" => record['path'], "requestSize" => record['size'], "status" => record['code'], "userAgent" => record['agent'], "remoteIp" => record['remote'], "serverIp" => record['host'], "referer" => record['referer']} }
  </record>
  remove_keys method, path, size, code, agent, remote, host, referer
</filter>

ログエントリの構成方法については、ログレコードの変更をご覧ください。

独自のパーサーを作成する

使用するログが標準パーサーでサポートされていない場合は、独自のパーサーを作成できます。パーサーは、ログレコードの照合とラベルの適用に使用される正規表現から構成されます。

次のサンプルコードは、ログレコードのログ行、ログ行の形式を表す正規表現を含む構成、結果のログエントリを示しています。

  • ログレコードのログ行:

    REPAIR CAR $500
    
  • ログ行の形式を表す正規表現を含む構成:

    $ sudo vim /etc/google-fluentd/config.d/test-structured-log.conf
    $ cat /etc/google-fluentd/config.d/test-structured-log.conf
    <source>
      @type tail
    
      # Format indicates the log should be translated from text to
      # structured (JSON) with three fields, "action", "thing" and "cost",
      # using the following regex:
      format /(?<action>\w+) (?<thing>\w+) \$(?<cost>\d+)/
      # The path of the log file.
      path /tmp/test-structured-log.log
      # The path of the position file that records where in the log file
      # we have processed already. This is useful when the agent
      # restarts.
      pos_file /var/lib/google-fluentd/pos/test-structured-log.pos
      read_from_head true
      # The log tag for this log input.
      tag structured-log
    </source>
    
  • 結果のログエントリ:

     {
      insertId:  "eps2n7g1hq99qp"
      jsonPayload: {
        "action": "REPAIR"
        "thing": "CAR"
        "cost": "500"
      }
      labels: {
        compute.googleapis.com/resource_name:  "add-structured-log-resource"
      }
      logName:  "projects/my-sample-project-12345/logs/structured-log"
      receiveTimestamp:  "2018-03-21T01:47:11.475065313Z"
      resource: {
        labels: {
          instance_id:  "3914079432219560274"
          project_id:  "my-sample-project-12345"
          zone:  "us-central1-c"
        }
        type:  "gce_instance"
      }
      timestamp:  "2018-03-21T01:47:05.051902169Z"
     }
    

問題のトラブルシューティング

Logging エージェントのインストールや操作に関する一般的な問題のトラブルシューティングについては、エージェントのトラブルシューティングをご覧ください。

次のステップ