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 (in milliseconds)
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 (in milliseconds)
event.payload # => {:extra=>information}
Rails.logger.info "#{event} Received!"
end
您也可以訂閱符合正規表示式的事件。這讓您可以一次訂閱多個事件。以下是訂閱來自 ActionController
的所有事件的方法
ActiveSupport::Notifications.subscribe(/action_controller/) do |*args|
# inspect all ActionController events
end
在 Ruby on Rails 架構中,為常見事件提供了許多掛鉤。這些事件及其有效負載的詳細資訊如下。
金鑰 |
值 |
:controller |
控制器名稱 |
:action |
動作 |
:request |
ActionDispatch::Request 物件 |
: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 |
HTTP 回應碼 |
:location |
要重新導向到的 URL |
:request |
ActionDispatch::Request 物件 |
{
status: 302,
location: "https://127.0.0.1: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'
}
金鑰 |
值 |
:status |
HTTP 回應碼 |
:location |
要重新導向到的 URL |
:request |
ActionDispatch::Request 物件 |
金鑰 |
值 |
:request |
ActionDispatch::Request 物件 |
金鑰 |
值 |
: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
鍵。
{
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 寶石產生 |
: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 |
產生的網址 |
僅在使用 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 |
報告不建議使用的寶石名稱 |
:deprecation_horizon |
將移除不建議使用行為的版本 |
如果在任何儀器化過程中發生例外,酬載將包含有關它的資訊。
金鑰 |
值 |
:exception |
包含兩個元素的陣列。例外類別名稱和訊息 |
:exception_object |
例外物件 |
新增自己的事件也很容易。Active Support 會為您處理所有繁重的工作。只需呼叫 ActiveSupport::Notifications.instrument
,並提供 name
、payload
和區塊。通知會在區塊傳回後傳送。Active Support 會產生開始和結束時間,並新增儀器的唯一 ID。傳遞到 instrument
呼叫的所有資料都會傳遞到 payload 中。
以下是範例
ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
# do your custom stuff here
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
的事件。
回饋
我們鼓勵您協助提升本指南的品質。
如果您發現任何錯字或事實錯誤,請協助我們修正。首先,您可以閱讀我們的 文件貢獻 部分。
您也可能會發現不完整或過時的內容。請為 main 新增任何遺漏的文件。請務必先查看 Edge Guides,以驗證問題是否已在 main 分支中修正。查看 Ruby on Rails Guides Guidelines 以了解樣式和慣例。
如果您發現需要修正的內容,但無法自行修正,請 開啟問題。
最後,歡迎在 官方 Ruby on Rails 論壇 上討論任何與 Ruby on Rails 文件相關的議題。