lib/ougai/logger.rb
# frozen_string_literal: true
module Ougai
# Main Logger
# @attr [String] default_message Use this if log message is not specified (by default this is 'No message').
# @attr [String] exc_key The field name of Exception (by default this is :err).
# @attr [Hash] with_fields The fields appending to all logs.
# @attr [Proc] before_log Hook before logging.
class Logger < ::Logger
alias_method :super_add, :add
include Logging
attr_accessor :default_message, :exc_key
def initialize(*, **)
super
@before_log = nil
@default_message = 'No message'
@exc_key = :err
@with_fields = {}
@formatter = create_formatter if @formatter.nil?
end
class << self
def child_class
@child_class ||= ChildLogger
end
def child_class=(klass)
@child_class = klass
end
def inherited(subclass)
subclass.child_class = Class.new(ChildLogger)
end
end
# Broadcasts the same logs to the another logger
# @param logger [Logger] The logger receiving broadcast logs.
def self.broadcast(logger)
Module.new do |mdl|
define_method(:_log) do |*args, &block|
logger._log(*args, &block)
super(*args, &block)
end
define_method(:level=) do |level|
logger.level = level
super(level)
end
define_method(:close) do
logger.close
super()
end
end
end
def level=(severity)
if severity.is_a?(Integer)
@level = severity
return
end
if severity.to_s.downcase == 'trace'
@level = TRACE
return
end
super
end
# Creates a child logger and returns it.
# @param fields [Hash] The fields appending to all logs
# @return [ChildLogger] A created child logger
def child(fields = {})
ch = self.class.child_class.new(self, fields)
if !block_given?
ch
else
yield ch
end
end
# @private
def chain(severity, args, fields, hooks)
hooks.push(@before_log) if @before_log
write(severity, args, weak_merge!(fields, @with_fields), hooks)
end
protected
# @private
def append(severity, args)
hooks = @before_log ? [@before_log] : []
write(severity, args, @with_fields, hooks)
end
def create_formatter
Formatters::Bunyan.new
end
private
def format_severity(severity)
to_label(severity)
end
def write(severity, args, fields, hooks)
data = weak_merge!(to_item(args), fields)
hooks.each do |hook|
return false if hook.call(data) == false
end
super_add(severity, data)
end
def to_item(args)
msg, ex, data = args
if msg.nil?
{ msg: @default_message }
elsif ex.nil?
create_item_with_1arg(msg)
elsif data.nil?
create_item_with_2args(msg, ex)
else
create_item_with_3args(msg, ex, data)
end
end
def create_item_with_1arg(arg)
item = {}
if arg.is_a?(Exception)
item[:msg] = arg.to_s
set_exc(item, arg)
elsif arg.is_a?(String)
item[:msg] = arg
else
item.merge!(as_hash(arg))
item[:msg] ||= @default_message
end
item
end
def create_item_with_2args(arg1, arg2)
item = {}
if arg2.is_a?(Exception) # msg, ex
item[:msg] = arg1.to_s
set_exc(item, arg2)
elsif arg1.is_a?(Exception) # ex, data
set_exc(item, arg1)
item.merge!(as_hash(arg2))
item[:msg] ||= arg1.to_s
else # msg, data
item[:msg] = arg1.to_s
item.merge!(as_hash(arg2))
end
item
end
def create_item_with_3args(msg, ex, data)
{}.tap do |item|
set_exc(item, ex) if ex.is_a?(Exception)
item.merge!(as_hash(data))
item[:msg] = msg.to_s
end
end
def set_exc(item, exc)
item[@exc_key] = @formatter.serialize_exc(exc)
end
def as_hash(data)
if data.is_a?(Hash) || data.respond_to?(:to_hash)
data
else
{ data: data }
end
end
end
end