1
0
Fork 0
mirror of https://github.com/mperham/sidekiq.git synced 2022-11-09 13:52:34 -05:00
mperham--sidekiq/lib/sidekiq/logger.rb
Matt Robinson d4fca4683c
Fix logger with from deleting existing context (#4770)
If using `with` to add logging context, any context that has the same
keys as existing context will disappear after the with block completes,
instead of reverting back to the original context.

This fixes that by restoring the original context directly instead of
just deleting the keys from the `with` context.
2020-12-23 12:44:35 -08:00

166 lines
3.6 KiB
Ruby

# frozen_string_literal: true
require "logger"
require "time"
module Sidekiq
module Context
def self.with(hash)
orig_context = current.dup
current.merge!(hash)
yield
ensure
Thread.current[:sidekiq_context] = orig_context
end
def self.current
Thread.current[:sidekiq_context] ||= {}
end
end
module LoggingUtils
LEVELS = {
"debug" => 0,
"info" => 1,
"warn" => 2,
"error" => 3,
"fatal" => 4
}
LEVELS.default_proc = proc do |_, level|
Sidekiq.logger.warn("Invalid log level: #{level.inspect}")
nil
end
def debug?
level <= 0
end
def info?
level <= 1
end
def warn?
level <= 2
end
def error?
level <= 3
end
def fatal?
level <= 4
end
def local_level
Thread.current[:sidekiq_log_level]
end
def local_level=(level)
case level
when Integer
Thread.current[:sidekiq_log_level] = level
when Symbol, String
Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
when nil
Thread.current[:sidekiq_log_level] = nil
else
raise ArgumentError, "Invalid log level: #{level.inspect}"
end
end
def level
local_level || super
end
# Change the thread-local level for the duration of the given block.
def log_at(level)
old_local_level = local_level
self.local_level = level
yield
ensure
self.local_level = old_local_level
end
# Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
# FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
def add(severity, message = nil, progname = nil, &block)
severity ||= ::Logger::UNKNOWN
progname ||= @progname
return true if @logdev.nil? || severity < level
if message.nil?
if block
message = yield
else
message = progname
progname = @progname
end
end
@logdev.write format_message(format_severity(severity), Time.now, progname, message)
end
end
class Logger < ::Logger
include LoggingUtils
def initialize(*args, **kwargs)
super
self.formatter = Sidekiq.log_formatter
end
module Formatters
class Base < ::Logger::Formatter
def tid
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
end
def ctx
Sidekiq::Context.current
end
def format_context
if ctx.any?
" " + ctx.compact.map { |k, v|
case v
when Array
"#{k}=#{v.join(",")}"
else
"#{k}=#{v}"
end
}.join(" ")
end
end
end
class Pretty < Base
def call(severity, time, program_name, message)
"#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
end
end
class WithoutTimestamp < Pretty
def call(severity, time, program_name, message)
"pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
end
end
class JSON < Base
def call(severity, time, program_name, message)
hash = {
ts: time.utc.iso8601(3),
pid: ::Process.pid,
tid: tid,
lvl: severity,
msg: message
}
c = ctx
hash["ctx"] = c unless c.empty?
Sidekiq.dump_json(hash) << "\n"
end
end
end
end
end