跳至内容 跳至搜索

通知

ActiveSupport::Notifications 为 Ruby 提供了一个 instrumentation API。

Instrumentation

要对事件进行 instrumentation,您只需执行以下操作

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

这首先执行代码块,然后在完成后通知所有订阅者。

在上面的示例中,render 是事件的名称,其余部分称为有效负载。有效负载是一种机制,允许 instrumentation 将额外信息传递给订阅者。有效负载由一个哈希组成,其内容是任意的,通常取决于事件。

订阅者

您可以通过注册订阅者来使用这些事件及其提供的信息。

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" 进行 instrumentation 时被调用。

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)

如果在该特定 instrumentation 期间发生异常,则有效负载将包含一个名为 :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)

导致以下输出在日志中,包括一个包含有效负载的哈希

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" 事件进行 instrumentation 时被调用。回调会在之后自动取消订阅。

要使用单调时间记录 startedfinished 值,请指定 subscribed 方法的可选 :monotonic 选项。默认情况下,: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

默认队列

Notifications 附带一个队列实现,它会使用和发布事件到所有日志订阅者。您可以使用任何您想要的队列实现。

命名空间
方法
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)

执行与 subscribe 相同的功能,但 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)

使用传递的 block 订阅给定的事件名称。

您可以通过传递一个 String 来订阅事件以匹配精确的事件名称,或者通过传递一个 Regexp 来订阅所有与模式匹配的事件。

如果传递给方法的代码块只接受一个参数,它会将一个 Event 对象传递给代码块

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

否则,block 将接收五个关于事件的信息参数

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

unsubscribe(subscriber_or_name)

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