module Semantic NOTIFICATIONS = { action_controller: %i[start_processing process_action redirect_to], action_view: %i[render_partial render_template render_collection render_layout], active_record: %i[sql strict_loading instantiation start_transaction transaction] }.freeze # enable/disable instrumentation callbacks class Instrumentalizer include SemanticLogger::Loggable class << self def activate(*event_groups) reset runtime_registry_subscriber event_groups.each { |event_group| enable(event_group) } end private def runtime_registry_subscriber global_subscribers << ActiveSupport::Notifications.monotonic_subscribe('sql.active_record') do |name, start, finish, id, payload| unless %w[SCHEMA TRANSACTION].include?(payload[:name]) ActiveRecord::RuntimeRegistry.queries_count += 1 ActiveRecord::RuntimeRegistry.cached_queries_count += 1 if payload[:cached] end runtime = (finish - start) * 1_000.0 ActiveRecord::RuntimeRegistry.async_sql_runtime += (runtime - payload[:lock_wait]) if payload[:async] ActiveRecord::RuntimeRegistry.sql_runtime += runtime end end def global_subscribers @global_subscribers ||= [] end def reset global_subscribers.each do |sub| ActiveSupport::Notifications.unsubscribe(sub) end global_subscribers.clear NOTIFICATIONS.each do |event_group, hooks| hooks.each do |hook| hook_full_name = "#{hook}.#{event_group}" ActiveSupport::Notifications.unsubscribe(hook_full_name) end end end def enable(event_group) log_subscriber = build_log_subscriber_from(event_group) NOTIFICATIONS[event_group].each do |hook| subscriber = ActiveSupport::Notifications.subscribe("#{hook}.#{event_group}") do |event| # logger.debug("SEND #{log_subscriber} hook=#{hook}") log_subscriber.send(hook, event) rescue StandardError => e logger.error('Error during instrumentation handling', e) end global_subscribers << subscriber end # logger.info("event_group #{event_group} is enabled!") end def build_log_subscriber_from(event_group) classname = event_group.to_s.camelize options = Rails.application.config.x.send(event_group).to_h Semantic::Subscribers.const_get(classname).new(**options) end end end end