2008-12-22 18 views
18

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 alcance, tienen sus aprobaciones y hace tiempo que dejaron los fructíferos valles de la supervisión y el control.¿Cómo puedo conectarme a la impresión de Perl?

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 código de ejemplo a continuación extremadamente trivial y artificial (tm) grabaría algunos 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 cuestiones tales como 'lo que en realidad se debería registrar', 'impresión no debe utilizarse para diagnósticos ',' perl sucks ', o' este ejemplo tiene los defectos xy y z '... esto se simplifica enormemente por brevedad y claridad.)

El problema básico se reduce a la captura y procesamiento de datos pasados ​​para imprimir (o cualquier perl incorporado, 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 hecho en los comentarios de la respuesta de codelogic:

Usted puede subclase http://perldoc.perl.org/IO/Handle.html y crear su propia identificador de archivo, que va a hacer el trabajo de registro. - Kamil Kisiel

Esto podría hacerlo, con dos salvedades:

1) que iba a necesitar una manera de exportar esta funcionalidad para cualquier persona que utiliza la biblioteca común. Tendría que aplicarse automáticamente a STDOUT y probablemente a STDERR también.

2) La documentación de the IO::Handle dice que no se puede crear una subclase, y mis intentos 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 con esto un poco, encontré 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.

+0

Entonces, ¿qué es * * Se le permite cambiar? ¿Líneas de comando? ¿Archivos de parámetros? Por ejemplo, digamos que dije "Sí, puedes enganchar impresión", ¿cuál es el alcance de lo que podrías hacer? – Axeman

+0

Puedo cambiar cualquier cosa en la biblioteca común. El usuario no debe necesitar ejecutar sus scripts de forma diferente, o debe pasar algo nuevo en la línea de comando. La secuencia final de datos en STDOUT y STDERR debe ser la misma que antes. –

+0

¿Qué sucede con la salida original? ¿Puedes seguirlo y procesar desde allí? – user44511

Respuesta

24

Hay una serie de incorporaciones que puede anular (consulte perlsub). Sin embargo, print es uno de los integradores que no funciona de esta manera. Las dificultades de anular print se detallan en este perlmonk's thread.

Sin embargo, usted puede

  1. Crear un paquete
  2. Tie un mango
  3. Seleccionar este mango.

Ahora, un par de personas han dado el marco básico, pero funciona a cabo algo así como esto:

package IO::Override; 
use base qw<Tie::Handle>; 
use Symbol qw<geniosym>; 

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, @_)); 
} 

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

+0

Oh, esto se ve bien. ¿Esto cambia el comportamiento de la impresión si usan imprimir para escribir en los manejadores de archivos, o simplemente para STDOUT? –

+0

Mirándolo más. Ya veo, está atado a un archivo único a la vez, ¿verdad? Voy a dar un giro en breve e informaré. –

+0

El "identificador de archivo" es solo un medio. Una vez que tengamos el flujo dirigido hacia donde podemos especificar el comportamiento, podemos escribir en tantos controles reales como queramos, filtrarlo como queramos, incluso hacer llamadas a la base de datos, si eso es lo que queríamos hacer. – Axeman

8

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 identificador de archivo puede ser cualquier cosa, incluso un conducto a otro proceso que post procesa sus mensajes de registro.

PerlIO::tee en el módulo PerlIO::Util parece que le permite 'poner' la salida de un identificador de archivo a varios destinos (por ejemplo, procesador de registro y STDOUT).

+0

seleccionar está todo bien, pero también tenemos que procesar esa información. ¿Hay un tipo de manejador de archivo que da ganchos para evaluar y hacer algo con los datos que se le pasan? –

+0

Además, no podemos modificar el comportamiento actual de estos scripts, por lo que el resultado también debe permanecer en STDOUT. –

+0

Puede abrir una manejador de archivo como un conducto a otro proceso. Haga las tareas de registro allí, luego puede imprimir desde el interior. – flussence

-1

Puede ejecutar la secuencia de comandos desde una secuencia de comandos envoltorio que captura el stdout del script original y escribe el resultado en algún lugar sensato.

+0

Lamentablemente, esto infringe el requisito de "secuencia de comandos debe ejecutar el mismo que antes". El usuario no debe tener que hacer nada diferente para obtener esta información de registro. –

+0

No viola nada. Nada dice que lo que se ejecuta cuando se escribe 'perl' es el perl real. –

+0

Lo suficientemente cierto desde el punto de vista de la implementación general; un script de envoltura es una posible solución. Sin embargo, pedirle a los usuarios que ejecuten otro script simplemente para ejecutar su script no es una solución que funcione en este escenario: la línea de comando, la distribución de perl y el resultado de la consola deben permanecer iguales. –

6

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.

3

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.

La captura de los encabezados con formato incorrecto 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 "<html>\n"; 
print "</html>\n"; 
Cuestiones relacionadas