¿Cómo puedo conectarme a la impresión de Perl?

Aquí hay un escenario. Tiene una gran cantidad de scripts heredados, todos usan una biblioteca común. Dichos scripts usan la instrucción ‘print’ para salida de diagnóstico. No se permiten cambios a los guiones: tienen un amplio scope, tienen sus aprobaciones y hace tiempo que dejaron los fructíferos valles de la supervisión y el control.

Ahora ha llegado una nueva necesidad: el registro ahora debe agregarse a la biblioteca. Esto debe hacerse de forma automática y transparente, sin que los usuarios de la biblioteca estándar tengan que cambiar sus scripts. Los métodos de biblioteca comunes simplemente pueden tener llamadas de registro agregadas a ellos; esa es la parte fácil. La parte difícil radica en el hecho de que la salida de diagnóstico de estos scripts siempre se muestra con la instrucción ‘print’. Esta salida de diagnóstico debe ser almacenada, pero igual de importante, procesada.

Como ejemplo de este procesamiento, la biblioteca solo debe registrar las líneas impresas que contienen las palabras ‘advertencia’, ‘error’, ‘aviso’ o ‘atención’. El siguiente Código de Ejemplo Extremadamente Trivial y Contráctido ™ registraría parte de dicha salida:

sub CheckPrintOutput { my @output = @_; # args passed to print eventually find their way here. foreach my $value (@output) { Log->log($value) if $value =~ /warning|error|notice|attention/i; } } 

(Me gustaría evitar problemas tales como ‘lo que realmente debería registrarse’, ‘imprimir no debería usarse para diagnósticos’, ‘perl sucks’, o ‘este ejemplo tiene los defectos xy y z’ … esto es muy simplificado para la brevedad y la claridad.)

El problema básico se reduce a capturar y procesar los datos pasados ​​para imprimir (o cualquier versión interna de Perl, a lo largo de esas líneas de razonamiento). ¿Es posible? ¿Hay alguna forma de hacerlo limpiamente? ¿Hay algún módulo de registro que tenga ganchos para poder hacerlo? ¿O es algo que debería evitarse como la peste, y debería dejar de capturar y procesar la salida impresa?

Adicional: debe ejecutarse multiplataforma: Windows y * nix por igual. El proceso de ejecución de los scripts debe ser el mismo, al igual que el resultado del script.

Adicional adicional: una sugerencia interesante hecha en los comentarios de la respuesta de Codelogic:

Puede subclasificar http://perldoc.perl.org/IO/Handle.html y crear su propio identificador de archivo que hará el trabajo de registro. – Kamil Kisiel

Esto podría hacerlo, con dos advertencias:

1) Necesitaría una forma de exportar esta funcionalidad a cualquier persona que use la biblioteca común. Tendría que aplicarse automáticamente a STDOUT y probablemente a STDERR también.

2) la documentación de IO :: Handle dice que no se puede subclasificar, y mis bashs hasta ahora han sido infructuosos. ¿Se necesita algo especial para hacer que sublclassing IO :: Handle funcione? El estándar ‘base de uso’ IO :: Handle ‘y luego anula los métodos nuevos / de impresión parecen no hacer nada.

Edición final: parece que IO :: Handle es un callejón sin salida, pero Tie :: Handle puede hacerlo. Gracias por todas las sugerencias; todos son realmente buenos Voy a probar la ruta Tie :: Handle. Si causa problemas, ¡volveré!

Adición: Tenga en cuenta que después de trabajar un poco con esto, descubrí que Tie :: Handle funcionará, si no hace nada complicado. Si utiliza alguna de las características de IO :: Handle con su STDOUT o STDERR vinculado, es básicamente un crapshoot para que funcionen de manera confiable. No pude encontrar la manera de hacer que el método de autoenjuague de IO :: Handle funcione en mi atadura. encargarse de. Si habilitaba autoflush antes de atar el mango, funcionaría. Si eso funciona para usted, la ruta de Tie :: Handle puede ser aceptable.

Hay una serie de integraciones que puede anular (ver perlsub ). Sin embargo, print es uno de los integradores que no funciona de esta manera. Las dificultades de anular la print se detallan en el hilo de Perlmonk .

Sin embargo, puedes

  1. Crea un paquete
  2. Ate un mango
  3. Seleccione este mango

Ahora, un par de personas han dado el marco básico, pero funciona más o menos así:

 package IO::Override; use base qw; use Symbol qw; sub TIEHANDLE { return bless geniosym, __PACKAGE__ } sub PRINT { shift; # You can do pretty much anything you want here. # And it's printing to what was STDOUT at the start. # print $OLD_STDOUT join( '', 'NOTICE: ', @_ ); } tie *PRINTOUT, 'IO::Override'; our $OLD_STDOUT = select( *PRINTOUT ); 

Puede anular printf de la misma manera:

 sub PRINTF { shift; # You can do pretty much anything you want here. # And it's printing to what was STDOUT at the start. # my $format = shift; print $OLD_STDOUT join( '', 'NOTICE: ', sprintf( $format, @_ )); } 

Ver Tie :: Handle para todo lo que pueda anular el comportamiento de STDOUT.

Puede usar Perl’s select para redirigir STDOUT.

 open my $fh, ">log.txt"; print "test1\n"; my $current_fh = select $fh; print "test2\n"; select $current_fh; print "test3\n"; 

El manejador de archivo puede ser cualquier cosa, incluso una conexión a otro proceso que post procesa sus mensajes de registro.

PerlIO :: tee en el módulo PerlIO :: Util parece que le permite ‘enviar’ la salida de un manejador de archivo a múltiples destinos (por ejemplo, procesador de registro y STDOUT).

Muchas opciones Use select () para cambiar el identificador de archivo que la impresión utiliza de forma predeterminada. O ate STDOUT. O reabrirlo. O aplique una capa IO a ella.

Esta no es la respuesta a su problema, pero debería poder adoptar la lógica para su propio uso. Si no, tal vez alguien más lo encuentre útil.

Captura encabezados malformados antes de que sucedan …

 package PsychicSTDOUT; use strict; my $c = 0; my $malformed_header = 0; open(TRUE_STDOUT, '>', '/dev/stdout'); tie *STDOUT, __PACKAGE__, (*STDOUT); sub TIEHANDLE { my $class = shift; my $handles = [@_]; bless $handles, $class; return $handles; } sub PRINT { my $class = shift; if (!$c++ && @_[0] !~ /^content-type/i) { my (undef, $file, $line) = caller; print STDERR "Missing content-type in $file at line $line!!\n"; $malformed_header = 1; } return 0 if ($malformed_header); return print TRUE_STDOUT @_; } 1; 

uso:

 use PsychicSTDOUT; print "content-type: text/html\n\n"; #try commenting out this line print "\n"; print "\n"; 

Podría ejecutar el script desde un script contenedor que capture el stdout del script original y escriba el resultado en algún lugar sensato.