• Publicidad

Archivo log simple y captura de errores (sin módulos extra)

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Archivo log simple y captura de errores (sin módulos extra)

Notapor pablgonz » 2020-04-27 22:47 @991

Hola a todos. Estoy intentando crear un archivo .log bastante simple para un pequeño script y me asaltaron algunas dudas sobre cómo hacerlo de manera correcta y sin módulos extra (solo los que trae la distribución básica).

El código que poseo es el siguiente:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.26;
  3. use Getopt::Long qw(:config bundling_values require_order no_ignore_case);
  4. use FileHandle;
  5. use File::Copy;
  6. use POSIX qw(strftime);
  7. use File::Temp qw(tempdir);
  8. use Cwd;
  9. use autodie;
  10.  
  11. my $verbose  = 0; # verbose
  12. my $debug;        # debug
  13.  
  14. my $result = GetOptions (
  15.     'debug!'         => \$debug,   # debug
  16.     'verbose!'       => \$verbose, # verbose
  17.     ) or die "No es una opción valida\n";
  18.  
  19. ### Mensajes de error extendidos
  20. sub exterr () {
  21.     chomp(my $msg_errno = $!);
  22.     chomp(my $msg_extended_os_error = $^E);
  23.     if ($msg_errno eq $msg_extended_os_error) {
  24.         $msg_errno;
  25.     }
  26.     else {
  27.         "$msg_errno/$msg_extended_os_error";
  28.     }
  29. }
  30.  
  31. ### Nombre del script
  32. chomp(my $scriptname = `basename $0`);
  33. $scriptname =~ s/\.pl$//;
  34.  
  35. ### Directorios y archivos
  36. my $tempDir = tempdir( CLEANUP => 1);
  37. my $workdir = cwd;
  38. my $LogFileName = "$tempDir/$scriptname.log";
  39. my $Log_file = new FileHandle;
  40. my $StampTime = strftime ("%y/%m/%d %H:%M:%S", localtime);
  41.  
  42. ### Abrimos
  43. $Log_file->open("> $LogFileName");
  44. $Log_file->print("[$StampTime] * Se inicio $scriptname en $workdir\n");
  45. $Log_file->print("[$StampTime] * Se creo el directorio $tempDir\n");
  46. $Log_file->print("[$StampTime] * Se creo el archivo $tempDir/$scriptname.log\n");
  47.  
  48. ### Log simple
  49. sub Log {
  50.   my $msg = shift;
  51.   my $now  = strftime ("%y/%m/%d %H:%M:%S", localtime);
  52.   $Log_file->print(sprintf  "[%s] %s\n", $now, $msg);
  53.   print "$msg\n" if $verbose;
  54.   return 0
  55. }
  56.  
  57. ### Captura y ejecución de comandos
  58. sub RUNOSCMD {
  59.   my $cmdname = shift;
  60.   my $argcmd  = shift;
  61.   my $captured = $cmdname." ".$argcmd;
  62.   Log("* Runing: $captured");
  63.   $captured = qx{$captured};
  64.   if ($? == -1) {
  65.     $cmdname = "* Error!!: ".$cmdname." failed to execute (%s)!\n";
  66.     $Log_file->print(sprintf  "$cmdname", exterr);
  67.     die sprintf "$cmdname", exterr;
  68.   } elsif ($? & 127) {
  69.     $cmdname = "* Error!!: ".$cmdname." died with signal %d!\n";
  70.     $Log_file->print(sprintf  "$cmdname", ($? & 127));
  71.     die sprintf "$cmdname", ($? & 127);
  72.   } elsif ($? != 0 ) {
  73.     $cmdname = "* Error!!: ".$cmdname." exited with error code %d!\n";
  74.     $Log_file->print(sprintf  "$cmdname", $? >> 8);
  75.     die sprintf "$cmdname",$? >> 8;
  76.   } else {
  77.    $Log_file->print($captured);
  78.    print($captured) if $verbose;
  79.   }
  80. }
  81.  
  82. ### Agregamos algunas líneas a nuestro log
  83. Log("Una línea agregada al log");
  84. Log("Otra línea agregada al log");
  85.  
  86. ### Definimos un comando
  87. my $cmd = "ls";
  88.  
  89. say "Ejecutando $cmd";
  90. RUNOSCMD($cmd, "-lh");
  91.  
  92. Log("Otra línea agregada al log");
  93.  
  94. ### Definimos otro comando (no existente)
  95. my $newwcmd = "XX";
  96.  
  97. say "Ejecutando $newwcmd";
  98. RUNOSCMD($newwcmd, "-lh");
  99.  
  100. Log("Esta línea no se agrega porque XX a dado un error");
  101.  
  102. END {
  103.   $Log_file->print("[$StampTime] * Se cierra el archivo $LogFileName");
  104.   close $Log_file;
  105.   ### Movemos el archivo al directorio de trabajo
  106.   if ($debug) {
  107.     move("$tempDir/$scriptname.log", "$workdir/$scriptname.log")
  108.     or die "* Error!!: No puedo mover $scriptname.log";
  109.     }
  110.   say "Sayonara";
  111. }
  112.  
  113. __END__
  114.  
Coloreado en 0.006 segundos, usando GeSHi 1.0.8.4


En él he definido dos funciones Log que sólo agrega líneas al archivo .log y RUNOSCMD que se encarga de ejecutar un comando de sistema capturando la salida y analizando los valores.

Al ejecutar el script con las opciones correctas todo parece ir bien, pero, si se pasa una opción desconocida, --k, por ejemplo, se obtiene:

Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Unknown option: k
No es una opción valida
Can't call method "print" on an undefined value at mylog.pl line 103.
END failed--call queue aborted.
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Y aquí es donde empieza mi problema. Las líneas:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Can't call method "print" on an undefined value at mylog.pl line 103.
END failed--call queue aborted.
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

no deberían aparecer, al pasar --k el script debería salir, asumo que es por el uso de END {, pero, si muevo el código de lugar, uso la opción --debug y ocurre algún error, no obtengo el archivo .log en el directorio de trabajo.

¿Existe una forma mejor de lograr esto?

Saludos.
Última edición por pablgonz el 2020-04-28 08:35 @399, editado 1 vez en total
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Publicidad

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor explorer » 2020-04-28 08:08 @380

En efecto, al hacer el die(), se entra en END.

¿Realmente necesitas la presencia de una subrutina END?

¿No valdría con colocar esas sentencias al final de la ejecución del programa?
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor pablgonz » 2020-04-28 09:56 @455

explorer escribiste:En efecto, al hacer el die(), se entra en END.

¿Realmente necesitas la presencia de una subrutina END?

¿No valdría con colocar esas sentencias al final de la ejecución del programa?


Tienes razón, se me olvido que se pueden tener múltiples END y die() no tiene efecto en estos :(.
Hice algunos ajustes y lo he dejado así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.26;
  3. use Getopt::Long qw(:config bundling_values require_order no_ignore_case);
  4. use FileHandle;
  5. use File::Copy;
  6. use POSIX qw(strftime);
  7. use File::Temp qw(tempdir);
  8. use Cwd;
  9. use autodie;
  10.  
  11. my $verbose  = 0; # verbose
  12. my $debug;        # debug
  13.  
  14. my $result = GetOptions (
  15.     'debug!'         => \$debug,   # debug
  16.     'verbose!'       => \$verbose, # verbose
  17.     ) or die "No es una opción valida\n";
  18.  
  19. ### Mensajes de error extendidos
  20. sub exterr () {
  21.     chomp(my $msg_errno = $!);
  22.     chomp(my $msg_extended_os_error = $^E);
  23.     if ($msg_errno eq $msg_extended_os_error) {
  24.         $msg_errno;
  25.     }
  26.     else {
  27.         "$msg_errno/$msg_extended_os_error";
  28.     }
  29. }
  30.  
  31. ### Nombre del script
  32. chomp(my $scriptname = `basename $0`);
  33. $scriptname =~ s/\.pl$//;
  34.  
  35. ### Directorios y archivos
  36. my $tempDir = tempdir( CLEANUP => 1);
  37. my $workdir = cwd;
  38. my $LogFileName = "$workdir/$scriptname.log";
  39. my $Log_file = FileHandle->new("> $LogFileName");
  40. my $StampTime = strftime ("%y/%m/%d %H:%M:%S", localtime);
  41.  
  42. ### Abrimos
  43. $Log_file->print("[$StampTime] * Se inició $scriptname en $workdir\n");
  44. $Log_file->print("[$StampTime] * Se creo el directorio $tempDir\n");
  45. $Log_file->print("[$StampTime] * Se creo el archivo $tempDir/$scriptname.log\n");
  46.  
  47. ### Log simple
  48. sub Log {
  49.   my $msg = shift;
  50.   my $now  = strftime ("%y/%m/%d %H:%M:%S", localtime);
  51.   $Log_file->print(sprintf  "[%s] %s\n", $now, $msg);
  52.   print "$msg\n" if $verbose;
  53.   return 0
  54. }
  55.  
  56. ### Captura y ejecución de comandos
  57. sub RUNOSCMD {
  58.   my $cmdname = shift;
  59.   my $argcmd  = shift;
  60.   my $captured = $cmdname." ".$argcmd;
  61.   Log("* Runing: $captured");
  62.   $captured = qx{$captured};
  63.   # Guardamos la salida del comando en el archivo .log
  64.   $Log_file->print($captured);
  65.   if ($? == -1) {
  66.     $cmdname = "* Error!!: ".$cmdname." failed to execute (%s)!\n";
  67.     $Log_file->print(sprintf  "$cmdname", exterr);
  68.     die sprintf "$cmdname", exterr;
  69.   } elsif ($? & 127) {
  70.     $cmdname = "* Error!!: ".$cmdname." died with signal %d!\n";
  71.     $Log_file->print(sprintf  "$cmdname", ($? & 127));
  72.     die sprintf "$cmdname", ($? & 127);
  73.   } elsif ($? != 0 ) {
  74.     $cmdname = "* Error!!: ".$cmdname." exited with error code %d!\n";
  75.     $Log_file->print(sprintf  "$cmdname", $? >> 8);
  76.     die sprintf "$cmdname",$? >> 8;
  77.   }
  78.    # Si pasamos --verbose mostramos la salida
  79.    print($captured) if $verbose;
  80. }
  81.  
  82. ### Agregamos algunas líneas a nuestro log
  83. Log("Una línea agregada al log");
  84. Log("Otra línea agregada al log");
  85.  
  86. ### Definimos un comando
  87. my $cmd = "ls";
  88.  
  89. say "Ejecutando $cmd";
  90. RUNOSCMD($cmd, "-lh");
  91.  
  92. Log("Otra línea agregada al log");
  93.  
  94. ### Definimos otro comando (no existente)
  95. my $newwcmd = "XX";
  96.  
  97. say "Ejecutando $newwcmd";
  98. RUNOSCMD($newwcmd, "-R");
  99.  
  100. Log("Esta línea no se agrega porque XX a dado un error");
  101.  
  102. ### Si todo esta correcto
  103. $Log_file->print("[$StampTime] * Se cierra el archivo $scriptname.log");
  104. close $Log_file;
  105.  
  106. ### Si no necesitamos el archivo, lo movemos el archivo al directorio temporal
  107. if (!$debug) {
  108.     move("$workdir/$scriptname.log", "$tempDir/$scriptname.log")
  109.     or die "* Error!!: No puedo mover $scriptname.log";
  110. }
  111.  
  112. __END__
  113.  
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Al ejecutar el script sin opciones genera el archivo .log:
Sintáxis: [ Descargar ] [ Ocultar ]
  1. [20/04/28 10:46:22] * Se inició nuevo en /home/pablo/GitHub 
  2. [20/04/28 10:46:22] * Se creo el directorio /tmp/cX49C_N8TR 
  3. [20/04/28 10:46:22] * Se creo el archivo /tmp/cX49C_N8TR/nuevo.log 
  4. [20/04/28 10:46:22] Una línea agregada al log 
  5. [20/04/28 10:46:22] Otra línea agregada al log 
  6. [20/04/28 10:46:22] * Runing: ls -lh 
  7. total 24K 
  8. drwxrwxr-x. 5 pablo pablo 4,0K abr 13 12:10 demopkg-jw 
  9. drwxrwxr-x. 7 pablo pablo 4,0K may 31 2019 ltxcole 
  10. drwxrwxr-x. 4 pablo pablo 4,0K abr 27 05:56 ltximg 
  11. -rw-rw-r--. 1 pablo pablo 3,1K abr 28 01:44 mylog.pl 
  12. -rw-rw-r--. 1 pablo pablo 320 abr 28 10:46 nuevo.log 
  13. -rw-rw-r--. 1 pablo pablo 3,1K abr 28 10:38 nuevo.pl 
  14. [20/04/28 10:46:22] Otra línea agregada al log 
  15. [20/04/28 10:46:22] * Runing: XX -R 
  16. * Error!!: XX failed to execute (No such file or directory)! 


y la siguiente salida:
Sintáxis: [ Descargar ] [ Ocultar ]
  1. Ejecutando ls 
  2. Ejecutando XX 
  3. * Error!!: XX failed to execute (No such file or directory)! 


Si paso una opción invalida no crea el archivo ni genera errores inesperados, pero no escribe la salida del comando que ha fallado, en mi caso al ejecutar XX -R obtengo la salida

Sintáxis: [ Descargar ] [ Ocultar ]
  1. bash: XX: orden no encontrada 


y me gustaría que ésta quedara escrita en el .log ¿Puedo guardar esta salida?
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor explorer » 2020-04-28 12:22 @557

Mueve las líneas 14 a 17 (tratamiento de GetOptions) después de abrir los archivos log (línea 46).

Luego, en la línea del die(), se puede poner algo así:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
} or do { Log("No es una opción válida"); die "No es una opción válida\n"; }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor pablgonz » 2020-04-28 12:50 @576

Genial, hice los cambios que has propuesto:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.     ) or do { Log("No es una opción válida"); die "No es una opción válida\n"; };
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

pero aún no puedo capturar :(
Sintáxis: [ Descargar ] [ Ocultar ]
  1. bash: XX: orden no encontrada 


¿Me falta alguna instrucción en RUNOSCMD o no se puede capturar esto?
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor explorer » 2020-04-28 14:42 @654

Creo que te refieres a capturar ese mensaje de error.

Ese mensaje sale por el canal STDERR, que no es capturado por qx().

Hay formas, en Perl, para capturar el STDOUT y el STDERR por separado, pero la primera solución sería recibir los errores que salen, en lugar de STDERR, por STDOUT.

El truco sería poner

2>&1

después del comando, para que shell (ejecutado por qx()) redirija un canal a otro. Sería algo así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $captured = "$cmdname $argcmd 2>&1";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Pero si, el número de errores (y advertencias), es muy grande entonces puede ser un lío porque saldrá mezclado con la salida normal del comando. En ese caso es cuando es mejor mirar otras opciones, como puede ser la función tap() del módulo Sysadm::Install (ejemplo).

Usa el sistema de búsqueda en estos foros y busca por STDERR para más ideas de cómo capturar esa salida.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor pablgonz » 2020-04-28 15:09 @673

Creo que lo de capturar errores es muy avanzado para lo que deseo, la solución que propones es la más factible, conozco cuales son los programas y las salidas esperadas. He agregado esto para portabilidad (Windows/Linux) que usaré en la implementación:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use File::Spec::Functions qw(catfile devnull);
  2. my $null     = devnull();
  3. my $captured = "$cmdname $argcmd >$null";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


que creo que es equivalente a 2>&1.

Consulta (no relacionada): ¿Por qué desde mi móvil me dice que la página del foro no es segura?

Agradecido por la respuesta.
Pablo
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor explorer » 2020-04-28 18:43 @821

No, no es equivalente.

Lo que has hecho es mandar todos los mensajes de error al dispositivo /dev/null, es decir, obviarlos.

El navegador te avisa de que la página usa una comunicación no cifrada (protocolo HTTP normal). Eso no significa que la página no sea segura. Es un abuso del lenguaje. Una página es segura si su contenido es seguro. Otra cosa es que se transmita de forma segura, que es lo que tu navegador está advirtiendo.

Desde hace unos meses los navegadores avisan de forma explícita cuando te conectas a páginas que usan el protocolo HTTP en lugar del HTTPS, como una medida de seguridad (se supone que si la página es con protocolo HTTPS, es porque su dueño ha pagado por tener un certificado digital que acredite que él es él) pero el caso es que cualquiera puede hacerse pasar por "sitio legal" comprando un dominio con un certificado digital cualquiera (incluso gratuito).
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor pablgonz » 2020-04-28 19:09 @840

explorer escribiste:No, no es equivalente.

Lo que has hecho es mandar todos los mensajes de error al dispositivo /dev/null, es decir, obviarlos.


Ya entiendo, manejar errores y salidas es más complicado de lo que pensé.

explorer escribiste:Desde hace unos meses los navegadores avisan de forma explícita cuando te conectas a páginas que usan el protocolo HTTP en lugar del HTTPS, como una medida de seguridad (se supone que si la página es con protocolo HTTPS, es porque su dueño ha pagado por tener un certificado digital que acredite que él es él) pero el caso es que cualquiera puede hacerse pasar por "sitio legal" comprando un dominio con un certificado digital cualquiera (incluso gratuito).


Gracias por la aclaración, me había percatado de que por defecto Google me mostraba/enviaba a direcciones HTTPS por sobre las HTTP, pensé que era un problema con mi navegador, es extraño que Google no me envíe directo a https://perlenespanol.com y me envíe a http://perlenespanol.com
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Archivo log simple y captura de errores (sin módulos ext

Notapor explorer » 2020-04-28 20:23 @891

Yo siempre uso HTTP salvo cuando quiero hacer una compra. La transferencia es más rápida que con HTTPS.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado