Spec-Zone.ru › Ruby on Rails 7.1

модуль ActiveSupport::Notifications

Уведомления

ActiveSupport::Notifications предоставляет API для инструментирования Ruby.

Инструментаторы

Для инструментирования события нужно сделать следующее:

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

Это сначала выполняет блок, а затем уведомляет всех подписчиков по завершении.

В примере выше render — это имя события, а остальное называется нагрузкой. Нагрузка — это механизм, позволяющий инструментаторам передавать дополнительную информацию подписчикам. Нагрузки представляют собой хэши, содержимое которых произвольно и, как правило, зависит от события.

Подписчики

Вы можете потреблять эти события и информацию, которую они предоставляют, зарегистрировав подписчика.

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

Здесь значения start и finish представляют время реальных часов. Если вас интересует точность, вы можете зарегистрировать монотонного подписчика.

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

Значения start и finish выше представляют монотонное время.

Например, давайте сохраним все события «render» в массиве:

events = []

ActiveSupport::Notifications.subscribe('render') do |*args|
  events << ActiveSupport::Notifications::Event.new(*args)
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 }

Блок в вызове subscribe получает имя события, начальную метку времени, конечную метку времени, строку с уникальным идентификатором для инструментатора этого события (что-то вроде «535801666f04d0298cd6») и хэш с нагрузкой в таком порядке.

Если во время конкретного инструментирования произойдет исключение, в нагрузке будет ключ :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, в этом случае вы подписываетесь на все события.

Временные подписки

Иногда вы не хотите подписываться на событие на протяжении всего жизненного цикла приложения. Есть два способа отписаться.

ПРЕДУПРЕЖДЕНИЕ: Фреймворк для инструментирования предназначен для подписчиков с длительным временем работы, используйте эту функцию экономно, потому что она очищает некоторые внутренние кэши, что негативно сказывается на производительности.

Подписка во время выполнения блока

Вы можете временно подписаться на какое-либо событие во время выполнения блока. Например, в

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

обработчик вызывается для всех событий «sql.active_record», инструментированных во время выполнения блока. Обработчик отписывается автоматически после этого.

Чтобы записать значения started и finished с монотонным временем, укажите необязательный параметр :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 |*args|
  ...
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 поставляется с реализацией очереди, которая потребляет и публикует события для всех подписчиков журнала. Вы можете использовать любую реализацию очереди, которую хотите.

Атрибуты

notifier[RW]

Общедоступные методы класса

instrument(name, payload = {}) { |payload| ... } Показать исходный код
# File activesupport/lib/active_support/notifications.rb, line 204
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 268
def instrumenter
  registry[notifier] ||= Instrumenter.new(notifier)
end
monotonic_subscribe(pattern = nil, callback = nil, &block) Показать исходный код
# File activesupport/lib/active_support/notifications.rb, line 253
def monotonic_subscribe(pattern = nil, callback = nil, &block)
  notifier.subscribe(pattern, callback, monotonic: true, &block)
end

Выполняет ту же функцию, что и subscribe, но аргументы блока start и finish указаны в монотонном времени вместо времени реальных часов. Монотонное время не будет перескакивать вперёд или назад (из-за NTP или перехода на летнее время). Используйте monotonic_subscribe когда точность временных интервалов имеет значение. Например, для вычисления времени, прошедшего между двумя событиями.

publish(name, *args) Показать исходный код
# File activesupport/lib/active_support/notifications.rb, line 196
def publish(name, *args)
  notifier.publish(name, *args)
end
subscribe(pattern = nil, callback = nil, &block) Показать исходный код
# File activesupport/lib/active_support/notifications.rb, line 243
def subscribe(pattern = nil, callback = nil, &block)
  notifier.subscribe(pattern, callback, monotonic: false, &block)
end

Подписывается на событие с заданным именем с переданным block.

Вы можете подписаться на события, передав String для соответствия точным именам событий или Regexp для соответствия всем событиям, которые соответствуют шаблону.

ActiveSupport::Notifications.subscribe(/render/) do |*args|
  @event = ActiveSupport::Notifications::Event.new(*args)
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/) do |event|
  @event = event
end

Вызывает ошибку, если тип имени события некорректен:

ActiveSupport::Notifications.subscribe(:render) {|*args| ...}
#=> ArgumentError (pattern must be specified as a String, Regexp or empty)
subscribed(callback, pattern = nil, monotonic: false) { || ... } Показать исходный код
# File activesupport/lib/active_support/notifications.rb, line 257
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 264
def unsubscribe(subscriber_or_name)
  notifier.unsubscribe(subscriber_or_name)
end

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API