Rails 8 での構造化ログ実装の軌跡

Rails 8 での構造化ログ実装の軌跡

English follows Japanese.

概要

  • 構造化ログと Cloud Logging の組み合わせは、便利
    • フィルタリング
    • 一連のログのグルーピング
  • Cloud Run
    • traceparent ヘッダーが標準で付与される
    • (隠れた)LB のログにも Trace ID が含まれる
    • 標準出力で実証
  • 実行環境
    • Rails 8
    • Ruby 3.4
    • DB なし

スモールスタートで、非機能の面にはあまり時間がかけられない場合でも、動くコードを含めた先行例があれば、実装の参考になると思いました。

はじめに

本記事では、Rails 8 アプリケーションにおける構造化ログの実装例を紹介します。特に、分散トレーシングのための Trace ID をログに含める方法に焦点を当て、SemanticLogger(GitHub, Rails Semantic Logger) と連携した W3C TraceContext の活用方法を解説します。

Lograge が良く使われている印象がありますが、JSON にしつつ、request_id を付与するのが面倒という記事もいくつか見かけました。その点、SemanticLogger は構造化ログを簡単に実現そうだったので、調査も兼ねて実装してみました。

背景

マイクロサービスアーキテクチャが普及する中、複数サービス間でのリクエストの追跡が重要な課題となっています。W3C TraceContext は、サービス間でトレース情報を伝播するための標準規格で、HTTP traceparent ヘッダーを使用して Trace ID を受け渡します。

当プロジェクトでは、この Trace ID を Rails アプリケーションのログに統合し、Google Cloud Logging での一元的な監視を可能にすることを目標としました。

まとめ

  1. middleware を新規実装し、使って Request Header (traceparent) から Trace ID を抽出
    • HTTP_TRACEPARENT ヘッダーを抽出し、env[:traceparent] に保存
    • Trace ID を env[:trace_id] に保存
    • req.request_id に Trace ID を上書き
  2. config.log_tags を使って、リクエスト ID と Trace ID をログにタグ付け
    • traceparenttrace_idlog_tags に追加
  3. 構造化ログ (JSON) を出力するためのフォーマッタを実装
    • SemanticLogger::Formatters::Json とほぼ同じ実装
    • SemanticLogger::Formatters::Raw を継承
    • traceparent フィールドを、トップレベルフィールドとして出力
    • logging.googleapis.com/trace フィールドをトップレベルフィールドとし、projects/#{ENV["PROJECT_ID"]}/traces/#{trace_id} とする形で Trace ID を設定

上記によって、Cloud Logging で Trace ID によるログのグルーピングが可能になります。専用のログ エージェントの導入が不要で、Cloud Run の標準出力に直接出力するだけで、Google Cloud Logging に構造化ログが送信されます。


(参考)Rails における Request ID の仕組み

まず、Rails の標準的なログに含まれる request_id の仕組みを理解しておくことが重要です。bisque によるRailsのログに含まれるrequest_idについてコードリーディングしたメモによれば、request_id は以下のように処理されます:

  1. 生成: ActionDispatch::RequestId ミドルウェアが Request Header の X-Request-Id を取得(ない場合は UUID を生成)
  2. 伝播: リクエストオブジェクトの request_id 属性として保存され、Response Header にも X-Request-Id として設定(注: Request Header なのでは…と思うが、良く分かっていない)
  3. ログ出力: Rack::Logger ミドルウェアが ActiveSupport::TaggedLogging を通じてログに request_id を挿入
  4. スレッド管理: ActiveSupport::TaggedLoggingThread.current を使ってタグを管理し、アプリケーション全体で参照可能に

この仕組みを応用して、W3C TraceContext からの Trace ID をログに統合する実装を行います。

X-Request-Id ヘッダーは、リクエストのトレースを追跡するために使用されていました(今でも使用されています)。分散トレーシングの観点からは、Trace ID を含む traceparent ヘッダーを使用することが推奨されます。

実装のステップ

1. 必要な Gem の導入

まず、構造化ログ出力のための Gem をインストールしました。Gemfile に以下を追加します。JSON で出力する仕組みも含まれています。

gem "rails_semantic_logger"

2. トレースミドルウェアの実装

Request Header から Trace ID を抽出するために、専用のミドルウェアを実装しました。これは Rails の ActionDispatch::RequestId と同様のアプローチです。

GitHub リポジトリへのリンクも付しておきます。trace_middleware.rb

# lib/trace_middleware.rb
class TraceMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    # HTTP_TRACEPARENT ヘッダーを抽出
    trace_parent = extract_trace_parent(env)
    if trace_parent
      env[:traceparent] = trace_parent
    end

    # ヘッダーから trace_id を抽出、
    # ない場合は SecureRandom.uuid を使用
    env[:trace_id] = make_trace_id(trace_parent)

    # Overwrite the request_id with the trace_id
    req = ActionDispatch::Request.new env
    req.request_id = insert_hyphens(env[:trace_id])

    @app.call(env)
  end

  # ...
end

このミドルウェアは、W3C TraceContext に準拠した traceparent ヘッダー(HTTP_TRACEPARENT に保存される)から Trace ID を抽出し、リクエスト環境変数に保存します。ヘッダーが存在しない場合は、UUID 形式の新しい Trace ID を生成します。

Rails の Request ID と Trace ID を統一する実装も行いました。traceparent がある場合には、それを優先しているだけなので、仕組みは同じです。

これにより、デフォルトのログ出力でも Trace ID が使われるようになり、一貫性のあるログ出力が可能になりました。insert_hyphens メソッドは、Trace ID にハイフンを挿入して UUID 形式に変換する機能を持っています:

def insert_hyphens(trace_id)
  trace_id = trace_id.dup.to_s

  # 各位置にハイフンを挿入(文字列長をチェックしながら)
  [8, 13, 18, 23].each do |pos|
    break if trace_id.length <= pos
    trace_id.insert(pos, "-")
  end

  trace_id
end

この実装は、ActionDispatch::RequestId ミドルウェアの直後に配置することで、Rails が生成した request_id を上書きします。これは bisque の記事Railsのログに含まれるrequest_idについてコードリーディングしたメモで解説されている Rails の request_id の仕組みを利用しています。

bin/rails middleware コマンドを実行すると、ミドルウェアの順序を確認できるんですね。

3. カスタムログフォーマッタの実装

次に、SemanticLogger のフォーマッタをカスタマイズし、Trace ID をログのトップレベルフィールドとして出力できるようにしました。これは en30 の記事Rails on GKEでも理想の構造化ログを目指すで紹介されているアプローチを参考にしています。

cloud_trace_log_json.rb

# lib/formatters/cloud_trace_log_json.rb
module Formatters
  class CloudTraceLogJson < SemanticLogger::Formatters::Raw
    # Default JSON time format is ISO8601
    def initialize(time_format: :iso_8601, time_key: :timestamp, **args)
      super(time_format: time_format, time_key: time_key, **args)
    end

    def traceparent
      if log.named_tags && log.named_tags[:traceparent]
        hash[:traceparent] = log.named_tags[:traceparent]
      end
    end

    def trace
      if log.named_tags && log.named_tags[:trace_id]
        trace_id = log.named_tags[:trace_id]
      elsif log.named_tags && log.named_tags[:request_id]
        trace_id = log.named_tags[:request_id].gsub("-", "")
      else
        return
      end

      hash["logging.googleapis.com/trace"] = "projects/#{ENV["PROJECT_ID"]}/traces/#{trace_id}" if trace_id && !ENV["PROJECT_ID"].nil? && !ENV["PROJECT_ID"].empty?
    end

    # ...
  end
end

このフォーマッタにより、ログ出力時に Trace ID が Google Cloud Logging が求める形式 構造化ロギング に変換され、UI で関連するログを Trace ID で検索できるようになります。特に Google Cloud Logging 固有の logging.googleapis.com/trace フィールドに、PROJECT_ID を含めた出力をしています。これで、Google Cloud のトレース機能との統合が容易になります。

参考記事では、Lograge と google-cloud-logging (ruby) を組み合わせていました。

4. Rails の設定

導入のため、いくつか Rails の設定を変更しました。特に、SemanticLogger を使用するための設定を行いました。config/application.rb に以下を追加します:

TaggedLogging

ログエントリにタグを付けておきます。log_tags にハッシュを指定すると、名前付きのタグを追加できます。これにより、リクエスト ID や Trace ID をログに含めることができます。

application.rb

# config/application.rb
config.log_tags = {
  request_id: :request_id,
  trace_id: ->(request) { request.env[:trace_id] },
  traceparent: ->(request) { request.env[:traceparent] }
}

このように、log_tags を設定すると、SemanticLogger では named_tags フィールドにタグが追加されます。これにより、リクエスト ID や Trace ID を含む構造化ログを生成できます。

config/environments/production.rb には config.log_tags = [ :request_id ] という行がありますが、config/application.rb の設定を上書きしてしまいます。config/environments/production.rb にも同様の設定を追加する必要があります。

ミドルウェアの配置順序

ミドルウェアの挿入位置は重要な検討事項でした。ActionDispatch::RequestId ミドルウェアの直後に配置することで、Request ID をすぐに上書きするようにします。とにかく最初に抽出したければ insert_after 0 で良いのですが、RequestId と整合性を持たせたかったので、ActionDispatch::RequestId の直後に配置しました。

# config/application.rb
# config.middleware.insert_after 0, TraceMiddleware
config.middleware.insert_after ActionDispatch::RequestId, TraceMiddleware

この配置により、以下の処理フローが実現されます:

  1. ActionDispatch::RequestId ミドルウェアがデフォルトの request_id を生成
  2. TraceMiddleware が Trace ID を抽出し、必要に応じて request_id を上書き
  3. Rack::Loggerrequest_id(上書きされた場合は Trace ID)をログに出力

SemanticLogger の設定

SemanticLogger を使用し、独自フォーマッタを導入するための設定を行います。config/application.rb に以下を追加します:

# config/application.rb
config.rails_semantic_logger.started = true
config.rails_semantic_logger.add_file_appender = false
# config.semantic_logger.add_appender(io: $stdout, formatter: :json)
config.semantic_logger.add_appender(io: $stdout, formatter: Formatters::CloudTraceLogJson.new)

開発環境でも JSON 形式で出力されるのが嫌な場合は、config/environments/production.rb だけに追加しておくと良いかもしれません。(ごめんなさい、試していません。開発環境で JSON になることを確認しながら実装をしたのです…)

閲覧

Cloud Logging では、"243 ms" のような、処理時間を表すフィールド(横にトレースを示す記号がついている)をクリックすると、メニューが出てきて "Show entries for this trace" を選択すると、特定トレースだけを抽出して表示することができます。

Cloud Logging 1

Rails アプリケーションからのログは、JSON 形式で出力されています。
Cloud Logging JSON

JSON 形式でログを出力した場合、"logging.googleapis.com/trace" フィールドは、jsonPayload には含まれず、トップレベルのフィールドとして出力されます。
Cloud Logging trace

おまけ: デプロイのためのコード

Terraform を使用したデプロイ

実際に Cloud Run にデプロイする際、Terraform を使用してインフラをコード化しました。terraform/environments/stg ディレクトリに配置しています。.tfvars ファイルを作成し、プロジェクト ID とバックエンドバケット名を指定します。

project_id          = "project-id"
backend_bucket_name = "tfstate-bucket"

あとは terraform apply -var-file=".tfvars" を実行するだけで、Cloud Run 環境が構築されます。ただし、イメージのビルドと、Secret Manager へのシークレットの登録は手動で行う必要があります。

イメージのビルド

PROJECT_ID を指定して、Cloud Build を使用してイメージをビルドします。

export PROJECT_ID="project-id"
gcloud builds submit . --project=$PROJECT_ID --config build_image.yaml

正しい書き方か自信はありませんが、build_image.yaml として置いておきます。

まとめ

この実装により、以下のことが達成されれました:

  1. 分散トレーシングのサポート - W3C TraceContext に準拠したトレース情報の伝播が可能に
  2. 構造化ログの実現 - JSON フォーマットでの出力により、検索・分析が容易に
  3. Google Cloud Logging との統合 - Trace ID を Cloud Logging のフォーマットで出力することで、ログのフィルタリングが改善
  4. アプリケーションの可観測性向上 - リクエスト全体を通した追跡が可能に
  5. 標準ログとの互換性 - 既存のログ収集ツールや分析ツールとの互換性を維持

Rails 8 での構造化ログ実装を通じて、SemanticLogger とカスタムミドルウェアを活用した Trace ID 統合の方法を紹介しました。この実装は、マイクロサービスアーキテクチャにおけるログの統合と追跡性向上に貢献し、デバッグやパフォーマンス分析の効率化に役立ちます。

bisque によるコードリーディングで解説されているように、Rails の request_id 機構は、ミドルウェア、スレッドローカル変数、タグ付けロガーの連携により実現されています。今回の実装では、その仕組みを活かしつつ、W3C TraceContext との統合を図りました。

この文章の一部は LLM に書かせたのですが、LLM は『今後の展望としては、より詳細なメトリクスの収集やアラート設定との連携など、モニタリング機能の強化を検討しています。また、OpenTelemetry との統合も視野に入れています。』と書いていました。そんなことするかは分かりません。

人間個人としては、他に、Cloud Logging 構造化ログの特別な JSON フィールドまとめを参考にしてフィールドを追加しても良いかなぁと思いました。

参考リソース


The Path to Implementing Structured Logging in Rails 8

Overview

  • The combination of structured logging and Cloud Logging is convenient
    • Filtering
    • Grouping of a series of logs
  • Cloud Run
    • The traceparent header is added by default
    • Trace ID is also included in the logs of the (hidden) LB
    • Demonstrated with standard output
  • Execution Environment
    • Rails 8
    • Ruby 3.4
    • No DB

I thought that even in cases of a small start where not much time can be spent on non-functional aspects, having a preceding example including working code would be helpful as a reference for implementation.

Introduction

In this article, I will introduce an example of implementing structured logging in a Rails 8 application. In particular, I will focus on how to include Trace IDs for distributed tracing in logs, and explain how to utilize W3C TraceContext in conjunction with SemanticLogger(GitHub, Rails Semantic Logger).

I have the impression that Lograge is often used, but I've also seen some articles mentioning that it's cumbersome to convert to JSON and add a request_id. In that regard, SemanticLogger seemed to make it easy to achieve structured logging, so I tried implementing it, partly as an investigation.

Background

As microservice architecture becomes more prevalent, tracking requests across multiple services has become an important issue. W3C TraceContext is a standard for propagating trace information between services, using the HTTP traceparent header to pass Trace IDs.

In this project, the goal was to integrate this Trace ID into the logs of the Rails application and enable centralized monitoring with Google Cloud Logging.

Summary

  1. Implement new middleware to extract Trace ID from Request Header (traceparent)
    • Extract the HTTP_TRACEPARENT header and save it to env[:traceparent]
    • Save the Trace ID to env[:trace_id]
    • Overwrite req.request_id with the Trace ID
  2. Use config.log_tags to tag logs with Request ID and Trace ID
    • Add traceparent and trace_id to log_tags
  3. Implement a formatter to output structured logs (JSON)
    • Almost the same implementation as SemanticLogger::Formatters::Json
    • Inherit from SemanticLogger::Formatters::Raw
    • Output the traceparent field as a top-level field
    • Set the logging.googleapis.com/trace field as a top-level field, and configure the Trace ID in the format projects/#{ENV["PROJECT_ID"]}/traces/#{trace_id}

With the above, grouping logs by Trace ID in Cloud Logging becomes possible. There is no need to install a dedicated log agent; structured logs are sent to Google Cloud Logging simply by outputting directly to Cloud Run's standard output.


(Reference) How Request ID works in Rails

First, it is important to understand the mechanism of request_id included in standard Rails logs. According to Notes on code reading about request_id included in Rails logs by bisque, request_id is processed as follows:

  1. Generation: The ActionDispatch::RequestId middleware retrieves X-Request-Id from the Request Header (generates a UUID if not present)
  2. Propagation: Saved as the request_id attribute of the request object and also set as X-Request-Id in the Response Header (Note: I think it might be a Request Header... but I don't understand it well)
  3. Log Output: The Rack::Logger middleware inserts request_id into logs via ActiveSupport::TaggedLogging
  4. Thread Management: ActiveSupport::TaggedLogging uses Thread.current to manage tags, making them accessible throughout the application

We will implement the integration of Trace ID from W3C TraceContext into logs by applying this mechanism.

The X-Request-Id header was used (and is still used) to track request traces. From a distributed tracing perspective, it is recommended to use the traceparent header, which includes the Trace ID.

Implementation Steps

1. Installing Necessary Gems

First, I installed the gem for structured log output. Add the following to your Gemfile. It also includes a mechanism for outputting in JSON.

gem "rails_semantic_logger"

2. Implementing Trace Middleware

To extract the Trace ID from the Request Header, I implemented dedicated middleware. This is a similar approach to Rails' ActionDispatch::RequestId.

I'll also include a link to the GitHub repository: trace_middleware.rb

# lib/trace_middleware.rb
class TraceMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    # Extract HTTP_TRACEPARENT header
    trace_parent = extract_trace_parent(env)
    if trace_parent
      env[:traceparent] = trace_parent
    end

    # Extract trace_id from the header,
    # if not present, use SecureRandom.uuid
    env[:trace_id] = make_trace_id(trace_parent)

    # Overwrite the request_id with the trace_id
    req = ActionDispatch::Request.new env
    req.request_id = insert_hyphens(env[:trace_id])

    @app.call(env)
  end

  # ...
end

This middleware extracts the Trace ID from the W3C TraceContext compliant traceparent header (saved in HTTP_TRACEPARENT) and stores it in the request environment variables. If the header does not exist, it generates a new Trace ID in UUID format.

I also implemented a way to unify Rails' Request ID and Trace ID. If traceparent exists, it is prioritized, so the mechanism is the same.

This allows the Trace ID to be used even in default log outputs, enabling consistent log output. The insert_hyphens method has the function of inserting hyphens into the Trace ID to convert it to UUID format:

def insert_hyphens(trace_id)
  trace_id = trace_id.dup.to_s

  # Insert hyphens at each position (while checking string length)
  [8, 13, 18, 23].each do |pos|
    break if trace_id.length <= pos
    trace_id.insert(pos, "-")
  end

  trace_id
end

This implementation is placed immediately after the ActionDispatch::RequestId middleware to overwrite the request_id generated by Rails. This utilizes the Rails request_id mechanism explained in bisque's article Railsのログに含まれるrequest_idについてコードリーディングしたメモ(Notes on code reading about request_id included in Rails logs).

It turns out you can check the order of middleware by running the bin/rails middleware command.

3. Implementing a Custom Log Formatter

Next, I customized SemanticLogger's formatter to output the Trace ID as a top-level field in the logs. This references the approach introduced in en30's article Rails on GKEでも理想の構造化ログを目指す(Aiming for Ideal Structured Logging even with Rails on GKE).

cloud_trace_log_json.rb

# lib/formatters/cloud_trace_log_json.rb
module Formatters
  class CloudTraceLogJson < SemanticLogger::Formatters::Raw
    # Default JSON time format is ISO8601
    def initialize(time_format: :iso_8601, time_key: :timestamp, **args)
      super(time_format: time_format, time_key: time_key, **args)
    end

    def traceparent
      if log.named_tags && log.named_tags[:traceparent]
        hash[:traceparent] = log.named_tags[:traceparent]
      end
    end

    def trace
      if log.named_tags && log.named_tags[:trace_id]
        trace_id = log.named_tags[:trace_id]
      elsif log.named_tags && log.named_tags[:request_id]
        trace_id = log.named_tags[:request_id].gsub("-", "")
      else
        return
      end

      hash["logging.googleapis.com/trace"] = "projects/#{ENV["PROJECT_ID"]}/traces/#{trace_id}" if trace_id && !ENV["PROJECT_ID"].nil? && !ENV["PROJECT_ID"].empty?
    end

    # ...
  end
end

With this formatter, when logs are output, the Trace ID is converted to the format required by Google Cloud Logging Structured logging, allowing related logs to be searched by Trace ID in the UI. In particular, it outputs to the Google Cloud Logging specific logging.googleapis.com/trace field, including the PROJECT_ID. This facilitates integration with Google Cloud's tracing features.

The reference article combined Lograge and google-cloud-logging (ruby).

4. Rails Configuration

For introduction, I changed some Rails settings. In particular, I configured settings for using SemanticLogger. Add the following to config/application.rb:

TaggedLogging

Add tags to log entries. If you specify a hash for log_tags, you can add named tags. This allows you to include Request ID and Trace ID in the logs.

application.rb

# config/application.rb
config.log_tags = {
  request_id: :request_id,
  trace_id: ->(request) { request.env[:trace_id] },
  traceparent: ->(request) { request.env[:traceparent] }
}

By setting log_tags in this way, tags are added to the named_tags field in SemanticLogger. This allows you to generate structured logs that include Request ID and Trace ID.

config/environments/production.rb has a line config.log_tags = [ :request_id ], but this will overwrite the settings in config/application.rb. You need to add similar settings to config/environments/production.rb as well.

Middleware Placement Order

The insertion position of the middleware was an important consideration. By placing it just after the ActionDispatch::RequestId middleware, the Request ID is overwritten immediately. If you just want to extract it first, insert_after 0 would be fine, but I wanted to maintain consistency with RequestId, so I placed it just after ActionDispatch::RequestId.

# config/application.rb
# config.middleware.insert_after 0, TraceMiddleware
config.middleware.insert_after ActionDispatch::RequestId, TraceMiddleware

This placement achieves the following processing flow:

  1. The ActionDispatch::RequestId middleware generates the default request_id
  2. TraceMiddleware extracts the Trace ID and overwrites request_id if necessary
  3. Rack::Logger outputs request_id (or Trace ID if overwritten) to the log

SemanticLogger Configuration

Configure settings to use SemanticLogger and introduce a custom formatter. Add the following to config/application.rb:

# config/application.rb
config.rails_semantic_logger.started = true
config.rails_semantic_logger.add_file_appender = false
# config.semantic_logger.add_appender(io: $stdout, formatter: :json)
config.semantic_logger.add_appender(io: $stdout, formatter: Formatters::CloudTraceLogJson.new)

If you don't want JSON format output in the development environment, it might be good to add this only to config/environments/production.rb. (I'm sorry, I haven't tried it. I implemented it while confirming that it becomes JSON in the development environment...)

Viewing

In Cloud Logging, if you click on a field representing processing time, like "243 ms" (which has a trace symbol next to it), a menu will appear, and selecting "Show entries for this trace" allows you to extract and display only that specific trace.

Cloud Logging 1

Logs from the Rails application are output in JSON format.
Cloud Logging JSON

When logs are output in JSON format, the "logging.googleapis.com/trace" field is not included in jsonPayload but is output as a top-level field.
Cloud Logging trace

Bonus: Code for Deployment

Deployment using Terraform

When actually deploying to Cloud Run, I used Terraform to codify the infrastructure. It is placed in the terraform/environments/stg directory. Create a .tfvars file and specify the project ID and backend bucket name.

project_id          = "project-id"
backend_bucket_name = "tfstate-bucket"

Then, just by running terraform apply -var-file=".tfvars", the Cloud Run environment will be built. However, building the image and registering secrets to Secret Manager need to be done manually.

Image Building

Specify the PROJECT_ID and build the image using Cloud Build.

export PROJECT_ID="project-id"
gcloud builds submit . --project=$PROJECT_ID --config build_image.yaml

I'm not sure if this is the correct way to write it, but I'll put it as build_image.yaml.

Summary

With this implementation, the following were achieved:

  1. Support for Distributed Tracing - Propagation of trace information compliant with W3C TraceContext becomes possible
  2. Realization of Structured Logging - Output in JSON format makes searching and analysis easier
  3. Integration with Google Cloud Logging - Outputting Trace ID in Cloud Logging's format improves log filtering
  4. Improved Application Observability - Tracking across the entire request becomes possible
  5. Compatibility with Standard Logs - Maintains compatibility with existing log collection and analysis tools

Through the implementation of structured logging in Rails 8, I introduced a method for Trace ID integration utilizing SemanticLogger and custom middleware. This implementation contributes to the integration of logs and improvement of traceability in microservice architecture, and is useful for streamlining debugging and performance analysis.

As explained in the code reading by bisque, Rails' request_id mechanism is realized by the cooperation of middleware, thread-local variables, and tagged loggers. In this implementation, while utilizing that mechanism, I aimed to integrate it with W3C TraceContext.

Part of this article was written by an LLM, and the LLM wrote, "As for future prospects, we are considering enhancing monitoring functions, such as collecting more detailed metrics and linking with alert settings. We are also considering integration with OpenTelemetry." I don't know if I will do such things.

As an individual human, I also thought it might be good to add fields by referring to Cloud Logging 構造化ログの特別な JSON フィールドまとめ(Summary of Special JSON Fields for Cloud Logging Structured Logs).

Reference Resources