модуль ActiveSupport::Notifications
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 поставляется с реализацией очереди, которая потребляет и публикует события всем подписчикам логов. Вы можете использовать любую реализацию очереди, которую хотите.
Атрибуты
Публичные методы класса
# File activesupport/lib/active_support/notifications.rb, line 201
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 # File activesupport/lib/active_support/notifications.rb, line 253 def instrumenter InstrumentationRegistry.instance.instrumenter_for(notifier) end
# File activesupport/lib/active_support/notifications.rb, line 238 def monotonic_subscribe(pattern = nil, callback = nil, &block) notifier.subscribe(pattern, callback, monotonic: true, &block) end
# File activesupport/lib/active_support/notifications.rb, line 197 def publish(name, *args) notifier.publish(name, *args) end
# File activesupport/lib/active_support/notifications.rb, line 234 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
# File activesupport/lib/active_support/notifications.rb, line 242 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 249 def unsubscribe(subscriber_or_name) notifier.unsubscribe(subscriber_or_name) end
© 2004–2020 David Heinemeier Hansson
Licensed under the MIT License.