class Sentry::Configuration
def add_post_initialization_callback(&block)
def add_post_initialization_callback(&block) callbacks[:initialize][:after] << block end
def after(event, &block)
def after(event, &block) callbacks[event.to_sym][:after] << block end
def before(event, &block)
def before(event, &block) callbacks[event.to_sym][:before] << block end
def before_breadcrumb=(value)
def before_breadcrumb=(value) check_callable!("before_breadcrumb", value) @before_breadcrumb = value end
def before_send=(value)
def before_send=(value) check_callable!("before_send", value) @before_send = value end
def before_send_check_in=(value)
def before_send_check_in=(value) check_callable!("before_send_check_in", value) @before_send_check_in = value end
def before_send_metric=(value)
def before_send_metric=(value) check_callable!("before_send_metric", value) @before_send_metric = value end
def before_send_transaction=(value)
def before_send_transaction=(value) check_callable!("before_send_transaction", value) @before_send_transaction = value end
def breadcrumbs_logger=(logger)
def breadcrumbs_logger=(logger) loggers = if logger.is_a?(Array) logger else Array(logger) end require "sentry/breadcrumb/sentry_logger" if loggers.include?(:sentry_logger) @breadcrumbs_logger = logger end
def build_validation_proc(optional, type)
def build_validation_proc(optional, type) case type when :numeric ->(value) do if optional && value.nil? true else unless value.is_a?(Numeric) message = "must be a Numeric" message += " or nil" if optional { error: message, value: value } else true end end end else ->(value) { true } end end
def callbacks
def callbacks @callbacks ||= { initialize: { before: [], after: [] }, configured: { before: [], after: [] } } end
def capture_in_environment?
def capture_in_environment? return true if enabled_in_current_env? @errors << "Not configured to send/capture in environment '#{environment}'" false end
def csp_report_uri
-
(String, nil)-
def csp_report_uri if dsn && dsn.valid? uri = dsn.csp_report_uri uri += "&sentry_release=#{CGI.escape(release)}" if release && !release.empty? uri += "&sentry_environment=#{CGI.escape(environment)}" if environment && !environment.empty? uri end end
def detect_release
- Api: - private
def detect_release return unless sending_allowed? @release ||= ReleaseDetector.detect_release(project_root: project_root, running_on_heroku: running_on_heroku?) if running_on_heroku? && release.nil? log_warn(HEROKU_DYNO_METADATA_MESSAGE) end rescue => e log_error("Error detecting release", e, debug: debug) end
def dsn=(value)
def dsn=(value) @dsn = init_dsn(value) end
def enabled_in_current_env?
def enabled_in_current_env? enabled_environments.nil? || enabled_environments.include?(environment) end
def environment=(environment)
def environment=(environment) @environment = environment.to_s end
def environment_from_env
def environment_from_env ENV["SENTRY_CURRENT_ENV"] || ENV["SENTRY_ENVIRONMENT"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development" end
def error_messages
- Api: - private
def error_messages @errors = [@errors[0]] + @errors[1..-1].map(&:downcase) # fix case of all but first @errors.join(", ") end
def exception_class_allowed?(exc)
def exception_class_allowed?(exc) if exc.is_a?(Sentry::Error) # Try to prevent error reporting loops log_debug("Refusing to capture Sentry error: #{exc.inspect}") false elsif excluded_exception?(exc) log_debug("User excluded error: #{exc.inspect}") false else true end end
def excluded_exception?(incoming_exception)
def excluded_exception?(incoming_exception) excluded_exception_classes.any? do |excluded_exception| matches_exception?(excluded_exception, incoming_exception) end end
def excluded_exception_classes
def excluded_exception_classes @excluded_exception_classes ||= excluded_exceptions.map { |e| get_exception_class(e) } end
def get_exception_class(x)
def get_exception_class(x) x.is_a?(Module) ? x : safe_const_get(x) end
def init_dsn(dsn_string)
def init_dsn(dsn_string) return if dsn_string.nil? || dsn_string.empty? DSN.new(dsn_string) end
def initialize
def initialize run_callbacks(:before, :initialize) self.app_dirs_pattern = APP_DIRS_PATTERN self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"]) self.background_worker_threads = (processor_count / 2.0).ceil self.background_worker_max_queue = BackgroundWorker::DEFAULT_MAX_QUEUE self.backtrace_cleanup_callback = nil self.strip_backtrace_load_path = true self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE self.breadcrumbs_logger = [] self.context_lines = 3 self.include_local_variables = false self.environment = environment_from_env self.enabled_environments = nil self.exclude_loggers = [] self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT self.inspect_exception_causes_for_exclusion = true self.linecache = ::Sentry::LineCache.new self.sdk_logger = ::Sentry::Logger.new(STDOUT) self.project_root = Dir.pwd self.propagate_traces = true self.sample_rate = 1.0 self.send_modules = true self.send_default_pii = false self.skip_rake_integration = false self.send_client_reports = true self.auto_session_tracking = true self.enable_backpressure_handling = false self.trusted_proxies = [] self.dsn = ENV["SENTRY_DSN"] self.capture_queue_time = true spotlight_env = ENV["SENTRY_SPOTLIGHT"] spotlight_bool = Sentry::Utils::EnvHelper.env_to_bool(spotlight_env, strict: true) self.spotlight = spotlight_bool.nil? ? (spotlight_env || false) : spotlight_bool self.server_name = server_name_from_env self.instrumenter = :sentry self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL] self.trace_ignore_status_codes = TRACE_IGNORE_STATUS_CODES_DEFAULT self.enabled_patches = DEFAULT_PATCHES.dup self.before_send = nil self.before_send_transaction = nil self.before_send_check_in = nil self.before_send_log = nil self.before_send_metric = nil self.std_lib_logger_filter = nil self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT self.traces_sampler = nil self.enable_logs = false self.enable_metrics = true self.profiler_class = Sentry::Profiler self.profiles_sample_interval = DEFAULT_PROFILES_SAMPLE_INTERVAL @transport = Transport::Configuration.new @cron = Cron::Configuration.new @structured_logging = StructuredLoggingConfiguration.new @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map) self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS self.max_metric_events = MetricEventBuffer::DEFAULT_MAX_METRICS run_callbacks(:after, :initialize) yield(self) if block_given? run_callbacks(:after, :configured) end
def instrumenter=(instrumenter)
def instrumenter=(instrumenter) @instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry end
def matches_exception?(excluded_exception_class, incoming_exception)
def matches_exception?(excluded_exception_class, incoming_exception) if inspect_exception_causes_for_exclusion? Sentry::Utils::ExceptionCauseChain.exception_to_array(incoming_exception).any? { |cause| excluded_exception_class === cause } else excluded_exception_class === incoming_exception end end
def post_initialization_callbacks
Post initialization callbacks are called at the end of initialization process
def post_initialization_callbacks @post_initialization_callbacks ||= [] end
def processor_count
def processor_count available_processor_count = Concurrent.available_processor_count if Concurrent.respond_to?(:available_processor_count) available_processor_count || Concurrent.processor_count end
def profiler_class=(profiler_class)
def profiler_class=(profiler_class) if profiler_class == Sentry::Vernier::Profiler begin require "vernier" rescue LoadError end end @profiler_class = profiler_class end
def profiles_sample_rate=(profiles_sample_rate)
def profiles_sample_rate=(profiles_sample_rate) @profiles_sample_rate = profiles_sample_rate end
def profiling_enabled?
def profiling_enabled? valid_sampler = !!(valid_sample_rate?(@profiles_sample_rate)) tracing_enabled? && valid_sampler && sending_allowed? end
def release=(value)
def release=(value) check_argument_type!(value, String, NilClass) @release = value end
def run_callbacks(hook, event)
def run_callbacks(hook, event) self.class.callbacks[event][hook].each do |hook| instance_eval(&hook) end end
def running_on_heroku?
def running_on_heroku? File.directory?("/etc/heroku") && !ENV["CI"] end
def safe_const_get(x)
def safe_const_get(x) x = x.to_s unless x.is_a?(String) Object.const_get(x) rescue NameError # There's no way to safely ask if a constant exist for an unknown string nil end
def sample_allowed?
def sample_allowed? return true if sample_rate == 1.0 Random.rand < sample_rate end
def sending_allowed?
def sending_allowed? spotlight || sending_to_dsn_allowed? end
def sending_to_dsn_allowed?
def sending_to_dsn_allowed? @errors = [] valid? && capture_in_environment? end
def server_name_from_env
def server_name_from_env if running_on_heroku? ENV["DYNO"] else # Try to resolve the hostname to an FQDN, but fall back to whatever # the load name is. Socket.gethostname || Socket.gethostbyname(hostname).first rescue server_name end end
def session_tracking?
def session_tracking? auto_session_tracking && enabled_in_current_env? end
def stacktrace_builder
- Api: - private
def stacktrace_builder @stacktrace_builder ||= StacktraceBuilder.new( project_root: @project_root.to_s, app_dirs_pattern: @app_dirs_pattern, linecache: @linecache, context_lines: @context_lines, backtrace_cleanup_callback: @backtrace_cleanup_callback, strip_backtrace_load_path: @strip_backtrace_load_path ) end
def std_lib_logger_filter=(value)
def std_lib_logger_filter=(value) check_callable!("std_lib_logger_filter", value) @std_lib_logger_filter = value end
def trace_ignore_status_codes=(codes)
def trace_ignore_status_codes=(codes) unless codes.is_a?(Array) && codes.all? { |code| valid_status_code_entry?(code) } raise ArgumentError, "trace_ignore_status_codes must be an Array of integers or ranges between (100-599) where begin <= end" end @trace_ignore_status_codes = codes end
def traces_sample_rate=(traces_sample_rate)
def traces_sample_rate=(traces_sample_rate) @traces_sample_rate = traces_sample_rate end
def tracing_enabled?
def tracing_enabled? valid_sampler = !!((valid_sample_rate?(@traces_sample_rate)) || @traces_sampler) valid_sampler && sending_allowed? end
def valid?
def valid? if @dsn&.valid? true else @errors << "DSN not set or not valid" false end end
def valid_http_status_code?(code)
def valid_http_status_code?(code) code.is_a?(Integer) && code >= 100 && code <= 599 end
def valid_sample_rate?(sample_rate)
def valid_sample_rate?(sample_rate) return false unless sample_rate.is_a?(Numeric) sample_rate >= 0.0 && sample_rate <= 1.0 end
def valid_status_code_entry?(entry)
def valid_status_code_entry?(entry) case entry when Integer valid_http_status_code?(entry) when Range valid_http_status_code?(entry.begin) && valid_http_status_code?(entry.end) && entry.begin <= entry.end else false end end
def validate(attribute, optional: false, type: nil)
def validate(attribute, optional: false, type: nil) validations[attribute] = { optional: optional, type: type, proc: build_validation_proc(optional, type) } end
def validate
def validate if profiler_class == Sentry::Profiler && profiles_sample_rate && !Sentry.dependency_installed?(:StackProf) log_warn("Please add the 'stackprof' gem to your Gemfile to use the StackProf profiler with Sentry.") end if profiler_class == Sentry::Vernier::Profiler && profiles_sample_rate && !Sentry.dependency_installed?(:Vernier) log_warn("Please add the 'vernier' gem to your Gemfile to use the Vernier profiler with Sentry.") end self.class.validations.each do |attribute, validation| value = public_send(attribute) next if (result = validation[:proc].call(value)) === true raise ArgumentError, result[:error] end end
def validations
def validations @validations ||= {} end