lib/sentry/rails/log_subscriber.rb
# frozen_string_literal: true require "active_support/log_subscriber" require "sentry/utils/logging_helper" module Sentry module Rails # Base class for Sentry log subscribers that extends ActiveSupport::LogSubscriber # to provide structured logging capabilities for Rails components. # # This class follows Rails' LogSubscriber pattern and provides common functionality # for capturing Rails instrumentation events and logging them through Sentry's # structured logging system. # # @example Creating a custom log subscriber # class MySubscriber < Sentry::Rails::LogSubscriber # attach_to :my_component # # def my_event(event) # log_structured_event( # message: "My event occurred", # level: :info, # attributes: { # duration_ms: event.duration, # custom_data: event.payload[:custom_data] # } # ) # end # end class LogSubscriber < ActiveSupport::LogSubscriber include Sentry::LoggingHelper ORIGIN = "auto.log.rails.log_subscriber" class << self if ::Rails.version.to_f < 6.0 # Rails 5.x does not provide detach_from def detach_from(namespace, notifications = ActiveSupport::Notifications) listeners = public_instance_methods(false) .flat_map { |key| notifications.notifier.listeners_for("#{key}.#{namespace}") } .select { |listener| listener.instance_variable_get(:@delegate).is_a?(self) } listeners.map do |listener| notifications.notifier.unsubscribe(listener) end end end end protected # Log a structured event using Sentry's structured logger # # @param message [String] The log message # @param level [Symbol] The log level (:trace, :debug, :info, :warn, :error, :fatal) # @param attributes [Hash] Additional structured attributes to include # @param origin [String] The origin of the log event def log_structured_event(message:, level: :info, attributes: {}, origin: ORIGIN) Sentry.logger.public_send(level, message, **attributes, origin: origin) rescue => e # Silently handle any errors in logging to avoid breaking the application Sentry.configuration.sdk_logger.debug("Failed to log structured event: #{e.message}") end # Calculate duration in milliseconds from an event # # @param event [ActiveSupport::Notifications::Event] The event # @return [Float] Duration in milliseconds def duration_ms(event) event.duration.round(2) end end end end