Spec-Zone.ru › Ruby on Rails 8.1

модуль ActiveSupport::Notifications

Уведомления

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

Инструментирование

Чтобы инструментировать событие, достаточно выполнить следующее:

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

Сначала выполняется блок, а затем, после его завершения, оповещаются все подписчики.

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

Подписчики

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

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

Объекты Event записывают время работы процессора и количество выделений памяти. Если вам это не нужно, можно также передать блок, принимающий пять аргументов:

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   # => 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 принимает переданные аргументы и предоставляет объектно-ориентированный интерфейс для работы с этими данными.

В качестве второго параметра метода subscribe вместо блока также можно передать объект, отвечающий методу call:

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 {|event| ... }
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 |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 включает реализацию очереди, которая получает события и передаёт их всем подписчикам журналов. Вы можете использовать любую реализацию очереди.

Атрибуты

notifier [RW]

Открытые методы класса

instrument (name, payload = {}) { |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) Показать исходный код
# 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

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

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) Показать исходный код
# File activesupport/lib/active_support/notifications.rb, line 244
def subscribe(pattern = nil, callback = nil, &block)
  notifier.subscribe(pattern, callback, monotonic: false, &block)
end

Подписывается на событие с указанным именем, используя переданный 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)
subscribed (callback, pattern = nil, monotonic: false) { || ... } Показать исходный код
# 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

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

Spec-Zone.ru

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