edge
Más en rubyonrails.org: Más Ruby on Rails

Instrumentación de Active Support

Active Support es una parte fundamental de Rails que proporciona extensiones del lenguaje Ruby, utilidades y otras cosas. Una de las cosas que incluye es una API de instrumentación que se puede utilizar dentro de una aplicación para medir ciertas acciones que ocurren dentro del código Ruby, como las que se encuentran dentro de una aplicación Rails o el propio framework. Sin embargo, no se limita a Rails, ya que también se puede utilizar de forma independiente en otros scripts de Ruby si se desea.

En esta guía, aprenderás cómo utilizar la API de instrumentación de Active Support para medir eventos dentro de Rails y otros códigos Ruby.

Después de leer esta guía, sabrás:

1 Introducción a la instrumentación

La API de instrumentación proporcionada por Active Support permite a los desarrolladores agregar ganchos a los que otros desarrolladores pueden conectarse. Hay varios de estos dentro del framework de Rails. Con esta API, los desarrolladores pueden elegir ser notificados cuando ocurren ciertos eventos dentro de su aplicación u otro código Ruby.

Por ejemplo, hay un gancho proporcionado dentro de Active Record que se llama cada vez que Active Record utiliza una consulta SQL en una base de datos. Este gancho podría ser suscripto y utilizado para realizar un seguimiento del número de consultas durante una determinada acción. Hay otro gancho relacionado con el procesamiento de una acción de un controlador. Esto podría ser utilizado, por ejemplo, para realizar un seguimiento de cuánto tiempo ha tardado una acción específica.

Incluso puedes crear tus propios eventos dentro de tu aplicación a los que luego puedes suscribirte.

2 Suscribirse a un evento

Suscribirse a un evento es fácil. Utiliza ActiveSupport::Notifications.subscribe con un bloque para escuchar cualquier notificación.

El bloque recibe los siguientes argumentos:

  • Nombre del evento
  • Hora de inicio
  • Hora de finalización
  • Un ID único para el instrumentador que disparó el evento
  • Los datos para el evento
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
  # tus propias acciones personalizadas
  Rails.logger.info "#{name} ¡Recibido! (inicio: #{started}, finalización: #{finished})" # process_action.action_controller Recibido (inicio: 2019-05-05 13:43:57 -0800, finalización: 2019-05-05 13:43:58 -0800)
end

Si te preocupa la precisión de started y finished para calcular un tiempo transcurrido preciso, entonces utiliza ActiveSupport::Notifications.monotonic_subscribe. El bloque dado recibirá los mismos argumentos que se mencionaron anteriormente, pero started y finished tendrán valores con un tiempo monótono preciso en lugar de un tiempo de reloj de pared.

ActiveSupport::Notifications.monotonic_subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
  # tus propias acciones personalizadas
  Rails.logger.info "#{name} ¡Recibido! (inicio: #{started}, finalización: #{finished})" # process_action.action_controller Recibido (inicio: 1560978.425334, finalización: 1560979.429234)
end

Definir todos esos argumentos de bloque cada vez puede ser tedioso. Puedes crear fácilmente un ActiveSupport::Notifications::Event a partir de los argumentos del bloque de esta manera:

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

  event.name      # => "process_action.action_controller"
  event.duration  # => 10 (en milisegundos)
  event.payload   # => {:extra=>información}

  Rails.logger.info "#{event} ¡Recibido!"
end

También puedes pasar un bloque que acepte solo un argumento, y recibirá un objeto de evento:

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |event|
  event.name      # => "process_action.action_controller"
  event.duration  # => 10 (en milisegundos)
  event.payload   # => {:extra=>información}

  Rails.logger.info "#{event} ¡Recibido!"
end

También puedes suscribirte a eventos que coincidan con una expresión regular. Esto te permite suscribirte a múltiples eventos a la vez. Así es como puedes suscribirte a todo lo relacionado con ActionController:

ActiveSupport::Notifications.subscribe(/action_controller/) do |*args|
  # inspeccionar todos los eventos de ActionController
end

3 Ver los tiempos de la instrumentación en tu navegador

Rails implementa el estándar Server Timing para hacer que la información de tiempo esté disponible en el navegador web. Para habilitarlo, edita la configuración de tu entorno (normalmente development.rb, ya que se utiliza principalmente en desarrollo) para incluir lo siguiente:

  config.server_timing = true

Una vez configurado (incluido reiniciar tu servidor), puedes ir a la pestaña Herramientas para desarrolladores de tu navegador, luego seleccionar Red y recargar tu página. Luego puedes seleccionar cualquier solicitud a tu servidor de Rails y verás los tiempos del servidor en la pestaña de tiempos. Para ver un ejemplo de cómo hacer esto, consulta la Documentación de Firefox.

4 Ganchos del framework de Rails

Dentro del framework Ruby on Rails, se proporcionan varios ganchos para eventos comunes. A continuación se detallan estos eventos y sus datos.

4.1 Controlador de Acción

4.1.1 start_processing.action_controller

Clave Valor
:controller El nombre del controlador
:action La acción
:params Hash de los parámetros de la solicitud sin ningún parámetro filtrado
:headers Encabezados de la solicitud
:format html/js/json/xml etc
:method Verbo de solicitud HTTP
:path Ruta de la solicitud
{
  controller: "PostsController",
  action: "new",
  params: { "action" => "new", "controller" => "posts" },
  headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
  format: :html,
  method: "GET",
  path: "/posts/new"
}

4.1.2 process_action.action_controller

Clave Valor
:controller El nombre del controlador
:action La acción
:params Hash de los parámetros de la solicitud sin ningún parámetro filtrado
:headers Encabezados de la solicitud
:format html/js/json/xml etc
:method Verbo de solicitud HTTP
:path Ruta de la solicitud
:request El objeto ActionDispatch::Request
:response El objeto ActionDispatch::Response
:status Código de estado HTTP
:view_runtime Tiempo transcurrido en la vista en ms
:db_runtime Tiempo transcurrido ejecutando consultas a la base de datos en ms
{
  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
}

4.1.3 send_file.action_controller

Clave Valor
:path Ruta completa al archivo

El llamador puede agregar claves adicionales.

4.1.4 send_data.action_controller

ActionController no agrega ninguna información específica a la carga útil. Todas las opciones se pasan a la carga útil.

4.1.5 redirect_to.action_controller

Clave Valor
:status Código de respuesta HTTP
:location URL a la que redirigir
:request El objeto ActionDispatch::Request
{
  status: 302,
  location: "http://localhost:3000/posts/new",
  request: <ActionDispatch::Request:0x00007ff1cb9bd7b8>
}

4.1.6 halted_callback.action_controller

Clave Valor
:filter Filtro que detuvo la acción
{
  filter: ":halting_filter"
}

4.1.7 unpermitted_parameters.action_controller

Clave Valor
:keys Las claves no permitidas
:context Hash con las siguientes claves: :controller, :action, :params, :request

4.2 Controlador de Acción — Caché

4.2.1 write_fragment.action_controller

Clave Valor
:key La clave completa
{
  key: 'posts/1-dashboard-view'
}

4.2.2 read_fragment.action_controller

Clave Valor
:key La clave completa
{
  key: 'posts/1-dashboard-view'
}

4.2.3 expire_fragment.action_controller

Clave Valor
:key La clave completa
{
  key: 'posts/1-dashboard-view'
}

4.2.4 exist_fragment?.action_controller

Clave Valor
:key La clave completa
{
  key: 'posts/1-dashboard-view'
}

4.3 Despacho de Acción

4.3.1 process_middleware.action_dispatch

Clave Valor
:middleware Nombre del middleware

4.3.2 redirect.action_dispatch

Clave Valor
:status Código de respuesta HTTP
:location URL a la que redirigir
:request El objeto ActionDispatch::Request

4.3.3 request.action_dispatch

Clave Valor
:request El objeto ActionDispatch::Request

4.4 Vista de Acción

4.4.1 render_template.action_view

Clave Valor
:identifier Ruta completa de la plantilla
:layout Diseño aplicable
:locals Variables locales pasadas a la plantilla
{
  identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb",
  layout: "layouts/application",
  locals: { foo: "bar" }
}

4.4.2 render_partial.action_view

Clave Valor
:identifier Ruta completa de la plantilla
:locals Variables locales pasadas a la plantilla
{
  identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb",
  locals: { foo: "bar" }
}

4.4.3 render_collection.action_view

Clave Valor
:identifier Ruta completa de la plantilla
:count Tamaño de la colección
:cache_hits Número de parciales obtenidos de la caché

La clave :cache_hits solo se incluye si la colección se renderiza con cached: true. ruby { identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb", count: 3, cache_hits: 0 }

4.4.4 render_layout.action_view

Clave Valor
:identifier Ruta completa del template
{
  identifier: "/Users/adam/projects/notifications/app/views/layouts/application.html.erb"
}

4.5 Active Record

4.5.1 sql.active_record

Clave Valor
:sql Sentencia SQL
:name Nombre de la operación
:connection Objeto de conexión
:binds Parámetros de enlace
:type_casted_binds Parámetros de enlace convertidos
:statement_name Nombre de la sentencia SQL
:cached Se agrega true cuando se utilizan consultas en caché

Los adaptadores pueden agregar sus propios datos también.

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

4.5.2 strict_loading_violation.active_record

Este evento solo se emite cuando config.active_record.action_on_strict_loading_violation se establece en :log.

Clave Valor
:owner Modelo con strict_loading habilitado
:reflection Reflexión de la asociación que intentó cargar

4.5.3 instantiation.active_record

Clave Valor
:record_count Número de registros que se instanciaron
:class_name Clase del registro
{
  record_count: 1,
  class_name: "User"
}

4.6 Action Mailer

4.6.1 deliver.action_mailer

Clave Valor
:mailer Nombre de la clase de correo
:message_id ID del mensaje, generado por la gema Mail
:subject Asunto del correo
:to Dirección(es) de destino del correo
:from Dirección de origen del correo
:bcc Direcciones BCC del correo
:cc Direcciones CC del correo
:date Fecha del correo
:mail La forma codificada del correo
:perform_deliveries Si se realiza o no la entrega de este mensaje
{
  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: "...", # omitido por brevedad
  perform_deliveries: true
}

4.6.2 process.action_mailer

Clave Valor
:mailer Nombre de la clase de correo
:action La acción
:args Los argumentos
{
  mailer: "Notification",
  action: "welcome_email",
  args: []
}

4.7 Active Support — Caching

4.7.1 cache_read.active_support

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento
:hit Si esta lectura es un acierto
:super_operation :fetch si se realiza una lectura con fetch

4.7.2 cache_read_multi.active_support

Clave Valor
:key Claves utilizadas en el almacenamiento
:store Nombre de la clase de almacenamiento
:hits Claves de aciertos en caché
:super_operation :fetch_multi si se realiza una lectura con fetch_multi

4.7.3 cache_generate.active_support

Este evento solo se emite cuando se llama a fetch con un bloque.

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento

Las opciones pasadas a fetch se fusionarán con la carga útil al escribir en el almacenamiento.

{
  key: "nombre-de-la-computación-complicada",
  store: "ActiveSupport::Cache::MemCacheStore"
}

4.7.4 cache_fetch_hit.active_support

Este evento solo se emite cuando se llama a fetch con un bloque.

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento

Las opciones pasadas a fetch se fusionarán con la carga útil.

{
  key: "nombre-de-la-computación-complicada",
  store: "ActiveSupport::Cache::MemCacheStore"
}

4.7.5 cache_write.active_support

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento

Los almacenes de caché pueden agregar sus propios datos también.

{
  key: "nombre-de-la-computación-complicada",
  store: "ActiveSupport::Cache::MemCacheStore"
}

4.7.6 cache_write_multi.active_support

Clave Valor
:key Claves y valores escritos en el almacenamiento
:store Nombre de la clase de almacenamiento

4.7.7 cache_increment.active_support

Este evento solo se emite cuando se utiliza MemCacheStore o RedisCacheStore.

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento
:amount Cantidad a incrementar
{
  key: "botellas-de-cerveza",
  store: "ActiveSupport::Cache::RedisCacheStore",
  amount: 99
}

4.7.8 cache_decrement.active_support

Este evento solo se emite cuando se utiliza los almacenes de caché Memcached o Redis.

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento
:amount Cantidad a decrementar
{
  key: "botellas-de-cerveza",
  store: "ActiveSupport::Cache::RedisCacheStore",
  amount: 1
}

4.7.9 cache_delete.active_support

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento
{
  key: "nombre-de-computacion-complicada",
  store: "ActiveSupport::Cache::MemCacheStore"
}

4.7.10 cache_delete_multi.active_support

Clave Valor
:key Claves utilizadas en el almacenamiento
:store Nombre de la clase de almacenamiento

4.7.11 cache_delete_matched.active_support

Este evento solo se emite cuando se utiliza RedisCacheStore, FileStore o MemoryStore.

Clave Valor
:key Patrón de clave utilizado
:store Nombre de la clase de almacenamiento
{
  key: "posts/*",
  store: "ActiveSupport::Cache::RedisCacheStore"
}

4.7.12 cache_cleanup.active_support

Este evento solo se emite cuando se utiliza MemoryStore.

Clave Valor
:store Nombre de la clase de almacenamiento
:size Número de entradas en la caché antes de la limpieza
{
  store: "ActiveSupport::Cache::MemoryStore",
  size: 9001
}

4.7.13 cache_prune.active_support

Este evento solo se emite cuando se utiliza MemoryStore.

Clave Valor
:store Nombre de la clase de almacenamiento
:key Tamaño objetivo (en bytes) para la caché
:from Tamaño (en bytes) de la caché antes de la poda
{
  store: "ActiveSupport::Cache::MemoryStore",
  key: 5000,
  from: 9001
}

4.7.14 cache_exist?.active_support

Clave Valor
:key Clave utilizada en el almacenamiento
:store Nombre de la clase de almacenamiento
{
  key: "nombre-de-computacion-complicada",
  store: "ActiveSupport::Cache::MemCacheStore"
}

4.8 Active Support — Mensajes

4.8.1 message_serializer_fallback.active_support

Clave Valor
:serializer Serializador primario (previsto)
:fallback Serializador secundario (real)
:serialized Cadena serializada
:deserialized Valor deserializado
{
  serializer: :json_allow_marshal,
  fallback: :marshal,
  serialized: "\x04\b{\x06I\"\nHola\x06:\x06ETI\"\nMundo\x06;\x00T",
  deserialized: { "Hola" => "Mundo" },
}

4.9 Active Job

4.9.1 enqueue_at.active_job

Clave Valor
:adapter Objeto QueueAdapter que procesa el trabajo
:job Objeto de trabajo

4.9.2 enqueue.active_job

Clave Valor
:adapter Objeto QueueAdapter que procesa el trabajo
:job Objeto de trabajo

4.9.3 enqueue_retry.active_job

Clave Valor
:job Objeto de trabajo
:adapter Objeto QueueAdapter que procesa el trabajo
:error El error que causó el reintento
:wait El retraso del reintento

4.9.4 enqueue_all.active_job

Clave Valor
:adapter Objeto QueueAdapter que procesa el trabajo
:jobs Un array de objetos de trabajo

4.9.5 perform_start.active_job

Clave Valor
:adapter Objeto QueueAdapter que procesa el trabajo
:job Objeto de trabajo

4.9.6 perform.active_job

Clave Valor
:adapter Objeto QueueAdapter que procesa el trabajo
:job Objeto de trabajo
:db_runtime Cantidad de tiempo gastado ejecutando consultas a la base de datos en ms

4.9.7 retry_stopped.active_job

Clave Valor
:adapter Objeto QueueAdapter que procesa el trabajo
:job Objeto de trabajo
:error El error que causó el reintento

4.9.8 discard.active_job

Clave Valor
:adapter Objeto QueueAdapter que procesa el trabajo
:job Objeto de trabajo
:error El error que causó el descarte

4.10 Action Cable

4.10.1 perform_action.action_cable

Clave Valor
:channel_class Nombre de la clase del canal
:action La acción
:data Un hash de datos

4.10.2 transmit.action_cable

Clave Valor
:channel_class Nombre de la clase del canal
:data Un hash de datos
:via Via

4.10.3 transmit_subscription_confirmation.action_cable

Clave Valor
:channel_class Nombre de la clase del canal

4.10.4 transmit_subscription_rejection.action_cable

Clave Valor
:channel_class Nombre de la clase del canal

4.10.5 broadcast.action_cable

Clave Valor
:broadcasting Una transmisión nombrada
:message Un hash de mensaje
:coder El codificador

4.11 Active Storage

4.11.1 preview.active_storage

Clave Valor
:key Token seguro

4.11.2 transform.active_storage

4.11.3 analyze.active_storage

Clave Valor
:analyzer Nombre del analizador, por ejemplo, ffprobe

4.12 Active Storage — Storage Service

4.12.1 service_upload.active_storage

Clave Valor
:key Token seguro
:service Nombre del servicio
:checksum Suma de comprobación para garantizar la integridad

4.12.2 service_streaming_download.active_storage

Clave Valor
:key Token seguro
:service Nombre del servicio

4.12.3 service_download_chunk.active_storage

Clave Valor
:key Token seguro
:service Nombre del servicio
:range Rango de bytes que se intentó leer

4.12.4 service_download.active_storage

Clave Valor
:key Token seguro
:service Nombre del servicio

4.12.5 service_delete.active_storage

Clave Valor
:key Token seguro
:service Nombre del servicio

4.12.6 service_delete_prefixed.active_storage

Clave Valor
:prefix Prefijo de clave
:service Nombre del servicio

4.12.7 service_exist.active_storage

Clave Valor
:key Token seguro
:service Nombre del servicio
:exist Archivo o blob existe o no

4.12.8 service_url.active_storage

Clave Valor
:key Token seguro
:service Nombre del servicio
:url URL generada

4.12.9 service_update_metadata.active_storage

Este evento solo se emite al usar el servicio de Google Cloud Storage.

Clave Valor
:key Token seguro
:service Nombre del servicio
:content_type Campo HTTP Content-Type
:disposition Campo HTTP Content-Disposition

4.13 Action Mailbox

4.13.1 process.action_mailbox

Clave Valor
:mailbox Instancia de la clase Mailbox que hereda de ActionMailbox::Base
:inbound_email Hash con datos sobre el correo electrónico entrante que se está procesando
{
  mailbox: #<RepliesMailbox:0x00007f9f7a8388>,
  inbound_email: {
    id: 1,
    message_id: "[email protected]",
    status: "processing"
  }
}

4.14 Railties

4.14.1 load_config_initializer.railties

Clave Valor
:initializer Ruta del inicializador cargado en config/initializers

4.15 Rails

4.15.1 deprecation.rails

Clave Valor
:message La advertencia de deprecación
:callstack De dónde proviene la advertencia de deprecación
:gem_name Nombre de la gema que informa la deprecación
:deprecation_horizon Versión en la que se eliminará el comportamiento obsoleto

5 Excepciones

Si ocurre una excepción durante cualquier instrumentación, el payload incluirá información al respecto.

Clave Valor
:exception Un array de dos elementos. El nombre de la clase de la excepción y el mensaje
:exception_object El objeto de la excepción

6 Creación de eventos personalizados

Agregar tus propios eventos también es fácil. Active Support se encargará de todo el trabajo pesado por ti. Simplemente llama a ActiveSupport::Notifications.instrument con un nombre, payload y un bloque. La notificación se enviará después de que el bloque regrese. Active Support generará los tiempos de inicio y fin, y agregará el ID único del instrumentador. Todos los datos pasados a la llamada instrument se incluirán en el payload. Aquí tienes un ejemplo:

ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
  # haz tus cosas personalizadas aquí
end

Ahora puedes escuchar este evento con:

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

También puedes llamar a instrument sin pasar un bloque. Esto te permite aprovechar la infraestructura de instrumentación para otros usos de mensajería.

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

Debes seguir las convenciones de Rails al definir tus propios eventos. El formato es: evento.biblioteca. Si tu aplicación está enviando Tweets, debes crear un evento llamado tweet.twitter.

Comentarios

Se te anima a ayudar a mejorar la calidad de esta guía.

Por favor, contribuye si encuentras algún error tipográfico o factual. Para empezar, puedes leer nuestra contribución a la documentación sección.

También puedes encontrar contenido incompleto o desactualizado. Por favor, añade cualquier documentación faltante para main. Asegúrate de revisar Edge Guides primero para verificar si los problemas ya están resueltos o no en la rama principal. Consulta las Directrices de las Guías de Ruby on Rails para el estilo y las convenciones.

Si por alguna razón encuentras algo que corregir pero no puedes solucionarlo tú mismo, por favor abre un problema.

Y por último, cualquier tipo de discusión sobre la documentación de Ruby on Rails es muy bienvenida en el Foro oficial de Ruby on Rails.