Active Support提供的儀器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|
# your own custom stuff
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
的準確性以計算精確的經過時間,則使用ActiveSupport::Notifications.monotonic_subscribe
。給定的塊將接收與上述相同的參數,但started
和finished
將具有準確的單調時間值,而不是壁掛時鐘時間。
ActiveSupport::Notifications.monotonic_subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
# your own custom stuff
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.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
您還可以傳遞一個只接受一個參數的塊,它將接收一個事件對象:
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|
# inspect all ActionController events
end
Rails實現了Server Timing標準,以在Web瀏覽器中提供時間信息。要啟用,編輯您的環境配置(通常是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: ":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'
}
鍵 |
值 |
: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 |
從快取中擷取的部分數量 |
只有在使用 cached: true
渲染集合時,才會包含 :cache_hits
鍵。
ruby
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb",
count: 3,
cache_hits: 0
}
{
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 |
郵件的密送地址 |
:cc |
郵件的抄送地址 |
:date |
郵件的日期 |
:mail |
郵件的編碼形式 |
:perform_deliveries |
是否執行此郵件的傳送 |
鍵 |
值 |
:mailer |
郵件類的名稱 |
:action |
操作的名稱 |
:args |
參數 |
{
mailer: "Notification",
action: "welcome_email",
args: []
}
鍵 |
值 |
:key |
存儲中使用的鍵 |
:store |
存儲類的名稱 |
:hit |
如果這次讀取是命中的話 |
: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 |
檔案或 blob 是否存在 |
鍵 |
值 |
:key |
安全令牌 |
:service |
服務的名稱 |
:url |
生成的 URL |
只有在使用 Google Cloud Storage 服務時才會觸發此事件。
鍵 |
值 |
:key |
安全令牌 |
:service |
服務的名稱 |
:content_type |
HTTP Content-Type 欄位 |
:disposition |
HTTP Content-Disposition 欄位 |
{
mailbox: #<RepliesMailbox:0x00007f9f7a8388>,
inbound_email: {
id: 1,
message_id: "[email protected]",
status: "processing"
}
}
鍵 |
值 |
:initializer |
在 config/initializers 中載入的初始化程式的路徑 |
鍵 |
值 |
:message |
廢棄警告訊息 |
:callstack |
廢棄警告的來源 |
:gem_name |
報告廢棄警告的 gem 的名稱 |
:deprecation_horizon |
廢棄行為將被移除的版本 |
如果在任何儀器儀表板中發生例外,載荷將包含有關它的資訊。
鍵 |
值 |
:exception |
一個包含兩個元素的陣列。例外類別名稱和訊息 |
:exception_object |
例外物件 |
添加自己的事件也很容易。Active Support 將為您處理所有繁重的工作。只需使用 name
、payload
和區塊調用 ActiveSupport::Notifications.instrument
。通知將在區塊返回後發送。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 指南,以確認問題是否已經修復或尚未在主分支上修復。
請參考 Ruby on Rails 指南指引 以了解風格和慣例。
如果您發現需要修復但無法自行修補的問題,請 開啟一個問題。
最後但同樣重要的是,關於 Ruby on Rails 文件的任何討論都非常歡迎在 官方 Ruby on Rails 論壇 上進行。