по-русски

Инструметарий Active Support

Active Support — часть ядра Rails, которая предоставляет расширение языка Ruby, утилиты и другие возможности. Она включает инструментарий API, который может использоваться внутри приложения, для отсле

Active Support — часть ядра Rails, которая предоставляет расширение языка Ruby, утилиты и другие возможности. Она включает инструментарий API, который может использоваться внутри приложения, для отслеживания определенных действий, которые возникают как в коде Ruby, так и внутри приложения Rails и самого фреймворка. Однако, она не ограничена Rails. При необходимости ее можно независимо использовать в других скриптах Ruby если вы желаете.

В этом руководстве вы научитесь использовать API инструментария Active Support для отслеживания событий внутри Rails или другого Ruby-кода.

После прочтения данного руководства вы будете знать:

  • Какой инструментарий предоставляется.
  • Как добавить подписчика к хуку.
  • Как увидеть тайминги от инструментария в браузере.
  • Какие есть хуки внутри фреймворка Rails для инструментария.
  • Как создать произвольную реализацию инструментария.

Введение в инструментарий

Инструментарий API, предоставленный Active Support, позволяет разработчикам создавать хуки, которыми могут пользоваться другие разработчики. Некоторые из них присутствуют в фреймворке Rails. С этим API, разработчики могут быть оповещены при возникновении определенного события в их приложении или другом коде Ruby.

Например, есть хук внутри Active Record который вызывается каждый раз когда Active Record использует запрос SQL к базе данных. На этот хук можно подписаться и использовать его для отслеживания количества запросов в течении определенного экшна. Есть другой хук, оборачивающий экшны контроллеров. Он может быть использован, например, для отслеживания, как долго выполнялся определенный экшн.

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

Подписка на события

Подписаться на событие просто. Используйте ActiveSupport::Notifications.subscribe с блоком, чтобы слушать любое уведомление.

Блок получает следующие аргументы:

  • Имя события
  • Время начала
  • Время окончания
  • Уникальный ID для инструментария, запустившего это событие
  • Полезная нагрузка для события
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
  # ваш собственный код
  Rails.logger.info "#{name} Received! (started: #{started}, finished: #{finished})" # process_action.action_controller Received (started: 2019-05-05 13:43:57 -0800, finished: 2019-05-05 13:43:58 -0800)
end

Если вы беспокоитесь об аккуратности started и finished для вычисления точного прошедшего времени, используйте ActiveSupport::Notifications.monotonic_subscribe. Преданный блок получает те же аргументы, что и предыдущий, но started и finished получит значения более аккуратного монотонного времени вместо секундного дискретного времени.

ActiveSupport::Notifications.monotonic_subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
  # ваш собственный код
  Rails.logger.info "#{name} Received! (started: #{started}, finished: #{finished})" # process_action.action_controller Received (started: 1560978.425334, finished: 1560979.429234)
end

Определение всех этих аргументов блока каждый раз может быть утомительно. Можно легко создать ActiveSupport::Notifications::Event из блока аргументов, например:

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)

  event.name      # => "process_action.action_controller"
  event.duration  # => 10 (in milliseconds)
  event.payload   # => {:extra=>information}

  Rails.logger.info "#{event} Received!"
end

Вы можете также подписаться на события, соответствующие регулярному выражению. Это позволит вам подписаться на несколько событий за раз. Вот как можно подписаться на все события ActionController:

ActiveSupport::Notifications.subscribe(/action_controller/) do |*args|
  # Проверка всех событий ActionController
end

Просмотр таймингов от инструментария в браузере

Rails реализует стандарт Server Timing, чтобы сделать информацию о тайминге доступной в веб-браузере. Чтобы включить, отредактируйте конфигурацию среды (обычно development.rb, так как это используется в основном в development), чтобы включить следующее:

  config.server_timing = true

Как только настроено (включая перезагрузку вашего сервера), можно пойти в панель Developer Tools вашего браузера, затем выбрать Network и перезагрузить вашу страницу. Затем можно выбрать любой запрос к серверу Rails, и увидеть тайминги сервера во вкладке таймингов. Пример этого можно увидеть в документации Firefox.

Хуки фреймворка Rails

Внутри фреймворка Ruby on Rails присутствует множество хуков для обычных событий. Эти события и их нагрузка описываются ниже.

Action Controller

start_processing.action_controller

КлючЗначение
:controllerИмя контроллера
:actionЭкшн
:paramsХэш параметров запроса без фильтрации параметров
:headersЗаголовки запроса
:formathtml/js/json/xml и.т.д.
:methodМетод HTTP-запроса
:pathПуть запроса
{
  controller: "PostsController",
  action: "new",
  params: { "action" => "new", "controller" => "posts" },
  headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
  format: :html,
  method: "GET",
  path: "/posts/new"
}

process_action.action_controller

КлючЗначение
:controllerИмя контроллера
:actionЭкшн
:paramsХэш параметров запроса без фильтрации параметров
:headersЗаголовки запроса
:formathtml/js/json/xml и.т.д.
:methodМетод HTTP-запроса
:pathПуть запроса
:requestОбъект ActionDispatch::Request
:responseОбъект ActionDispatch::Response
:statusКод статуса HTTP
:view_runtimeКоличество времени, потраченного во вью
:db_runtimeВремя, потраченное на выполнение запросов к БД в мс
{
  controller: "PostsController",
  action: "index",
  params: {"action" => "index", "controller" => "posts"},
  headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
  format: :html,
  method: "GET",
  path: "/posts",
  request: #<ActionDispatch::Request:0x00007ff1cb9bd7b8>,
  response: #<ActionDispatch::Response:0x00007f8521841ec8>,
  status: 200,
  view_runtime: 46.848,
  db_runtime: 0.157
}

send_file.action_controller

КлючЗначение
:pathПолный путь к файлу

Дополнительные ключи могут быть добавлены при вызове.

send_data.action_controller

ActionController не добавляет какой-либо конкретной информации при загрузке. Все опции передаются через полезную нагрузку (payload).

redirect_to.action_controller

КлючЗначение
:statusКод HTTP ответа
:locationURL для переадресации
:requestОбъект ActionDispatch::Request
{
  status: 302,
  location: "http://localhost:3000/posts/new",
  request: <ActionDispatch::Request:0x00007ff1cb9bd7b8>
}

halted_callback.action_controller

КлючЗначение
:filterФильтр, прервавший экшн
{
  filter: ":halting_filter"
}

unpermitted_parameters.action_controller

КлючЗначение
:keysНеразрешенные ключи
:contextХэш со следующими ключами: :controller, :action, :params, :request

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'
}

Action Dispatch

process_middleware.action_dispatch

КлючЗначение
:middlewareИмя промежуточной программы

redirect.action_dispatch

КлючЗначение
:statusКод отклика HTTP
:locationURL, куда перенаправить
:requestОбъект ActionDispatch::Request

request.action_dispatch

КлючЗначение
:requestОбъект ActionDispatch::Request

Action View

render_template.action_view

КлючЗначение
:identifierПолный путь до шаблона
:layoutПрименяемый макет
:localsЛокальные переменные, переданные в шаблон
{
  identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb",
  layout: "layouts/application",
  locals: { foo: "bar" }
}

render_partial.action_view

КлючЗначение
:identifierПолный путь до шаблона
:localsЛокальные переменные, переданные в шаблон
{
  identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb",
  locals: { foo: "bar" }
}

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
}

render_layout.action_view

КлючЗначение
:identifierПолный путь к шаблону
{
  identifier: "/Users/adam/projects/notifications/app/views/layouts/application.html.erb"
}

Active Record

sql.active_record

КлючЗначение
:sqlВыражение SQL
:nameИмя операции
:connectionОбъект соединения
:bindsСвязанные параметры
:type_casted_bindsПриведенные связанные параметры
:statement_nameИмя выражения SQL
:cachedtrue если использованы кэшированные запросы

Адаптеры могут добавлять свои собственные данные.

{
  sql: "SELECT \"posts\".* FROM \"posts\" ",
  name: "Post Load",
  connection: <ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f7a838850>,
  binds: [<ActiveModel::Attribute::WithCastValue:0x00007fe19d15dc00>],
  type_casted_binds: [11],
  statement_name: nil
}

strict_loading_violation.active_record

Это событие выпускается, только когда config.active_record.action_on_strict_loading_violation установлен :log.

КлючЗначение
:ownerМодель с включенным strict_loading
:reflectionОтражение связи, которая пытается загрузиться

instantiation.active_record

КлючЗначение
:record_countКоличество записей
:class_nameКласс записи
{
  record_count: 1,
  class_name: "User"
}

Action Mailer

deliver.action_mailer

КлючЗначение
:mailerИмя класса рассыльщика
:message_idID сообщения, создается Mail гемом
:subjectТема сообщения
:toАдресат(ы) сообщения
:fromОтправитель сообщения
:bccBCC адреса сообщения
:ccCC адреса сообщения
:dateДата сообщения
:mailКодированная форма сообщения
:perform_deliveriesБыла ли вызвана доставка этого сообщения или нет
{
  mailer: "Notification",
  message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail",
  subject: "Rails Guides",
  to: ["users@rails.com", "dhh@rails.com"],
  from: ["me@rails.com"],
  date: Sat, 10 Mar 2012 14:18:09 +0100,
  mail: "...", # опущено для краткости
  perform_deliveries: true
}

process.action_mailer

КлючЗначение
:mailerИмя класса рассыльщика
:actionЭкшн
:argsАргументы
{
  mailer: "Notification",
  action: "welcome_email",
  args: []
}

Active Support - кэширование

cache_read.active_support

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища
:hitЕсли это чтение успешно
:super_operation:fetch, когда чтение выполняется с fetch

cache_read_multi.active_support

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища
:hitЕсли это чтение успешно
:super_operation:fetch_multi, когда чтение выполняется с fetch_multi

cache_generate.active_support

Это событие используется, только когда fetch вызывается с блоком.

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища

Опции, переданные в fetch, будут объединены с полезной нагрузкой при записи в хранилище.

{
  key: "name-of-complicated-computation",
  store: "ActiveSupport::Cache::MemCacheStore"
}

cache_fetch_hit.active_support

Это событие используется только когда fetch вызывается с блоком.

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища

Опции, переданные в fetch, будут объединены с полезной нагрузкой.

{
  key: "name-of-complicated-computation",
  store: "ActiveSupport::Cache::MemCacheStore"
}

cache_write.active_support

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища

Хранилища кэша также могут добавлять свои собственные данные.

{
  key: "name-of-complicated-computation",
  store: "ActiveSupport::Cache::MemCacheStore"
}

cache_write_multi.active_support

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища

cache_increment.active_support

Это событие вызывается только при использовании MemCacheStore или RedisCacheStore.

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища
:amountСумма увеличения
{
  key: "bottles-of-beer",
  store: "ActiveSupport::Cache::RedisCacheStore",
  amount: 99
}

cache_decrement.active_support

Это событие вызывается только при использовании хранилищ кэша Memcached или Redis.

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища
:amountСумма уменьшения
{
  key: "bottles-of-beer",
  store: "ActiveSupport::Cache::RedisCacheStore",
  amount: 1
}

cache_delete.active_support

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища
{
  key: "name-of-complicated-computation",
  store: "ActiveSupport::Cache::MemCacheStore"
}

cache_delete_multi.active_support

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища

cache_delete_matched.active_support

Это событие вызывается только при использовании RedisCacheStore, FileStore или MemoryStore.

КлючЗначение
:keyИспользуемый паттерн ключа
:storeИмя класса хранилища
{
  key: "posts/*",
  store: "ActiveSupport::Cache::RedisCacheStore"
}

cache_cleanup.active_support

Это событие вызывается только при использовании MemoryStore.

КлючЗначение
:storeИмя класса хранилища
:sizeКоличество записей в кэше перед очисткой
{
  store: "ActiveSupport::Cache::MemoryStore",
  size: 9001
}

cache_prune.active_support

Это событие вызывается только при использовании MemoryStore.

КлючЗначение
:storeИмя класса хранилища
:keyЦелевой размер (в байтах) для кэша
:fromРазмер (в байтах) кэша перед сокращением
{
  store: "ActiveSupport::Cache::MemoryStore",
  key: 5000,
  from: 9001
}

cache_exist?.active_support

КлючЗначение
:keyКлюч, используемый при хранении
:storeИмя класса хранилища
{
  key: "name-of-complicated-computation",
  store: "ActiveSupport::Cache::MemCacheStore"
}

Active Support — сообщения

message_serializer_fallback.active_support

КлючЗначение
:serializerОсновной (предназначенный) сериализатор
:fallbackЗапасной (фактический) сериализатор
:serializedСериализованная строка
:deserializedДесериализованное значение
{
  serializer: :json_allow_marshal,
  fallback: :marshal,
  serialized: "\x04\b{\x06I\"\nHello\x06:\x06ETI\"\nWorld\x06;\x00T",
  deserialized: { "Hello" => "World" },
}

Active Job

enqueue_at.active_job

КлючЗначение
:adapterОбъект QueueAdapter, обрабатывающий задание
:jobОбъект задания

enqueue.active_job

КлючЗначение
:adapterОбъект QueueAdapter, обрабатывающий задание
:jobОбъект задания

enqueue_retry.active_job

КлючЗначение
:jobОбъект задания
:adapterОбъект QueueAdapter, обрабатывающий задание
:errorОшибка, вызвавшая повтор
:waitЗадержка повтора

enqueue_all.active_job

КлючЗначение
:adapterОбъект QueueAdapter, обрабатывающий задание
:jobsМассив объектов Job

perform_start.active_job

КлючЗначение
:adapterОбъект QueueAdapter, обрабатывающий задание
:jobОбъект задания

perform.active_job

КлючЗначение
:adapterОбъект QueueAdapter, обрабатывающий задание
:jobОбъект задания
:db_runtimeСколько затрачено на выполнение запросов в базу данных в миллисекундах

retry_stopped.active_job

КлючЗначение
:adapterОбъект QueueAdapter, обрабатывающий задание
:jobОбъект задания
:errorОшибка, вызвавшая повтор

discard.active_job

КлючЗначение
:adapterОбъект QueueAdapter, обрабатывающий задание
:jobОбъект задания
:errorОшибка, вызвавшая отказ

Action Cable

perform_action.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

preview.active_storage

КлючЗначение
:keyТокен безопасности

transform.active_storage

analyze.active_storage

КлючЗначение
:analyzerИмя анализатора, например ffprobe

Active Storage — сервис хранения

service_upload.active_storage

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса
:checksumКонтрольная сумма для обеспечения целостности

service_streaming_download.active_storage

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса

service_download.active_storage

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса

service_download_chunk.active_storage

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса
:rangeДиапазон битов к прочтению

service_delete.active_storage

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса

service_delete_prefixed.active_storage

КлючЗначение
:prefixПрефикс ключа
:serviceИмя сервиса

service_exist.active_storage

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса
:existСуществует или же нет файл или blob

service_url.active_storage

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса
:urlСгенерированный URL

service_update_metadata.active_storage

Это событие вызывается только при использовании сервиса Google Cloud Storage.

КлючЗначение
:keyТокен безопасности
:serviceИмя сервиса
:content_typeПоле HTTP Content-Type
:dispositionПоле HTTP Content-Disposition

Action Mailbox

process.action_mailbox

КлючЗначение
:mailboxЭкземпляр класс Mailbox, унаследованного от ActionMailbox::Base
:inbound_emailХэш с данными о входящем письме, которое обрабатывается
{
  mailbox: #<RepliesMailbox:0x00007f9f7a8388>,
  inbound_email: {
    id: 1,
    message_id: "0CB459E0-0336-41DA-BC88-E6E28C697DDB@37signals.com",
    status: "processing"
  }
}

Railties

load_config_initializer.railties

КлючЗначение
:initializerПуть к загруженному инициализатору в config/initializers

Rails

deprecation.rails

КлючЗначение
:messageПредупреждение устаревания
:callstackОткуда предупреждение пришло
:gem_nameНазвание гема, отчитывающегося об устаревания
:deprecation_horizonВерсия, в которой устаревшее поведение будет убрано

Исключения

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

КлючЗначение
:exceptionМассив из двух элементов. Имя класса исключение и сообщение
:exception_objectОбъект исключения

Создание пользовательского события

Добавить свои события очень просто. Active Support будет делать всю тяжелую работу за вас. Просто вызовите ActiveSupport::Notifications.instrument с name, payload и блоком. Уведомление будет отправлено после возвращения блока. Active Support сгенерирует время старта и окончания и добавит уникальный ID инструментария. Все данные переданные в вызов instrument будут выполнены в полезной нагрузке.

Пример:

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 # {:this=>:data}
end

Также можно вызвать instrument без передачи блока. Это позволяет использовать инфраструктуру инструментария для других применений (обмен сообщениями).

ActiveSupport::Notifications.instrument "my.custom.event", this: :data

ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data|
  puts data.inspect # {:this=>:data}
end

Вы должны следовать соглашениям Rails при создании своих событий. Формат: event.library. Если ваше приложение отправляет Tweets, вы должны назвать событие tweet.twitter.

On this page

Введение в инструментарийПодписка на событияПросмотр таймингов от инструментария в браузереХуки фреймворка RailsAction Controllerstart_processing.action_controllerprocess_action.action_controllersend_file.action_controllersend_data.action_controllerredirect_to.action_controllerhalted_callback.action_controllerunpermitted_parameters.action_controllerAction Controller — кэшированиеwrite_fragment.action_controllerread_fragment.action_controllerexpire_fragment.action_controllerexist_fragment?.action_controllerAction Dispatchprocess_middleware.action_dispatchredirect.action_dispatchrequest.action_dispatchAction Viewrender_template.action_viewrender_partial.action_viewrender_collection.action_viewrender_layout.action_viewActive Recordsql.active_recordstrict_loading_violation.active_recordinstantiation.active_recordAction Mailerdeliver.action_mailerprocess.action_mailerActive Support - кэшированиеcache_read.active_supportcache_read_multi.active_supportcache_generate.active_supportcache_fetch_hit.active_supportcache_write.active_supportcache_write_multi.active_supportcache_increment.active_supportcache_decrement.active_supportcache_delete.active_supportcache_delete_multi.active_supportcache_delete_matched.active_supportcache_cleanup.active_supportcache_prune.active_supportcache_exist?.active_supportActive Support — сообщенияmessage_serializer_fallback.active_supportActive Jobenqueue_at.active_jobenqueue.active_jobenqueue_retry.active_jobenqueue_all.active_jobperform_start.active_jobperform.active_jobretry_stopped.active_jobdiscard.active_jobAction Cableperform_action.action_cabletransmit.action_cabletransmit_subscription_confirmation.action_cabletransmit_subscription_rejection.action_cablebroadcast.action_cableActive Storagepreview.active_storagetransform.active_storageanalyze.active_storageActive Storage — сервис храненияservice_upload.active_storageservice_streaming_download.active_storageservice_download.active_storageservice_download_chunk.active_storageservice_delete.active_storageservice_delete_prefixed.active_storageservice_exist.active_storageservice_url.active_storageservice_update_metadata.active_storageAction Mailboxprocess.action_mailboxRailtiesload_config_initializer.railtiesRailsdeprecation.railsИсключенияСоздание пользовательского события