Инструментарий Active Support
Active Support — часть ядра Rails, которая предоставляет расширение языка Ruby, утилиты и другие возможности. Она включает инструментарий API, который может использоваться внутри приложения, для отслеживания определенных действий, которые возникают как в коде Ruby, так и внутри приложения Rails и самого фреймворка. Однако, она не ограничена Rails. При необходимости ее можно независимо использовать в других скриптах Ruby если вы желаете.
В этом руководстве вы научитесь использовать инструменты Active Support API для отслеживания событий внутри Rails или другого Ruby кода.
После прочтения данного руководства вы будете знать:
- Какой инструментарий предоставляется.
- Какие есть хуки внутри фреймворка Rails для инструментария.
- О добавлении подписчиков к хукам.
- О построении произвольной реализации инструментария.
Введение в инструментарий
Инструментарий API, предоставленный Active Support, позволяет разработчикам создавать хуки, которыми могут пользоваться другие разработчики. Некоторые из них присутствуют в фреймворке Rails, как показано ниже. С этим API, разработчики могут быть оповещены при возникновении определенного события в их приложении или другом коде Ruby.
Например, есть хук внутри Active Record который вызывается каждый раз когда Active Record использует запрос SQL к базе данных. На этот хук можно подписаться и использовать его для отслеживания количества запросов в течении определенного экшна. Есть другой хук, оборачивающий экшны контроллеров. Он может быть использован, например, для отслеживания, как долго выполнялся определенный экшн.
Вы даже можете создать свои собственные события внутри приложения, на которые вы потом сможете подписаться.
Хуки фреймворка Rails
Внутри фреймворка Ruby on Rails присутствует множество хуков для обычных событий. Они описываются ниже.
Action Controller
write_fragment.action_controller
| Ключ |
Значение |
:key |
Полный ключ |
{
key: 'posts/1-dashboard-view'
}
read_fragment.action_controller
| Ключ |
Значение |
:key |
Полный ключ |
{
key: 'posts/1-dashboard-view'
}
expire_fragment.action_controller
| Ключ |
Значение |
:key |
Полный ключ |
{
key: 'posts/1-dashboard-view'
}
exist_fragment?.action_controller
| Ключ |
Значение |
:key |
Полный ключ |
{
key: 'posts/1-dashboard-view'
}
write_page.action_controller
| Ключ |
Значение |
:path |
Полный путь |
{
path: '/users/1'
}
expire_page.action_controller
| Ключ |
Значение |
:path |
Полный путь |
{
path: '/users/1'
}
start_processing.action_controller
| Ключ |
Значение |
:controller |
Имя контроллера |
:action |
Экшн |
:params |
Хэш параметров запроса без фильтрации параметров |
:headers |
Заголовки запроса |
:format |
html/js/json/xml и.т.д. |
:method |
Метод HTTP запроса |
:path |
Путь запроса |
{
controller: "PostsController",
action: "new",
params: { "action" => "new", "controller" => "posts" },
headers:
format: :html,
method: "GET",
path: "/posts/new"
}
process_action.action_controller
| Ключ |
Значение |
:controller |
Имя контроллера |
:action |
Экшн |
:params |
Хэш параметров запроса без фильтрации параметров |
:headers |
Заголовки запроса |
:format |
html/js/json/xml и.т.д. |
:method |
Метод HTTP запроса |
:path |
Путь запроса |
:status |
Код статуса HTTP |
:view_runtime |
Количество времени, потраченного во вьюхе |
:db_runtime |
Время, потраченное на выполнение запросов к БД в мс |
{
controller: "PostsController",
action: "index",
params: {"action" => "index", "controller" => "posts"},
headers:
format: :html,
method: "GET",
path: "/posts",
status: 200,
view_runtime: 46.848,
db_runtime: 0.157
}
send_file.action_controller
| Ключ |
Значение |
:path |
Полный путь к файлу |
INFO. Дополнительные ключи могут быть добавлены при вызове.
send_data.action_controller
ActionController не имеет какой-либо конкретной информации при загрузке. Все опции передаются через загрузку.
redirect_to.action_controller
| Ключ |
Значение |
:status |
Код HTTP ответа |
:location |
URL для переадресации |
{
status: 302,
location: "http://localhost:3000/posts/new"
}
halted_callback.action_controller
| Ключ |
Значение |
:filter |
Фильтр, прервавший экшн |
{
filter: ":halting_filter"
}
unpermitted_parameters.action_controller
| Ключ |
Значение |
:keys |
Неразрешенные ключи |
Action View
render_template.action_view
| Ключ |
Значение |
:identifier |
Полный путь до шаблона |
:layout |
Применяемый макет |
{
identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb",
layout: "layouts/application"
}
render_partial.action_view
| Ключ |
Значение |
:identifier |
Полный путь до шаблона |
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb",
}
render_collection.action_view
| Ключ |
Значение |
:identifier |
Полный путь к шаблону |
:count |
Размер коллекции |
:cache_hits |
Количество партиалов, извлеченных из кэша |
:cache_hits включается, только если коллекция рендерится с cached: true.
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb",
count: 3,
cache_hits: 0
}
Active Record
sql.active_record
| Ключ |
Значение |
:sql |
Выражение SQL |
:name |
Имя операции |
:connection_id |
self.object_id |
:binds |
Связанные параметры |
:cached |
true если использованы кэшированные запросы |
INFO. Адаптеры будут добавлять свои собственные данные.
{
sql: "SELECT \"posts\".* FROM \"posts\" ",
name: "Post Load",
connection_id: 70307250813140,
binds: []
}
instantiation.active_record
| Key |
Value |
:record_count |
Количество записей |
:class_name |
Класс записи |
{
record_count: 1,
class_name: "User"
}
Action Mailer
receive.action_mailer
| Ключ |
Значение |
:mailer |
Имя класса рассыльщика |
:message_id |
ID сообщения, создается Mail гемом |
:subject |
Тема сообщения |
:to |
Адресат(ы) сообщения |
:from |
Отправитель сообщения |
:bcc |
BCC адреса сообщения |
:cc |
CC адреса сообщения |
:date |
Дата сообщения |
:mail |
Кодированная форма сообщения |
{
mailer: "Notification",
message_id: "[email protected]",
subject: "Rails Guides",
to: ["[email protected]", "[email protected]"],
from: ["[email protected]"],
date: Sat, 10 Mar 2012 14:18:09 +0100,
mail: "..."
}
deliver.action_mailer
| Ключ |
Значение |
:mailer |
Имя класса рассыльщика |
:message_id |
ID сообщения, создается Mail гемом |
:subject |
Тема сообщения |
:to |
Адресат(ы) сообщения |
:from |
Отправитель сообщения |
:bcc |
BCC адреса сообщения |
:cc |
CC адреса сообщения |
:date |
Дата сообщения |
:mail |
Кодированная форма сообщения |
{
mailer: "Notification",
message_id: "[email protected]",
subject: "Rails Guides",
to: ["[email protected]", "[email protected]"],
from: ["[email protected]"],
date: Sat, 10 Mar 2012 14:18:09 +0100,
mail: "..."
}
process.action_mailer
| Ключ |
Значение |
:mailer |
Имя класса рассыльщика |
:action |
Экшн |
:args |
Аргументы |
{
mailer: "Notification",
action: "welcome_email",
args: []
}
Active Support
cache_read.active_support
| Ключ |
Значение |
:key |
Ключ, используемый при хранении |
:hit |
Если это чтение успешно |
:super_operation |
:fetch добавляется когда чтение используется с #fetch |
cache_generate.active_support
Это событие используется только когда #fetch вызывается с блоком.
| Ключ |
Значение |
:key |
Ключ, используемый при хранении |
INFO. Опции, переданные в вызов, будут объединены с информацией при записи в хранилище.
{
key: 'name-of-complicated-computation'
}
cache_fetch_hit.active_support
Это событие используется только когда #fetch вызывается с блоком.
| Ключ |
Значение |
:key |
Ключ, используемый при хранении |
INFO. Опции, переданные в вызов, будут объединены с информацией.
{
key: 'name-of-complicated-computation'
}
cache_write.active_support
| Ключ |
Значение |
:key |
Ключ, используемый при хранении |
INFO. Кэш хранилище может добавить свой собственный ключ.
{
key: 'name-of-complicated-computation'
}
cache_delete.active_support
| Ключ |
Значение |
:key |
Ключ, используемый при хранении |
{
key: 'name-of-complicated-computation'
}
cache_exist?.active_support
| Ключ |
Значение |
:key |
Ключ, используемый при хранении |
{
key: 'name-of-complicated-computation'
}
Active Job
enqueue_at.active_job
| Key |
Value |
:adapter |
Объект QueueAdapter, обрабатывающий задание |
:job |
Объект задания |
enqueue.active_job
| Key |
Value |
:adapter |
Объект QueueAdapter, обрабатывающий задание |
:job |
Объект задания |
| Key |
Value |
:adapter |
Объект QueueAdapter, обрабатывающий задание |
:job |
Объект задания |
| Key |
Value |
:adapter |
Объект QueueAdapter, обрабатывающий задание |
:job |
Объект задания |
Action Cable
| Ключ |
Значение |
:channel_class |
Имя класса канала |
:action |
Экшн |
:data |
Данные хэша |
transmit.action_cable
| Ключ |
Значение |
:channel_class |
Имя класса канала |
:data |
Данные хэша |
:via |
С помощью |
transmit_subscription_confirmation.action_cable
| Ключ |
Значение |
:channel_class |
Имя класса канала |
transmit_subscription_rejection.action_cable
| Ключ |
Значение |
:channel_class |
Имя класса канала |
broadcast.action_cable
| Ключ |
Значение |
:broadcasting |
Имя трансляции |
:message |
Сообщение хэша |
:coder |
Кодировщик |
Active Storage (Rails 5.2)
service_upload.active_storage
| Ключ |
Значение |
:key |
Защищенный токен |
:service |
Имя сервиса |
:checksum |
Контрольная сумма для обеспечения целостности |
service_streaming_download.active_storage
| Ключ |
Значение |
:key |
Защищенный токен |
:service |
Имя сервиса |
service_download.active_storage
| Ключ |
Значение |
:key |
Защищенный токен |
:service |
Имя сервиса |
service_delete.active_storage
| Ключ |
Значение |
:key |
Защищенный токен |
:service |
Имя сервиса |
service_exist.active_storage
| Ключ |
Значение |
:key |
Защищенный токен |
:service |
Имя сервиса |
:exist |
Существует или же нет файл или blob |
service_url.active_storage
| Ключ |
Значение |
:key |
Защищенный токен |
:service |
Имя сервиса |
:url |
Сгенерированный url |
Railties
load_config_initializer.railties
| Ключ |
Значение |
:initializer |
Путь к загруженному инициализатору из config/initializers |
Rails
deprecation.rails
| Ключ |
Значение |
:message |
Предупреждение устаревания |
:callstack |
Откуда предупреждение пришло |
Подписка на события
Подписаться на событие просто. Используйте ActiveSupport::Notifications.subscribe с блоком, чтобы слушать любое уведомление.
Блок получает следующие аргументы:
- Имя события
- Время начала
- Время окончания
- Уникальный ID этого события
- Информация (описывается в предыдущем разделе)
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
Rails.logger.info "#{name} Received!"
end
Определение всех этих аргументов блока каждый раз может быть утомительно. Можно легко создать ActiveSupport::Notifications::Event из блока аргументов, например:
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
event = ActiveSupport::Notifications::Event.new *args
event.name
event.duration
event.payload
Rails.logger.info "#{event} Received!"
end
В основном вас будет интересовать сама информация. Ниже приведен краткий вариант, как можно получить информацию.
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
data = args.extract_options!
data
end
Вы можете также подписаться на события, соответствующие регулярному выражению. Это позволит вам подписаться на несколько событий за раз. Вот как можно подписаться на все события ActionController.
ActiveSupport::Notifications.subscribe /action_controller/ do |*args|
end
Создание пользовательского события
Добавить свои события очень просто. ActiveSupport::Notifications будет делать всю тяжелую работу за вас. Просто вызовите instrument с name, payload и блоком. Уведомление будет отправлено после возвращения блока. ActiveSupport сгенерирует время старта и окончания и уникальный ID. Все данные переданные в вызов instrument будут выполнены в payload.
Пример:
ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
end
Теперь можно слушать это событие:
ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data|
puts data.inspect
end
Вы должны следовать соглашениям Rails при создании своих событий. Формат: event.library. Если ваше приложение отправляет Tweets, вы должны назвать событие tweet.twitter.