1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
|
# frozen_string_literal: true
require 'logger'
module Sentry
class Breadcrumb
module SentryLogger
LEVELS = {
::Logger::DEBUG => 'debug',
::Logger::INFO => 'info',
::Logger::WARN => 'warn',
::Logger::ERROR => 'error',
::Logger::FATAL => 'fatal'
}.freeze
def add(*args, &block)
super
add_breadcrumb(*args, &block)
nil
end
def add_breadcrumb(severity, message = nil, progname = nil)
# because the breadcrumbs now belongs to different Hub's Scope in different threads
# we need to make sure the current thread's Hub has been set before adding breadcrumbs
return unless Sentry.initialized? && Sentry.get_current_hub
category = "logger"
# this is because the nature of Ruby Logger class:
#
# when given 1 argument, the argument will become both message and progname
#
# ```
# logger.info("foo")
# # message == progname == "foo"
# ```
#
# and to specify progname with a different message,
# we need to pass the progname as the argument and pass the message as a proc
#
# ```
# logger.info("progname") { "the message" }
# ```
#
# so the condition below is to replicate the similar behavior
if message.nil?
if block_given?
message = yield
category = progname
else
message = progname
end
end
return if ignored_logger?(progname) || message == ""
# some loggers will add leading/trailing space as they (incorrectly, mind you)
# think of logging as a shortcut to std{out,err}
message = message.to_s.strip
last_crumb = current_breadcrumbs.peek
# try to avoid dupes from logger broadcasts
if last_crumb.nil? || last_crumb.message != message
level = Sentry::Breadcrumb::SentryLogger::LEVELS.fetch(severity, nil)
crumb = Sentry::Breadcrumb.new(
level: level,
category: category,
message: message,
type: severity >= 3 ? "error" : level
)
Sentry.add_breadcrumb(crumb, hint: { severity: severity })
end
end
private
def ignored_logger?(progname)
progname == LOGGER_PROGNAME ||
Sentry.configuration.exclude_loggers.include?(progname)
end
def current_breadcrumbs
Sentry.get_current_scope.breadcrumbs
end
end
end
end
::Logger.send(:prepend, Sentry::Breadcrumb::SentryLogger)
|