跳到內容 跳到搜尋

通知物件

ActiveSupport::Notifications提供一個 Ruby 用的 instrumentation API。

工頭

要量測一個事件,你只要做

ActiveSupport::Notifications.instrument('render', extra: :information) do
  render plain: 'Foo'
end

這會先執行區塊,然後在執行完時通知所有訂閱者。

在上面的範例裡,render 是這個事件的名稱,其餘的稱為酬載。酬載機制讓工頭可以將額外的資訊傳給訂閱者。酬載由一個 hash 構成,其內容是任意的,且通常會依事件而定。

訂閱者

你可以透過註冊訂閱者來使用這些事件和它們提供的資訊。

ActiveSupport::Notifications.subscribe('render') do |event|
  event.name          # => "render"
  event.duration      # => 10 (in milliseconds)
  event.payload       # => { extra: :information }
  event.allocations   # => 1826 (objects)
end

Event 物件會記錄 CPU 時間和分配。如果你不需要這個功能,也可以傳遞一個區塊,這個區塊接受五個參數

ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
  name    # => String, name of the event (such as 'render' from above)
  start   # => Time, when the instrumented block started execution
  finish  # => Time, when the instrumented block ended execution
  id      # => String, unique ID for the instrumenter that fired the event
  payload # => Hash, the payload
end

這裡,startfinish 值代表時鐘時間。如果你有準確度上的疑慮,可以註冊一個單調訂閱者。

ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
  name    # => String, name of the event (such as 'render' from above)
  start   # => Float, monotonic time when the instrumented block started execution
  finish  # => Float, monotonic time when the instrumented block ended execution
  id      # => String, unique ID for the instrumenter that fired the event
  payload # => Hash, the payload
end

例如,我們來用一個陣列儲存所有的「render」事件

events = []

ActiveSupport::Notifications.subscribe('render') do |event|
  events << event
end

這段程式碼可以立即回傳,你只是在訂閱「render」事件。這個區塊會被儲存,且每當有人量測「render」時就會被呼叫

ActiveSupport::Notifications.instrument('render', extra: :information) do
  render plain: 'Foo'
end

event = events.first
event.name          # => "render"
event.duration      # => 10 (in milliseconds)
event.payload       # => { extra: :information }
event.allocations   # => 1826 (objects)

如果在那個特定量測期間發生例外,酬載會有個鍵 :exception,其值是一個有兩個元素的陣列:一個字串代表例外類別,和一個例外訊息。酬載的 :exception_object 鍵會有這個例外的本身做為其值

event.payload[:exception]         # => ["ArgumentError", "Invalid value"]
event.payload[:exception_object]  # => #<ArgumentError: Invalid value>

正如早期範例所示,類別 ActiveSupport::Notifications::Event 可以照它們來的情況處理參數,並提供一個物件導向的介面來處理這些資料。

也可以傳遞一個回應 call 方法的物件,當成 subscribe 方法的第二個參數,取代區塊

module ActionController
  class PageRequest
    def call(name, started, finished, unique_id, payload)
      Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
    end
  end
end

ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)

這會在日誌內產生下列輸出,包括一個有酬載的 hash

notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
   controller: "Devise::SessionsController",
   action: "new",
   params: {"action"=>"new", "controller"=>"devise/sessions"},
   format: :html,
   method: "GET",
   path: "/login/sign_in",
   status: 200,
   view_runtime: 279.3080806732178,
   db_runtime: 40.053
 }

你也可以訂閱符合某個正則表示式名稱的所有事件

ActiveSupport::Notifications.subscribe(/render/) do |*args|
  ...
end

甚至不傳遞任何參數給 subscribe,這樣你就會訂閱所有事件。

暫時訂閱

有時候你不會想要在應用程式整個生命週期訂閱某個事件。有兩種取消訂閱的方法。

警告:此 instrumentation 架構是為常駐式訂閱者所設計,請謹慎使用這個功能,因為它會清除一些內部快取,這會對效能有負面影響。

在區塊執行期間訂閱

你可以暫時訂閱某個事件,在某個區塊執行期間。例如,在

callback = lambda {|event| ... }
ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
  ...
end

所有在區塊執行期間量測的「sql.active_record」事件的 callback 都會被呼叫。callback 會在之後自動取消訂閱。

要使用單調的時間記錄 startedfinished 值,請指定選用的 :monotonic 選項給 subscribed 方法。預設 :monotonic 選項設定為 false

callback = lambda {|name, started, finished, unique_id, payload| ... }
ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do
  ...
end

手動取消訂閱

subscribe 方法會回傳一個訂閱者物件

subscriber = ActiveSupport::Notifications.subscribe("render") do |event|
  ...
end

若要阻止這個區塊再被呼叫,只需傳遞該參照就能取消訂閱

ActiveSupport::Notifications.unsubscribe(subscriber)

您也可以透過傳遞訂閱者物件的名稱來取消訂閱。請注意,這會取消所有與給定名稱相符的訂閱

ActiveSupport::Notifications.unsubscribe("render")

使用正規表示法或其他模式匹配物件的訂閱者將會持續訂閱所有符合其原始模式的事件,除非這些事件符合傳遞給 unsubscribe 的字串

subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
ActiveSupport::Notifications.unsubscribe('render_template.action_view')
subscriber.matches?('render_template.action_view') # => false
subscriber.matches?('render_partial.action_view') # => true

預設佇列

通知 附帶一個佇列實作,用於消費和發布事件至所有日誌訂閱者。您可以使用任何您想要的佇列實作。

命名空間
方法
I
M
P
S
U

屬性

[RW] notifier

類別公開方法

instrument(name, payload = {})

# File activesupport/lib/active_support/notifications.rb, line 208
def instrument(name, payload = {})
  if notifier.listening?(name)
    instrumenter.instrument(name, payload) { yield payload if block_given? }
  else
    yield payload if block_given?
  end
end

instrumenter()

# File activesupport/lib/active_support/notifications.rb, line 269
def instrumenter
  registry[notifier] ||= Instrumenter.new(notifier)
end

monotonic_subscribe(pattern = nil, callback = nil, &block)

執行與訂閱相同的功能,但 startfinish 區塊參數以單調時間呈現,而非以絕對時間呈現。單調時間不會向前或向後跳動(因為 NTP 或夏令時間)。在時間持續性很重要的時候,請使用 monotonic_subscribe。例如,計算兩個事件之間的經過時間。

# File activesupport/lib/active_support/notifications.rb, line 254
def monotonic_subscribe(pattern = nil, callback = nil, &block)
  notifier.subscribe(pattern, callback, monotonic: true, &block)
end

publish(name, *args)

# File activesupport/lib/active_support/notifications.rb, line 200
def publish(name, *args)
  notifier.publish(name, *args)
end

subscribe(pattern = nil, callback = nil, &block)

使用傳遞的 區塊 訂閱給定的事件名稱。

您可以透過傳遞 字串 來訂閱事件,以符合確切的事件名稱,或透過傳遞 正規表示法 來符合所有符合模式的事件。

如果傳遞給方法的區塊只接受一個參數,它會將 事件 物件傳遞給區塊

ActiveSupport::Notifications.subscribe(/render/) do |event|
  @event = event
end

否則 區塊 會收到五個參數,其中包含有關事件的資訊

ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
  name    # => String, name of the event (such as 'render' from above)
  start   # => Time, when the instrumented block started execution
  finish  # => Time, when the instrumented block ended execution
  id      # => String, unique ID for the instrumenter that fired the event
  payload # => Hash, the payload
end

如果傳遞了無效的事件名稱型別,會擲回錯誤

ActiveSupport::Notifications.subscribe(:render) {|event| ...}
#=> ArgumentError (pattern must be specified as a String, Regexp or empty)
# File activesupport/lib/active_support/notifications.rb, line 244
def subscribe(pattern = nil, callback = nil, &block)
  notifier.subscribe(pattern, callback, monotonic: false, &block)
end

subscribed(callback, pattern = nil, monotonic: false, &block)

# File activesupport/lib/active_support/notifications.rb, line 258
def subscribed(callback, pattern = nil, monotonic: false, &block)
  subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic)
  yield
ensure
  unsubscribe(subscriber)
end

取消訂閱(訂閱者或名稱)

# File activesupport/lib/active_support/notifications.rb, line 265
def unsubscribe(subscriber_or_name)
  notifier.unsubscribe(subscriber_or_name)
end