以下是我使用Log4r的一些现有日志记录代码的工作原理。正如你在WorkerX :: a_method中看到的那样,在我记录消息的任何时候我都希望包含类名和调用方法(我不想要所有调用者历史记录或任何其他噪声,这是我的目的背后LgrHelper)。
class WorkerX
include LgrHelper
def initialize(args = {})
@logger = Lgr.new({:debug => args[:debug], :logger_type => 'WorkerX'})
end
def a_method
error_msg("some error went down here")
# This prints out: "WorkerX::a_method - some error went down here"
end
end
class Lgr
require 'log4r'
include Log4r
def initialize(args = {}) # args: debug boolean, logger type
@debug = args[:debug]
@logger_type = args[:logger_type]
@logger = Log4r::Logger.new(@logger_type)
format = Log4r::PatternFormatter.new(:pattern => "%l:\t%d - %m")
outputter = Log4r::StdoutOutputter.new('console', :formatter => format)
@logger.outputters = outputter
if @debug then
@logger.level = DEBUG
else
@logger.level = INFO
end
end
def debug(msg)
@logger.debug(msg)
end
def info(msg)
@logger.info(msg)
end
def warn(msg)
@logger.warn(msg)
end
def error(msg)
@logger.error(msg)
end
def level
@logger.level
end
end
module LgrHelper
# This module should only be included in a class that has a @logger instance variable, obviously.
protected
def info_msg(msg)
@logger.info(log_intro_msg(self.method_caller_name) + msg)
end
def debug_msg(msg)
@logger.debug(log_intro_msg(self.method_caller_name) + msg)
end
def warn_msg(msg)
@logger.warn(log_intro_msg(self.method_caller_name) + msg)
end
def error_msg(msg)
@logger.error(log_intro_msg(self.method_caller_name) + msg)
end
def log_intro_msg(method)
msg = class_name
msg += '::'
msg += method
msg += ' - '
msg
end
def class_name
self.class.name
end
def method_caller_name
if /`(.*)'/.match(caller[1]) then # caller.first
$1
else
nil
end
end
end
我真的不喜欢这种方法。我宁愿只使用现有的@logger实例变量来打印消息,并且足够聪明以了解上下文。怎样才能采用这种或类似的简单方法?
我的环境是Rails 2.3.11(现在!)。
答案 0 :(得分:1)
在使用extend
发布我的回答后,(请参阅下面的“编辑”),我想我会尝试使用set_trace_func
来保持一种堆栈跟踪在我发布的讨论中。这是我的最终解决方案; set_trace_proc
调用将被放入初始化程序或类似程序中。
#!/usr/bin/env ruby
# Keep track of the classes that invoke each "call" event
# and the method they called as an array of arrays.
# The array is in the format: [calling_class, called_method]
set_trace_func proc { |event, file, line, id, bind, klass|
if event == "call"
Thread.current[:callstack] ||= []
Thread.current[:callstack].push [klass, id]
elsif event == "return"
Thread.current[:callstack].pop
end
}
class Lgr
require 'log4r'
include Log4r
def initialize(args = {}) # args: debug boolean, logger type
@debug = args[:debug]
@logger_type = args[:logger_type]
@logger = Log4r::Logger.new(@logger_type)
format = Log4r::PatternFormatter.new(:pattern => "%l:\t%d - %m")
outputter = Log4r::StdoutOutputter.new('console', :formatter => format)
@logger.outputters = outputter
if @debug then
@logger.level = DEBUG
else
@logger.level = INFO
end
end
def debug(msg)
@logger.debug(msg)
end
def info(msg)
@logger.info(msg)
end
def warn(msg)
@logger.warn(msg)
end
def error(msg)
@logger.error(msg)
end
def level
@logger.level
end
def invoker
Thread.current[:callstack] ||= []
( Thread.current[:callstack][-2] || ['Kernel', 'main'] )
end
end
class CallingMethodLogger < Lgr
[:info, :debug, :warn, :error].each do |meth|
define_method(meth) { |msg| super("#{invoker[0]}::#{invoker[1]} - #{msg}") }
end
end
class WorkerX
def initialize(args = {})
@logger = CallingMethodLogger.new({:debug => args[:debug], :logger_type => 'WorkerX'})
end
def a_method
@logger.error("some error went down here")
# This prints out: "WorkerX::a_method - some error went down here"
end
end
w = WorkerX.new
w.a_method
我不知道对proc的调用会对应用程序的性能产生多大影响(如果有的话);如果它最终成为一个问题,也许对调用类不那么聪明的东西(就像下面我的旧答案)会更好。
[编辑:以下是我上面提到的旧答案。]
如何使用extend
?这是一个快速而肮脏的脚本,我从你的代码中加入来测试它;我不得不重新排序以避免错误,但代码是相同的,除了LgrHelper
(我重命名为CallingMethodLogger
)和WorkerX
的初始化程序的第二行:
#!/usr/bin/env ruby
module CallingMethodLogger
def info(msg)
super("#{@logger_type}::#{method_caller_name} - " + msg)
end
def debug(msg)
super("#{@logger_type}::#{method_caller_name} - " + msg)
end
def warn(msg)
super("#{@logger_type}::#{method_caller_name} - " + msg)
end
def error(msg)
super("#{@logger_type}::#{method_caller_name} - " + msg)
end
def method_caller_name
if /`(.*)'/.match(caller[1]) then # caller.first
$1
else
nil
end
end
end
class Lgr
require 'log4r'
include Log4r
def initialize(args = {}) # args: debug boolean, logger type
@debug = args[:debug]
@logger_type = args[:logger_type]
@logger = Log4r::Logger.new(@logger_type)
format = Log4r::PatternFormatter.new(:pattern => "%l:\t%d - %m")
outputter = Log4r::StdoutOutputter.new('console', :formatter => format)
@logger.outputters = outputter
if @debug then
@logger.level = DEBUG
else
@logger.level = INFO
end
end
def debug(msg)
@logger.debug(msg)
end
def info(msg)
@logger.info(msg)
end
def warn(msg)
@logger.warn(msg)
end
def error(msg)
@logger.error(msg)
end
def level
@logger.level
end
end
class WorkerX
def initialize(args = {})
@logger = Lgr.new({:debug => args[:debug], :logger_type => 'WorkerX'})
@logger.extend CallingMethodLogger
end
def a_method
@logger.error("some error went down here")
# This prints out: "WorkerX::a_method - some error went down here"
end
end
w = WorkerX.new
w.a_method
输出结果为:
ERROR: 2011-07-24 20:01:40 - WorkerX::a_method - some error went down here
缺点是,通过这种方法,调用者的类名不会自动计算出来;它是基于传递到@logger_type
实例的Lgr
显式的。但是,您可以使用其他方法来获取类的实际名称 - 可能类似于call_stack gem或使用Kernel#set_trace_func
- 请参阅this thread。