2012-03-09 10 views
8

Tengo una Logger ejemplo:redirección stderr a instancia Logger

require 'logger' 
logger = Logger.new('foo.log', 'weekly') 

quiero redireccionar los errores de ejecución (salida stderr) en el registro también. He encontrado this forum thread que tiene el consejo:

new_fd = logger.get_logger_file_descriptor 
$stderr.reopen new_fd 

Sin embargo, Logger no cuenta con un método de instancia get_logger_file_descriptor, ni puedo encontrar métodos expuestos alrededor de acceder al dispositivo de registro o archivo.

¿Cómo puedo hacer que todos los resultados de $ stderr entren en el registro?

Respuesta

15

Si va a crear el registrador usted mismo, puede crear el objeto File primero, y luego utilizarlo para crear el registrador y asignarlo a $stderr:

log_file = File.new('foo.log', 'a') 
logger = Logger.new(log_file, 'weekly') 
$stderr = log_file #usually no need to use reopen 

Tenga en cuenta que esto se traducirá en la salida del registro confundirse con la salida $stderr, lo que puede causar problemas si analiza el archivo de registro esperando que esté en un formato determinado (esto también sucederá con su solución).

Si no tiene el archivo subyacente pero acaba de recibir el logger desde otro lugar, es un poco más complicado. Lo que se necesita es un objeto similar IO que se puede asignar a $stderr y pasa todo lo que está escrito en el registrador. La clase IO en Ruby lamentablemente está muy relacionada con el sistema de E/S subyacente (descriptores de archivos y similares), y no hay una interfaz general que se pueda usar para crear flujos de entrada y salida. (StringIO siendo la excepción notable).

Sin embargo la mayoría, si no todos, de los métodos de salida en última instancia IO pasar por #write, por lo que esta reemplazando un método que puede acercarse a lo que está buscando:

class IOToLog < IO 

    def initialize(logger) 
    @logger = logger 
    end 

    def write(string) 
    #assume anything written to stderr is an error 
    @logger.error(message) 
    end 

end 

logger = get_logger_from_somewhere 

$stderr = IOToLog.new(logger) 

Ahora nada escrito $stderr terminará yendo al archivo de registro. Sin embargo, el formateo será un poco extraño. Cada vez que cualquiera de los métodos de escritura llame al #write, se hará una nueva entrada en el archivo de registro. Por ejemplo, #puts llamado con una matriz llamará a #write para cada entrada de la matriz, y nuevamente con un carácter de nueva línea entre cada entrada, lo que resulta en 2n - 1 entradas de registro, n - 1 de las cuales estará en blanco.

Puede hacer que el método Oreemplazado sea más complejo para manejar esto, quizás usando un búfer interno, y solo llame al registrador cuando crea que tiene un mensaje completo. Alternativamente, usted puede anular los métodos individuales para escribir en el registrador. Si hiciera esto, la clase IOToLog no necesariamente tendría que heredar de IO.

Su mejor solución dependerá de cómo quiera que aparezca la salida de error estándar en el archivo de registro, cómo su programa usa $stderr, y la cantidad de trabajo que desea hacer implementando métodos desde IO.

+0

¡Gran respuesta, gracias! En mi caso, estoy creando el registrador para que su primera solución sea limpia y elegante. Me gusta especialmente que haya incluido la solución para el otro caso. (Aunque en tal caso podría abogar mi intento de acercarme y tomar el archivo directamente.) – Phrogz

+0

El segundo método no funciona para mí en jruby. Me sale el siguiente error: (Errno :: EBADF) Descriptor de archivo incorrecto –

+0

Debería haber '@ logger.error (string)' Creo – lojza

2

El mejor que he podido llegar a este tramo es todo-Hack:

$stderr.reopen logger.instance_variable_get(:@logdev).dev 

Funciona, pero por supuesto rompe la encapsulación de Logger que supongo que estaba allí por una razón.