Quelque chose comme une fonctionnalité de té dans l'enregistreur.
Réponses
Trop de publicités?Vous pouvez écrire un pseudo - IO
classe qui va écrire à plusieurs IO
objets. Quelque chose comme:
class MultiIO
def initialize(*targets)
@targets = targets
end
def write(*args)
@targets.each {|t| t.write(*args)}
end
def close
@targets.each(&:close)
end
end
Puis le définir comme votre fichier de log:
log_file = File.open("log/debug.log", "a")
Logger.new MultiIO.new(STDOUT, log_file)
Chaque fois Logger
des appels puts
sur votre MultiIO
objet, il conviendra d'écrire à la fois STDOUT
et votre fichier de log.
Edit: je suis allé de l'avant et a trouvé le reste de l'interface. Un journal de l'appareil doit répondre aux write
et close
(pas puts
). Tant que MultiIO
répond aux besoins des personnes et des procurations à la vraie IO objets, cela devrait fonctionner.
@ La solution de David est très bonne. J'ai créé une classe de délégation générique pour plusieurs cibles en fonction de son code.
require 'logger'
class MultiDelegator
def initialize(*targets)
@targets = targets
end
def self.delegate(*methods)
methods.each do |m|
define_method(m) do |*args|
@targets.map { |t| t.send(m, *args) }
end
end
self
end
class <<self
alias to new
end
end
log_file = File.open("debug.log", "a")
log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)
Vous pouvez également ajouter plusieurs fonctionnalités de journalisation de périphérique directement dans le consignateur:
require 'logger'
class Logger
# Creates or opens a secondary log file.
def attach(name)
@logdev.attach(name)
end
# Closes a secondary log file.
def detach(name)
@logdev.detach(name)
end
class LogDevice # :nodoc:
attr_reader :devs
def attach(log)
@devs ||= {}
@devs[log] = open_logfile(log)
end
def detach(log)
@devs ||= {}
@devs[log].close
@devs.delete(log)
end
alias_method :old_write, :write
def write(message)
old_write(message)
@devs ||= {}
@devs.each do |log, dev|
dev.write(message)
end
end
end
end
Par exemple:
logger = Logger.new(STDOUT)
logger.warn('This message goes to stdout')
logger.attach('logfile.txt')
logger.warn('This message goes both to stdout and logfile.txt')
logger.detach('logfile.txt')
logger.warn('This message goes just to stdout')
Bien que j'aime bien les suggestions ci-dessus, j'ai constaté que j'avais le même problème, mais je voulais pouvoir utiliser différents niveaux de journalisation pour STDERR et le fichier (comme je le pouvais avec les plus grands cadres de journalisation comme NLog). Je me suis retrouvé avec une stratégie de routage qui multiplexe au niveau de l'enregistreur plutôt qu'au niveau des E / S, afin que chaque enregistreur puisse ensuite fonctionner à des niveaux de journalisation indépendants:
class MultiLogger
def initialize(*targets)
@targets = targets
end
%w(log debug info warn error).each do |m|
define_method(m) do |*args|
@targets.map { |t| t.send(m, *args) }
end
end
end
$stderr_log = Logger.new(STDERR)
$file_log = Logger.new(File.open('logger.log','a'))
$stderr_log.level = Logger::INFO
$file_log.level = Logger::DEBUG
$log = MultiLogger.new( $stderr_log, $file_log )
Voici une autre mise en œuvre, inspirée par @jonas054's réponse.
Il utilise un modèle similaire à Delegator
. De cette façon, vous n'avez pas à énumérer toutes les méthodes que vous souhaitez déléguer, car il va déléguer toutes les méthodes qui sont définies dans l'une quelconque des objets cibles:
class Tee < DelegateToAllClass(IO)
end
$stdout = Tee.new(STDOUT, File.open("#{__FILE__}.log", "a"))
Vous devriez être capable de l'utiliser avec l'Enregistreur.
delegate_to_all.rb est disponible ici: https://gist.github.com/TylerRick/4990898