После публикации моего ответа с использованием extend
(см. " EDIT " ниже), я подумал, что попробую использовать 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нить .