module 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 может принимать аргументы по мере их поступления и предоставлять объектно-ориентированный интерфейс к этим данным.
Также можно передать объект, который отвечает на метод 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 {|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 поставляется с реализацией очереди, которая потребляет и публикует события всем подписчикам журналов. Вы можете использовать любую реализацию очереди, которую хотите.
Атрибуты
Открытые методы класса
# 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 # File activesupport/lib/active_support/notifications.rb, line 269 def instrumenter registry[notifier] ||= Instrumenter.new(notifier) end
# 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 , когда точность продолжительности времени имеет значение. Например, для вычисления времени, прошедшего между двумя событиями.
# File activesupport/lib/active_support/notifications.rb, line 200 def publish(name, *args) notifier.publish(name, *args) end
# 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) # 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
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.