debian-mirror-gitlab/lib/gitlab/error_tracking.rb

223 lines
8 KiB
Ruby
Raw Normal View History

2020-01-01 13:55:28 +05:30
# frozen_string_literal: true
module Gitlab
module ErrorTracking
2020-04-22 19:07:51 +05:30
# Exceptions in this group will receive custom Sentry fingerprinting
CUSTOM_FINGERPRINTING = %w[
Acme::Client::Error::BadNonce
Acme::Client::Error::NotFound
Acme::Client::Error::RateLimited
Acme::Client::Error::Timeout
Acme::Client::Error::UnsupportedOperation
ActiveRecord::ConnectionTimeoutError
Gitlab::RequestContext::RequestDeadlineExceeded
GRPC::DeadlineExceeded
JIRA::HTTPError
Rack::Timeout::RequestTimeoutException
].freeze
2021-04-29 21:17:54 +05:30
PROCESSORS = [
::Gitlab::ErrorTracking::Processor::SidekiqProcessor,
::Gitlab::ErrorTracking::Processor::GrpcErrorProcessor,
2022-04-01 21:47:47 +05:30
::Gitlab::ErrorTracking::Processor::ContextPayloadProcessor,
2022-08-13 15:12:31 +05:30
::Gitlab::ErrorTracking::Processor::SanitizeErrorMessageProcessor,
# IMPORTANT: this processor must stay at the bottom, right before
# sending the event to Sentry.
::Gitlab::ErrorTracking::Processor::SanitizerProcessor
2021-04-29 21:17:54 +05:30
].freeze
2020-01-01 13:55:28 +05:30
class << self
2022-05-07 20:08:51 +05:30
def configure(&block)
configure_raven(&block)
configure_sentry(&block)
end
def configure_raven
2020-01-01 13:55:28 +05:30
Raven.configure do |config|
config.dsn = sentry_dsn
config.release = Gitlab.revision
config.current_environment = Gitlab.config.sentry.environment
# Sanitize fields based on those sanitized from Rails.
config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s)
2020-11-24 15:15:51 +05:30
2020-01-01 13:55:28 +05:30
# Sanitize authentication headers
config.sanitize_http_headers = %w[Authorization Private-Token]
2022-05-07 20:08:51 +05:30
config.before_send = method(:before_send_raven)
yield config if block_given?
end
end
def configure_sentry
Sentry.init do |config|
config.dsn = new_sentry_dsn
config.release = Gitlab.revision
config.environment = new_sentry_environment
config.before_send = method(:before_send_sentry)
config.background_worker_threads = 0
config.send_default_pii = true
2020-06-23 00:09:42 +05:30
yield config if block_given?
2020-01-01 13:55:28 +05:30
end
end
# This should be used when you want to passthrough exception handling:
# rescue and raise to be catched in upper layers of the application.
#
# If the exception implements the method `sentry_extra_data` and that method
# returns a Hash, then the return value of that method will be merged into
# `extra`. Exceptions can use this mechanism to provide structured data
# to sentry in addition to their message and back-trace.
def track_and_raise_exception(exception, extra = {})
2022-07-23 23:45:48 +05:30
process_exception(exception, extra: extra)
2020-01-01 13:55:28 +05:30
raise exception
end
# This can be used for investigating exceptions that can be recovered from in
# code. The exception will still be raised in development and test
# environments.
#
# That way we can track down these exceptions with as much information as we
# need to resolve them.
#
# If the exception implements the method `sentry_extra_data` and that method
# returns a Hash, then the return value of that method will be merged into
# `extra`. Exceptions can use this mechanism to provide structured data
# to sentry in addition to their message and back-trace.
#
# Provide an issue URL for follow up.
# as `issue_url: 'http://gitlab.com/gitlab-org/gitlab/issues/111'`
def track_and_raise_for_dev_exception(exception, extra = {})
2022-07-23 23:45:48 +05:30
process_exception(exception, extra: extra)
2020-01-01 13:55:28 +05:30
raise exception if should_raise_for_dev?
end
# This should be used when you only want to track the exception.
#
# If the exception implements the method `sentry_extra_data` and that method
# returns a Hash, then the return value of that method will be merged into
# `extra`. Exceptions can use this mechanism to provide structured data
# to sentry in addition to their message and back-trace.
def track_exception(exception, extra = {})
2022-07-23 23:45:48 +05:30
process_exception(exception, extra: extra)
2020-01-01 13:55:28 +05:30
end
# This should be used when you only want to log the exception,
# but not send it to Sentry.
#
# If the exception implements the method `sentry_extra_data` and that method
# returns a Hash, then the return value of that method will be merged into
# `extra`. Exceptions can use this mechanism to provide structured data
# to sentry in addition to their message and back-trace.
def log_exception(exception, extra = {})
2022-07-23 23:45:48 +05:30
process_exception(exception, extra: extra, trackers: [Logger])
2020-01-01 13:55:28 +05:30
end
2023-03-17 16:20:25 +05:30
# This should be used when you want to log the exception and passthrough
# exception handling: rescue and raise to be catched in upper layers of
# the application.
#
# If the exception implements the method `sentry_extra_data` and that method
# returns a Hash, then the return value of that method will be merged into
# `extra`. Exceptions can use this mechanism to provide structured data
# to sentry in addition to their message and back-trace.
def log_and_raise_exception(exception, extra = {})
process_exception(exception, extra: extra, trackers: [Logger])
raise exception
end
2020-01-01 13:55:28 +05:30
private
2022-05-07 20:08:51 +05:30
def before_send_raven(event, hint)
2022-07-16 23:28:13 +05:30
return unless Feature.enabled?(:enable_old_sentry_integration)
2022-05-07 20:08:51 +05:30
before_send(event, hint)
end
def before_send_sentry(event, hint)
2022-07-16 23:28:13 +05:30
return unless Feature.enabled?(:enable_new_sentry_integration)
2022-05-07 20:08:51 +05:30
before_send(event, hint)
end
2020-04-22 19:07:51 +05:30
def before_send(event, hint)
2023-01-13 00:05:48 +05:30
# Don't report Sidekiq retry errors to Sentry
return if hint[:exception].is_a?(Gitlab::SidekiqMiddleware::RetryError)
2021-03-08 18:12:59 +05:30
inject_context_for_exception(event, hint[:exception])
custom_fingerprinting(event, hint[:exception])
2020-04-22 19:07:51 +05:30
2021-04-29 21:17:54 +05:30
PROCESSORS.reduce(event) do |processed_event, processor|
processor.call(processed_event)
end
2020-04-22 19:07:51 +05:30
end
2022-07-23 23:45:48 +05:30
def process_exception(exception, extra:, trackers: default_trackers)
2021-04-17 20:07:23 +05:30
context_payload = Gitlab::ErrorTracking::ContextPayloadGenerator.generate(exception, extra)
2020-03-13 15:44:24 +05:30
2022-07-23 23:45:48 +05:30
trackers.each do |tracker|
tracker.capture_exception(exception, **context_payload)
2020-01-01 13:55:28 +05:30
end
2022-07-23 23:45:48 +05:30
end
2020-01-01 13:55:28 +05:30
2022-07-23 23:45:48 +05:30
def default_trackers
[].tap do |destinations|
destinations << Raven if Raven.configuration.server
# There is a possibility that this method is called before Sentry is
# configured. Since Sentry 4.0, some methods of Sentry are forwarded to
# to `nil`, hence we have to check the client as well.
destinations << ::Sentry if ::Sentry.get_current_client && ::Sentry.configuration.dsn
destinations << Logger
2020-01-01 13:55:28 +05:30
end
end
def sentry_dsn
2022-05-07 20:08:51 +05:30
return unless sentry_configurable?
2020-01-01 13:55:28 +05:30
return unless Gitlab.config.sentry.enabled
Gitlab.config.sentry.dsn
end
2022-05-07 20:08:51 +05:30
def new_sentry_dsn
return unless sentry_configurable?
return unless Gitlab::CurrentSettings.respond_to?(:sentry_enabled?)
return unless Gitlab::CurrentSettings.sentry_enabled?
Gitlab::CurrentSettings.sentry_dsn
end
def new_sentry_environment
return unless Gitlab::CurrentSettings.respond_to?(:sentry_environment)
Gitlab::CurrentSettings.sentry_environment
end
def sentry_configurable?
Rails.env.production? || Rails.env.development?
end
2020-01-01 13:55:28 +05:30
def should_raise_for_dev?
Rails.env.development? || Rails.env.test?
end
2020-04-22 19:07:51 +05:30
# Group common, mostly non-actionable exceptions by type and message,
# rather than cause
2021-03-08 18:12:59 +05:30
def custom_fingerprinting(event, ex)
2020-04-22 19:07:51 +05:30
return event unless CUSTOM_FINGERPRINTING.include?(ex.class.name)
event.fingerprint = [ex.class.name, ex.message]
2021-03-08 18:12:59 +05:30
end
2020-04-22 19:07:51 +05:30
2021-03-08 18:12:59 +05:30
def inject_context_for_exception(event, ex)
2022-01-26 12:08:38 +05:30
sql = Gitlab::ExceptionLogFormatter.find_sql(ex)
event.extra[:sql] = sql if sql
2020-04-22 19:07:51 +05:30
end
2020-01-01 13:55:28 +05:30
end
end
end