# frozen_string_literal: true # Sentry configuration for error tracking and performance monitoring # Only initializes if SENTRY_DSN is configured return unless ENV['SENTRY_DSN'].present? require 'sentry-rails' Sentry.init do |config| config.dsn = ENV['SENTRY_DSN'] # Configure sampling for production (lower in production, higher in staging) config.traces_sample_rate = case Rails.env when 'production' then ENV.fetch('SENTRY_TRACES_SAMPLE_RATE', '0.05').to_f when 'staging' then ENV.fetch('SENTRY_TRACES_SAMPLE_RATE', '0.2').to_f else ENV.fetch('SENTRY_TRACES_SAMPLE_RATE', '0.1').to_f end # Enable breadcrumbs for better debugging config.breadcrumbs_logger = [:active_support_logger, :http_logger] # Send PII data (like IPs) to Sentry for debugging (disable in production if needed) config.send_default_pii = ENV.fetch('SENTRY_SEND_PII', 'false') == 'true' # Set environment config.environment = Rails.env # Configure release info from Docker tag or Git if ENV['GIT_COMMIT_SHA'] config.release = ENV['GIT_COMMIT_SHA'][0..7] elsif ENV['APP_VERSION'] config.release = ENV['APP_VERSION'] end # Set server name for multi-instance environments config.server_name = ENV.fetch('SERVER_NAME', 'baffle-agent') # Filter out certain errors to reduce noise and add tags config.before_send = lambda do |event, hint| # Filter out 404 errors and other expected HTTP errors if event.contexts.dig(:response, :status_code) == 404 nil # Filter out validation errors in development elsif Rails.env.development? && event.exception&.message&.include?("Validation failed") nil # Filter out specific noisy exceptions elsif %w[ActionController::RoutingError ActionController::InvalidAuthenticityToken ActionController::UnknownFormat ActionDispatch::Http::Parameters::ParseError].include?(event.exception&.class&.name) nil else # Add tags for better filtering in Sentry event.tags.merge!({ ruby_version: RUBY_VERSION, rails_version: Rails.version, environment: Rails.env }) event end end # Configure exception class exclusion config.excluded_exceptions += [ 'ActionController::RoutingError', 'ActionController::InvalidAuthenticityToken', 'CGI::Session::CookieStore::TamperedWithCookie', 'ActionController::UnknownFormat', 'ActionDispatch::Http::Parameters::ParseError', 'Mongoid::Errors::DocumentNotFound' ] end # SolidQueue monitoring will be automatically handled by sentry-solid_queue gem # Add correlation ID to Sentry context ActiveSupport::Notifications.subscribe('action_controller.process_action') do |name, start, finish, id, payload| controller = payload[:controller] action = payload[:action] request_id = payload[:request]&.request_id if controller && action && request_id Sentry.set_context(:request, { correlation_id: request_id, controller: controller.controller_name, action: action.action_name, ip: request&.remote_ip }) end end # Add ActiveJob context to all job transactions ActiveSupport::Notifications.subscribe('perform.active_job') do |name, start, finish, id, payload| job = payload[:job] Sentry.configure_scope do |scope| scope.set_tag(:job_class, job.class.name) scope.set_tag(:job_queue, job.queue_name) scope.set_tag(:job_id, job.job_id) scope.set_context(:job, { job_id: job.job_id, job_class: job.class.name, queue_name: job.queue_name, arguments: job.arguments.to_s, enqueued_at: job.enqueued_at, executions: job.executions }) end end # Monitor SolidQueue job failures ActiveSupport::Notifications.subscribe('solid_queue.error') do |name, start, finish, id, payload| job = payload[:job] error = payload[:error] Sentry.with_scope do |scope| scope.set_tag(:job_class, job.class_name) scope.set_tag(:job_queue, job.queue_name) scope.set_context(:job, { job_id: job.active_job_id, arguments: job.arguments.to_s, queue_name: job.queue_name, created_at: job.created_at }) scope.set_context(:error, { error_class: error.class.name, error_message: error.message }) Sentry.capture_exception(error) end end # Set user context when available if defined?(Current) && Current.user Sentry.set_user(id: Current.user.id, email: Current.user.email) end # Add application-specific context app_version = begin File.read(Rails.root.join('VERSION')).strip rescue ENV['APP_VERSION'] || ENV['GIT_COMMIT_SHA']&.[](0..7) || 'unknown' end Sentry.set_context('application', { name: 'BaffleHub', version: app_version, environment: Rails.env, database: ActiveRecord::Base.connection.adapter_name, queue_adapter: Rails.application.config.active_job.queue_adapter }) Rails.logger.info "Sentry configured for environment: #{Rails.env}"