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