Rails 8.1.0で追加されたStructured Event Reporterについて整理してみた – Zenet Tech Blog

はじめに

ゼネットの土屋です。
先日 2025/10/22 に Rails 8.1.0 がリリースされました。

今回の記事では、Rails 8.1 で追加された注目機能 Structured Event Reporter を紹介します。
これは「Rails.logger」と「ActiveSupport::Notifications」を進化的に統合し、アプリケーションの“観測性”を大きく高める仕組みです。


Structured Event Reporter(Rails.event) とは

これまで Rails.loggerActiveSupport::Notifications などに分散していた「イベントの記録・監視」を、統一的かつ機械可読な形式で扱えるようにした仕組みです。
Rails イベントを構造化して扱えるようになり、ログや監視システム(CloudWatch や Elasticsearch など)との連携がより強力になりました。
タグ付けやコンテキストの設定にも対応しており、大規模アプリケーションでの監視・トレーシング・分析を Rails 標準で行えるようになりました。


Rails.logger・ActiveSupport::Notifications・Rails.event の違い

API 目的 出力形式 主な使い道
Rails.logger 人間向けログ テキスト 本番運用のログ監視・集約/開発・デバッグ
ActiveSupport::Notifications 内部計測 イベント パフォーマンス監視/APM 連携
Rails.event(新機能) 構造化イベント JSON などの構造化データ ログ集約・観測・監査/分散トレーシングの文脈付与

それぞれの違いを実例で見てみましょう。


Rails.logger:最もシンプルなログ出力

Rails.logger.info("User signed up: id=123, email=user@example.com")

出力は人間が読む前提のテキスト形式です(構造を持たないため後処理がしにくい点に注意):

INFO -- : User signed up: id=123, email=user@example.com

特徴

  • シンプルで導入しやすい
  • しかし構造がないため、分析や集計には不向き
  • 「どのリクエストで」「誰が」「いつ」などの文脈を自動で持たない

ActiveSupport::Notifications:内部イベントの発火とsubscribe

ActiveSupport::Notifications.instrument("user.signup", id: 123) do
  
end

subscribe 側で処理を定義できます:

ActiveSupport::Notifications.subscribe("user.signup") do |name, start, finish, id, payload|
  puts "#{name} took #{(finish - start).round(3)}s: #{payload.inspect}"
end

特徴

  • Rails 内部(DB、View、Cache など)もこの仕組みで通知している
  • 処理時間の計測などに強い
  • ただし、統一的なデータ構造や文脈共有が弱い

Rails.event:構造化イベントとして統一的に扱う

Rails.event.set_context(request_id: "req-42", user_id: 123)

Rails.event.tagged("signup") do
  Rails.event.notify("user.signup", email: "user@example.com")
end

Subscriber を登録してイベントを受け取ります:

class JsonSubscriber
  def emit(event)
    Rails.logger.info(event)
  end
end

Rails.event.subscribe(JsonSubscriber.new)

以下は、Rails.event.notify の呼び出しによって出力される JSON 構造の一例です。

{
  "name": "user.signup",
  "payload": { "email": "user@example.com" },
  "tags": { "signup": true },
  "context": { "request_id": "req-42", "user_id": 123 },
  "timestamp": "2025-10-20T12:34:56Z",
  "source_location": {
    "filepath": "app/controllers/users_controller.rb",
    "lineno": 42
  }
}

特徴

  • 完全に構造化されたイベントとして出力可能
  • タグやコンテキストを付与して システム横断的なトレース ができる
  • JSON 形式のため、CloudWatch や Elasticsearch などに容易に連携できる

Rails.event の仕組みを理解する:イベントはどう構築されるのか

Structured Event Reporter は、単に「イベントを通知する API」ではありません。
内部では ActiveSupport::EventReporter::Event オブジェクト を生成し、その中に「アプリケーションで起きた出来事」を構造化して保持します。


Rails.event.notify の内部で何が起きているか

例えば、次のようなコードを考えてみましょう。

before_action do
  Rails.event.set_context(request_id: "abc123", user_id: 42)
end


def create
  
  Rails.event.notify("user.signup", email: "user@example.com")
end

これは 「user.signup というイベントを発行する」 という操作を行い、
Rails 内部では次のような Event オブジェクトが生成されます。


{
  name: "user.signup",                            
  payload: { email: "user@example.com" },         
  context: { request_id: "abc123", user_id: 42 }, 
  tags: {},                                       
  timestamp: "2025-10-20T13:55:00Z",              
  source_location: {
    filepath: "app/controllers/users_controller.rb",
    lineno: 5
  }                                               
}

この構造体は、Subscriber にそのまま渡されます。


イベントを受け取るSubscriber

class JsonLogger
  def emit(event)
    Rails.logger.info(event)
  end
end

Rails.event.subscribe(JsonLogger.new)

このSubscriber を登録しておくと、notify が呼ばれるたびに
上記の event オブジェクトが JSON に変換され、ログ出力されます。


出力結果の例

{
  "name": "user.signup",
  "payload": { "email": "user@example.com" },
  "context": { "request_id": "abc123", "user_id": 42 },
  "tags": {},
  "timestamp": "2025-10-20T13:55:00Z",
  "source_location": {
    "filepath": "app/controllers/users_controller.rb",
    "lineno": 5
  }
}

このように、notify の引数で渡した email だけでも、
Rails は自動的に contexttimestampsource_location を補完してくれます。


各フィールドの意味

フィールド名 生成元 内容 自動/手動
name notify の第 1 引数 イベント名(例: "user.signup" 手動
payload notify のキーワード引数 イベント固有データ 手動
context Rails.event.set_context(...) リクエストやユーザーなど共通情報 自動
tags Rails.event.tagged(...) イベントの分類(例: "graphql" 自動
timestamp Rails 内部 イベント発火時刻(UTC) 自動
source_location Rails 内部 ファイルパスと行番号 自動

仕組みをまとめると

notify() でイベントを送る
      ↓
ActiveSupport::EventReporter::Event が生成される
      ↓
context / tags / timestamp / source_location が自動付与される
      ↓
Subscriber#emit に渡される
      ↓
ログや監視ツールへ送信される

この流れにより、アプリケーション全体の“出来事”を
構造化された一貫したフォーマットで観測できるようになっています。


まとめ

  • Rails.event.notify は「名前+データ」だけを渡せば OK
  • context(文脈)と timestamp(時刻)は Rails が自動で補完
  • Subscriber を通じて、ログ・監視・トレースなどに容易に統合可能
  • 「ログを書く」ではなく「イベントを発行する」 という発想への転換が、Rails 8.1 以降の観測性向上の鍵となります。




Source link

関連記事

コメント

この記事へのコメントはありません。