アクティブサポートが提供するインストゥルメンテーションAPIを使用すると、開発者は他の開発者がフックにフックすることができるフックを提供することができます。Railsフレームワーク内には、これらのフックがいくつかあります。このAPIを使用すると、開発者は自分のアプリケーションや他のRubyコード内で特定のイベントが発生したときに通知を受けることができます。
たとえば、Active Record内には、データベース上でActive RecordがSQLクエリを使用するたびに呼び出されるフックがあります。このフックはサブスクライブされ、特定のアクション中のクエリの数を追跡するために使用することができます。また、コントローラのアクションの処理に関する別のフックもあります。これは、特定のアクションがどれくらいの時間を要したかを追跡するために使用することができます。
また、後でサブスクライブすることができるアプリケーション内で独自のイベントを作成することもできます。
イベントにサブスクライブするのは簡単です。ActiveSupport::Notifications.subscribe
を使用して、通知をリッスンするためのブロックを使用します。
ブロックは以下の引数を受け取ります:
- イベントの名前
- 開始時刻
- 終了時刻
- イベントを発生させたインストゥルメンタの一意のID
- イベントのペイロード
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
# あなた自身のカスタムな処理
Rails.logger.info "#{name} Received! (started: #{started}, finished: #{finished})" # process_action.action_controller Received (started: 2019-05-05 13:43:57 -0800, finished: 2019-05-05 13:43:58 -0800)
end
started
とfinished
の正確な経過時間を計算するためにstarted
とfinished
の正確な経過時間を計算するためにActiveSupport::Notifications.monotonic_subscribe
[]を使用する場合は、上記と同じ引数がブロックに渡されますが、started
とfinished
は壁時計の時間ではなく、正確な単調増加の時間を持つ値になります。
ActiveSupport::Notifications.monotonic_subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
# あなた自身のカスタムな処理
Rails.logger.info "#{name} Received! (started: #{started}, finished: #{finished})" # process_action.action_controller Received (started: 1560978.425334, finished: 1560979.429234)
end
毎回ブロック引数を定義するのは面倒です。ActiveSupport::Notifications::Event
を使用して、ブロック引数からActiveSupport::Notifications::Event
を簡単に作成できます。
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
event.name # => "process_action.action_controller"
event.duration # => 10 (ミリ秒単位)
event.payload # => {:extra=>information}
Rails.logger.info "#{event} Received!"
end
また、1つの引数のみを受け取るブロックを渡すこともできます。この場合、イベントオブジェクトが渡されます。
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |event|
event.name # => "process_action.action_controller"
event.duration # => 10 (ミリ秒単位)
event.payload # => {:extra=>information}
Rails.logger.info "#{event} Received!"
end
正規表現に一致するイベントにもサブスクライブすることができます。これにより、複数のイベントに一度にサブスクライブすることができます。たとえば、ActionController
からのすべてのイベントにサブスクライブするには、次のようにします。
ActiveSupport::Notifications.subscribe(/action_controller/) do |*args|
# ActionControllerのすべてのイベントを調査する
end
Railsは、タイミング情報をWebブラウザで利用できるようにするために、Server Timingの標準を実装しています。有効にするには、環境設定(通常はdevelopment.rb
が最も使用されるため)を編集して、次のようにします。
config.server_timing = true
設定が完了したら(サーバーを再起動することも含む)、ブラウザの開発者ツールペインに移動し、ネットワークを選択してページをリロードします。その後、Railsサーバーへのリクエストを選択すると、タイミングタブでサーバータイミングが表示されます。これを行う例については、Firefoxのドキュメントを参照してください。
Ruby on Railsフレームワーク内には、一般的なイベントに対して提供されるフックがいくつかあります。これらのイベントとそのペイロードについては、以下で詳細に説明します。
キー |
値 |
:controller |
コントローラー名 |
:action |
アクション名 |
:params |
フィルタリングされていないリクエストパラメータのハッシュ |
:headers |
リクエストヘッダー |
:format |
html/js/json/xml など |
:method |
HTTP リクエストの動詞 |
:path |
リクエストパス |
{
controller: "PostsController",
action: "new",
params: { "action" => "new", "controller" => "posts" },
headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
format: :html,
method: "GET",
path: "/posts/new"
}
キー |
値 |
:controller |
コントローラー名 |
:action |
アクション名 |
:params |
フィルタリングされていないリクエストパラメータのハッシュ |
:headers |
リクエストヘッダー |
:format |
html/js/json/xml など |
:method |
HTTP リクエストの動詞 |
:path |
リクエストパス |
:request |
ActionDispatch::Request オブジェクト |
:response |
ActionDispatch::Response オブジェクト |
:status |
HTTP ステータスコード |
:view_runtime |
ビューでの実行時間(ミリ秒) |
:db_runtime |
データベースクエリの実行時間(ミリ秒) |
{
controller: "PostsController",
action: "index",
params: {"action" => "index", "controller" => "posts"},
headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
format: :html,
method: "GET",
path: "/posts",
request: #<ActionDispatch::Request:0x00007ff1cb9bd7b8>,
response: #<ActionDispatch::Response:0x00007f8521841ec8>,
status: 200,
view_runtime: 46.848,
db_runtime: 0.157
}
呼び出し元によって追加のキーが追加される場合があります。
ActionController
はペイロードに特定の情報を追加しません。すべてのオプションはペイロードに渡されます。
{
status: 302,
location: "http://localhost:3000/posts/new",
request: <ActionDispatch::Request:0x00007ff1cb9bd7b8>
}
キー |
値 |
:filter |
アクションを停止したフィルター |
{
filter: ":halting_filter"
}
キー |
値 |
:keys |
許可されていないキー |
:context |
以下のキーを持つハッシュ: :controller , :action , :params , :request |
{
key: 'posts/1-dashboard-view'
}
{
key: 'posts/1-dashboard-view'
}
{
key: 'posts/1-dashboard-view'
}
{
key: 'posts/1-dashboard-view'
}
キー |
値 |
:middleware |
ミドルウェアの名前 |
キー |
値 |
:identifier |
テンプレートへの完全なパス |
:layout |
適用されるレイアウト |
:locals |
テンプレートに渡されるローカル変数 |
{
identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb",
layout: "layouts/application",
locals: { foo: "bar" }
}
キー |
値 |
:identifier |
テンプレートへの完全なパス |
:locals |
テンプレートに渡されるローカル変数 |
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb",
locals: { foo: "bar" }
}
キー |
値 |
:identifier |
テンプレートへの完全なパス |
:count |
コレクションのサイズ |
:cache_hits |
キャッシュから取得されたパーシャルの数 |
:cache_hits
キーは、コレクションが cached: true
でレンダリングされる場合にのみ含まれます。
ruby
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb",
count: 3,
cache_hits: 0
}
キー |
値 |
:identifier |
テンプレートのフルパス |
{
identifier: "/Users/adam/projects/notifications/app/views/layouts/application.html.erb"
}
キー |
値 |
:sql |
SQLステートメント |
:name |
操作の名前 |
:connection |
コネクションオブジェクト |
:binds |
バインドパラメータ |
:type_casted_binds |
型変換されたバインドパラメータ |
:statement_name |
SQLステートメントの名前 |
:cached |
キャッシュされたクエリが使用された場合に true が追加されます |
アダプタは独自のデータを追加することもあります。
{
sql: "SELECT \"posts\".* FROM \"posts\" ",
name: "Post Load",
connection: <ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f7a838850>,
binds: [<ActiveModel::Attribute::WithCastValue:0x00007fe19d15dc00>],
type_casted_binds: [11],
statement_name: nil
}
このイベントは、config.active_record.action_on_strict_loading_violation
が :log
に設定されている場合にのみ発生します。
キー |
値 |
:owner |
strict_loading が有効になっているモデル |
:reflection |
ロードを試みた関連のリフレクション |
キー |
値 |
:record_count |
インスタンス化されたレコードの数 |
:class_name |
レコードのクラス |
{
record_count: 1,
class_name: "User"
}
キー |
値 |
:mailer |
メーラークラスの名前 |
:message_id |
メッセージのID、Mail gem によって生成される |
:subject |
メールの件名 |
:to |
メールの宛先アドレス |
:from |
メールの送信元アドレス |
:bcc |
メールのBCCアドレス |
:cc |
メールのCCアドレス |
:date |
メールの日付 |
:mail |
メールのエンコードされた形式 |
:perform_deliveries |
このメッセージの配信が行われるかどうか |
キー |
値 |
:mailer |
メーラークラスの名前 |
:action |
アクション |
:args |
引数 |
{
mailer: "Notification",
action: "welcome_email",
args: []
}
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
:hit |
この読み取りがヒットした場合は true |
:super_operation |
fetch で読み取りが行われた場合は :fetch |
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
:hits |
キャッシュヒットのキー |
:super_operation |
fetch_multi で読み取りが行われた場合は :fetch_multi |
このイベントは、fetch
がブロックとともに呼び出された場合にのみ発生します。
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
fetch
に渡されたオプションは、ストアへの書き込み時にペイロードとマージされます。
{
key: "name-of-complicated-computation",
store: "ActiveSupport::Cache::MemCacheStore"
}
このイベントは、fetch
がブロックとともに呼び出された場合にのみ発生します。
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
fetch
に渡されたオプションは、ペイロードとマージされます。
{
key: "name-of-complicated-computation",
store: "ActiveSupport::Cache::MemCacheStore"
}
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
キャッシュストアは独自のデータを追加することもあります。
{
key: "name-of-complicated-computation",
store: "ActiveSupport::Cache::MemCacheStore"
}
キー |
値 |
:key |
ストアに書き込まれたキーと値 |
:store |
ストアクラスの名前 |
このイベントは、MemCacheStore
またはRedisCacheStore
を使用している場合にのみ発生します。
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
:amount |
インクリメント量 |
{
key: "bottles-of-beer",
store: "ActiveSupport::Cache::RedisCacheStore",
amount: 99
}
このイベントは、MemcachedまたはRedisキャッシュストアを使用している場合にのみ発生します。
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
:amount |
デクリメント量 |
{
key: "bottles-of-beer",
store: "ActiveSupport::Cache::RedisCacheStore",
amount: 1
}
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
{
key: "name-of-complicated-computation",
store: "ActiveSupport::Cache::MemCacheStore"
}
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
このイベントは、RedisCacheStore
、FileStore
、またはMemoryStore
を使用している場合にのみ発生します。
キー |
値 |
:key |
使用されるキーパターン |
:store |
ストアクラスの名前 |
{
key: "posts/*",
store: "ActiveSupport::Cache::RedisCacheStore"
}
このイベントは、MemoryStore
を使用している場合にのみ発生します。
キー |
値 |
:store |
ストアクラスの名前 |
:size |
クリーンアップ前のキャッシュのエントリ数 |
{
store: "ActiveSupport::Cache::MemoryStore",
size: 9001
}
このイベントは、MemoryStore
を使用している場合にのみ発生します。
キー |
値 |
:store |
ストアクラスの名前 |
:key |
キャッシュのターゲットサイズ(バイト単位) |
:from |
プルーニング前のキャッシュのサイズ(バイト単位) |
{
store: "ActiveSupport::Cache::MemoryStore",
key: 5000,
from: 9001
}
キー |
値 |
:key |
ストアで使用されるキー |
:store |
ストアクラスの名前 |
{
key: "name-of-complicated-computation",
store: "ActiveSupport::Cache::MemCacheStore"
}
キー |
値 |
:serializer |
プライマリ(意図された)シリアライザ |
:fallback |
フォールバック(実際の)シリアライザ |
:serialized |
シリアライズされた文字列 |
:deserialized |
デシリアライズされた値 |
{
serializer: :json_allow_marshal,
fallback: :marshal,
serialized: "\x04\b{\x06I\"\nHello\x06:\x06ETI\"\nWorld\x06;\x00T",
deserialized: { "Hello" => "World" },
}
キー |
値 |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:job |
ジョブオブジェクト |
キー |
値 |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:job |
ジョブオブジェクト |
キー |
値 |
:job |
ジョブオブジェクト |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:error |
リトライの原因となったエラー |
:wait |
リトライの遅延 |
キー |
値 |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:jobs |
ジョブオブジェクトの配列 |
キー |
値 |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:job |
ジョブオブジェクト |
キー |
値 |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:job |
ジョブオブジェクト |
:db_runtime |
データベースクエリの実行にかかった時間(ミリ秒単位) |
キー |
値 |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:job |
ジョブオブジェクト |
:error |
リトライの原因となったエラー |
キー |
値 |
:adapter |
ジョブを処理するQueueAdapterオブジェクト |
:job |
ジョブオブジェクト |
:error |
破棄の原因となったエラー |
キー |
値 |
:channel_class |
チャネルクラスの名前 |
:action |
アクション |
:data |
データのハッシュ |
キー |
値 |
:channel_class |
チャネルクラスの名前 |
:data |
データのハッシュ |
:via |
経由 |
キー |
値 |
:channel_class |
チャネルクラスの名前 |
キー |
値 |
:channel_class |
チャネルクラスの名前 |
キー |
値 |
:broadcasting |
名前付きのブロードキャスト |
:message |
メッセージのハッシュ |
:coder |
コーダー |
キー |
値 |
:analyzer |
アナライザーの名前、例:ffprobe |
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
:checksum |
データの整合性を確保するためのチェックサム |
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
:range |
読み取ろうとしたバイト範囲 |
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
キー |
値 |
:prefix |
キーのプレフィックス |
:service |
サービスの名前 |
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
:exist |
ファイルまたはブロブが存在するかどうか |
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
:url |
生成されたURL |
このイベントは、Google Cloud Storageサービスを使用している場合にのみ発生します。
キー |
値 |
:key |
セキュアトークン |
:service |
サービスの名前 |
:content_type |
HTTPのContent-Type フィールド |
:disposition |
HTTPのContent-Disposition フィールド |
キー |
値 |
:mailbox |
ActionMailbox::Base を継承したMailboxクラスのインスタンス |
:inbound_email |
処理されているインバウンドメールに関するデータのハッシュ |
{
mailbox: #<RepliesMailbox:0x00007f9f7a8388>,
inbound_email: {
id: 1,
message_id: "[email protected]",
status: "processing"
}
}
キー |
値 |
:initializer |
config/initializers 内のロードされたイニシャライザのパス |
キー |
値 |
:message |
廃止予定の警告メッセージ |
:callstack |
廃止予定の元の呼び出し元 |
:gem_name |
廃止予定を報告しているgemの名前 |
:deprecation_horizon |
廃止予定の動作が削除されるバージョン |
計測中に例外が発生した場合、ペイロードに関する情報が含まれます。
キー |
値 |
:exception |
2つの要素からなる配列。例外クラス名とメッセージ |
:exception_object |
例外オブジェクト |
独自のイベントを追加することも簡単です。Active Supportがすべての重い作業を代行してくれます。単にActiveSupport::Notifications.instrument
をname
、payload
、およびブロックと共に呼び出すだけです。ブロックが返った後に通知が送信されます。Active Supportは開始時間と終了時間を生成し、計測器の固有のIDを追加します。instrument
呼び出しに渡されたすべてのデータがペイロードに含まれます。
以下は例です:
ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
# ここでカスタムの処理を行います
end
これで、次のようにこのイベントをリッスンできます:
ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data|
puts data.inspect # {:this=>:data}
end
また、ブロックを渡さずにinstrument
を呼び出すこともできます。これにより、他のメッセージング用途でインストルメンテーションインフラストラクチャを活用することができます。
ActiveSupport::Notifications.instrument "my.custom.event", this: :data
ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data|
puts data.inspect # {:this=>:data}
end
独自のイベントを定義する際には、Railsの規約に従う必要があります。フォーマットはevent.library
です。アプリケーションがツイートを送信している場合、tweet.twitter
という名前のイベントを作成する必要があります。
フィードバック
このガイドの品質向上にご協力ください。
タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。
開始するには、ドキュメントへの貢献セクションを読んでください。
不完全なコンテンツや最新でない情報も見つかるかもしれません。
メインのドキュメントに不足しているドキュメントを追加してください。
修正済みかどうかは、まずEdge Guidesを確認してください。
スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。
修正すべき点を見つけたが、自分で修正できない場合は、
問題を報告してください。
そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。