• Publicidad

Parseo de log dansguardian

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

Parseo de log dansguardian

Notapor Fernando » 2011-04-16 08:29 @395

¿Qué tal, gente? Es mi primer post en el foro. Soy nuevo en el mundo de Perl.

Estoy renegando con un script en Perl desde hace unos días ya. Es por eso que les pido su ayuda. El problema es el siguiente:

Tengo el log de dansguardian (filtro de contenidos) con el siguiente formato:

Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
"2011.4.14 6:15:01","-","192.168.203.1","http://www.google.com","","GET",""
"2011.4.14 6:15:01","-","192.168.203.5","http://www.otro.com","*DENIED","GET",""
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Lo que necesito es dividirlo en campos según "", para que quede algo así (tomo de ejemplo la primer línea):
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
2011.4.14 6:15:01
-
192.168.203.1
http://www.google.com
(espacio en blanco)
GET
(espacio en blanco)
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


El problema que tengo es que no logro hacer que me reconozca como campo los que se encuentran vacíos (los que figuran en el código de arriba como 'espacio en blanco'), es decir, los que tiene este formato: ,"" .

Como luego paso todo esto a una base de datos MySQL, necesito poder separar correctamente cada campo, ya que dependiendo de la consulta esos campos pueden estar vacíos o llenos (ver línea 2 de log dansguardian: ,"*DENIED", ).

Muchas gracias por su ayuda.

Saludos./

Fernando.-
Fernando
Perlero nuevo
Perlero nuevo
 
Mensajes: 15
Registrado: 2011-04-16 08:10 @382

Publicidad

Re: Parseo de log dansguardian

Notapor explorer » 2011-04-16 09:23 @433

Bienvenido a los foros de Perl en español, Fernando.

Si supieras que, dentro de los campos, nunca va a salir el carácter coma (,), entonces podrías solventarlo de forma muy sencilla con la función split():

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $linea = q("2011.4.14 6:15:01","-","192.168.203.1","http://www.google.com","","GET","");
  2.  
  3. my ($date, $auth, $ip, $url, $field1, $method, $field2) = split /,/, $linea;
Coloreado en 0.003 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: Parseo de log dansguardian

Notapor Fernando » 2011-04-16 09:29 @436

¡Hola!

Antes que nada, muchas gracias por la pronta respuesta.

Entiendo lo que me decís. El tema es que, efectivamente, pueden existir comas en el texto; por ejemplo, corto una parte del mensaje:

Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
[..],"*DENIED* Weighted phrase limit of 50 : 160 ((anonymous, proxy)+(proxy, anonymous)+(proxy, block)+(proxy, filter)+(proxy, anonymous)+ proxy+-test +-wiki)","GET",[...]
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Es decir, pueden existir comas y espacios. Lo que sí, siempre un campo se encuentra encerrado entre "" (comillas), y separado por , (comas).

¡¡Muchas gracias!!
Fernando
Perlero nuevo
Perlero nuevo
 
Mensajes: 15
Registrado: 2011-04-16 08:10 @382

Re: Parseo de log dansguardian

Notapor explorer » 2011-04-16 09:51 @452

Bueno, pues si se cumple la regla de que TODOS los campos están entrecomillados dobles, se puede usar una expresión regular:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use diagnostics;
  5.  
  6. my @lineas = (
  7.     q("2011.4.14 6:15:01","-","192.168.203.1","http://www.google.com","","GET","")     ,
  8.     q("2011.4.14 6:15:01","-","192.168.203.5","http://www.otro.com","*DENIED","GET",""),
  9. );
  10.  
  11. for my $linea (@lineas) {
  12.     my @campos = $linea =~ m/"(.*?)"/g;    
  13.     print join('|', @campos), "\n";
  14. }
Coloreado en 0.002 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: Parseo de log dansguardian

Notapor Fernando » 2011-04-16 14:24 @641

¡¡Muchas gracias!!

¡Era justo lo que necesitaba! ¡Te agradezco la buena onda!

Saludos./
Fernando
Perlero nuevo
Perlero nuevo
 
Mensajes: 15
Registrado: 2011-04-16 08:10 @382

Leer parte de un archivo de log

Notapor Fernando » 2011-04-19 09:05 @420

¿Qué tal, gente?

Les cuento el problema que tengo:

Armé un script en Perl que con la ayuda de uds., que procesa un archivo de LOG (de dansguardian) y va escribiendo en una base de datos. Lo hace en tiempo real. FIFO

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. [...]
  2. open( FIFO, "< /var/log/dansguardian/access.log" )
  3.     or die "Error abriendo log FIFO: $!\n";
  4. LOG: while (1) {
  5.     my $message = <FIFO>;
  6.     next LOG unless defined $message;
  7.     my ($dt,  $ident, $ip,   $url,  $resul, $met,    $size, $wei,
  8.         $cat, $fg,    $code, $mime, $cl,    $fgname, $bw
  9.     ) = $message =~ m/"(.*?)"/g;
  10.     my @dt = split( /\s+/, $dt );
  11.     $dt[0] =~ s/\./\-/g;
  12.     my $d = $dt[0];
  13.     my $t = $dt[1];
  14.     my ( $proto, $dom, $uri, $query, $frag ) = uri_split($url);
  15.     $sth->execute( $d, $t, $ip, $dom, $url, $resul, $met, $size, $wei, $fg,
  16.         $code );
  17.     usleep(1000);
  18. }
  19.  
  20. [...]
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4



Hace lo que quiero que haga. Pero el tema es que tengo permanentemente el micro al 99%.

Lo estuve investigando un poco y muchos tienen el mismo problema con este tipo de scripts. Entonces, lo que quiero hacer es otro script, que en lugar de pasar la info a la DB en tiempo real, lo haga cada 1 o 2 minutos (proceso en cron).

El tema es que el archivo puede pesar bastante, y yo necesito ir pasando las diferencias, es decir las últimas líneas, con lo cual debería leer una marca (supongamos MARK1 que puede ser una fecha:hora) y pasar a la base de datos desde ahí hasta el final. Luego vuelvo a poner la marca en el final (o guardo algún dato relevante de la última línea), y cuando se vuelva a ejecutar el script se repetiría el proceso.

Pensé en guardar esa marca en la misma base de datos, pero es irrelevante para el problema en cuestión.

Estuve leyendo un poco cómo hacerlo, pero mi duda es cómo puedo hacer para guardar en memoria (o archivo) para trabajar en el pasaje a la base de datos solamente la parte que necesito, y no todo el archivo.

Y otra duda que tengo es cómo almacenar algún dato (MARK1 o fecha:hora) de la última línea leída para utilizar como marca la próxima vez que se ejecute el script. Probé con un while(), pero las variables del while() no me las toma fuera de éste.

Bueno, muchas gracias de antemano.
Última edición por explorer el 2011-04-19 09:28 @436, editado 2 veces en total
Razón: Marcas Perl, formatear código con Perltidy
Fernando
Perlero nuevo
Perlero nuevo
 
Mensajes: 15
Registrado: 2011-04-16 08:10 @382

Re: Leer parte de un archivo de log

Notapor explorer » 2011-04-19 10:21 @473

Fernando escribiste:Armé un script en Perl que con la ayuda de uds., que procesa un archivo de LOG (de dansguardian) y va escribiendo en una base de datos. Lo hace en tiempo real. FIFO

Hace lo que quiero que haga. Pero el tema es que tengo permanentemente el micro al 99%.
El problema es que has creado un bucle sin fin, en donde no indicas en ningún sitio (salvo con usleep()) que el procesador puede tomarse un descanso hasta que haya nuevos datos. En cambio, el procesador está volcado en tu tarea, continuamente.

Fernando escribiste:Lo estuve investigando un poco y muchos tienen el mismo problema con este tipo de scripts. Entonces, lo que quiero hacer es otro script, que en lugar de pasar la info a la DB en tiempo real, lo haga cada 1 o 2 minutos (proceso en cron).

El tema es que el archivo puede pesar bastante, y yo necesito ir pasando las diferencias, es decir las últimas líneas, con lo cual debería leer una marca (supongamos MARK1 que puede ser una fecha:hora) y pasar a la base de datos desde ahí hasta el final. Luego vuelvo a poner la marca en el final (o guardo algún dato relevante de la última línea), y cuando se vuelva a ejecutar el script se repetiría el proceso.

Pensé en guardar esa marca en la misma base de datos, pero es irrelevante para el problema en cuestión.

Estuve leyendo un poco cómo hacerlo, pero mi duda es cómo puedo hacer para guardar en memoria (o archivo) para trabajar en el pasaje a la base de datos solamente la parte que necesito, y no todo el archivo.

Y otra duda que tengo es cómo almacenar algún dato (MARK1 o fecha:hora) de la última línea leída para utilizar como marca la próxima vez que se ejecute el script. Probé con un while(), pero las variables del while() no me las toma fuera de éste.
Bueno, sí, esa es una forma de hacerlo, pero la "marca" que normalmente se usa en registros que van creciendo es ir guardando el tamaño del fichero en ese momento. Ejemplo:

  • Entramos en el bucle
  • Leemos la última posición leída en el registro access.log
  • Mirar a ver si esa última posición es menor, mayor o igual que el actual tamaño del registro
  • Si son iguales, terminamos el bucle (no hubo cambios)
  • Si la última posición leída es mayor que el tamaño del registro, es posible que el registro haya sido inicializado (el servicio del que depende ha sido reinicializado, por ejemplo, o se ha producido una rotación de ficheros de registro). En ese caso, ponemos el valor de posición a 0 y comenzamos a procesar el registro desde el principio
  • Sino, es que la última posición leída es menor que el tamaño del fichero. Entonces, abrimos el fichero con open(), nos posicionamos con seek() en la última posición leída, y procesamos el resto del registro
  • Una vez terminado de procesar, solo tenemos que guardar la actual posición de lectura del fichero de registro para la siguiente vuelta. Ese dato lo podemos saber con la función tell().
  • Repetir bucle

Quedaría pendiente de resolver el caso de qué hacer si se detecta un cambio por rotación de ficheros de registro, y si se debe, o no, procesar las últimas líneas del registro anterior. Por ejemplo, los registros de actividad de Apache, pasan de llamarse access.log a access.log.0, a las 06:30 del domingo. Pueden quedarse algunas líneas sin procesar en el access.log.0. Dependerá de lo vital que sea la información, tendremos que procesarlas o no.

Hay otra forma de resolver esto...

Para estas situaciones en las cuales hay que estar pendiente de la actividad de un registro que va creciendo y/o que puede ir rotando con el tiempo, uso el módulo File::Tail, que se ocupa de hacer todo lo que hemos comentado antes, y de forma muy cómoda para nosotros.

Al principio del programa hago un listado de los registros que voy a monitorizar:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my @ficheros_a_vigilar = qw(
  2.     /var/log/exim4/mainlog
  3.     /var/log/mail/mail.log
  4.     /var/log/syslog
  5. );
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

luego, creo tantos objetos File::Tail como ficheros tengo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my @ft;
  2. for my $fichero (@ficheros_a_vigilar) {
  3.     push @ft , File::Tail->new(name => $fichero, debug => $debug);
  4. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

entro en el bucle, infinito, naturalmente, y lo primero que hago es llamar a File::Tail para que vigile la actividad de todos los ficheros:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. ...;
  2. while ("forever") {                                               # bucle infinito
  3.     my ($nfound,$timeleft,@pending)                               # File::Tail esperará $timeout segundos por la
  4.         = File::Tail::select(undef, undef, undef, $timeout, @ft); # actividad en los ficheros @ft, guardándolos
  5.                                                                   # en @pending. $nfound indicará cuántos son, y
  6.                                                                   # $timeleft indica cuántos segundos quedaban
  7.                                                                   # para el siguiente $timeout
  8.  
  9.     unless ($nfound) {                                            # si no hubo actividad
  10.         say "PING" if $debug;                                     # ocurrió un timeout                
  11.     }
  12.     else {                                                        # si sí hubo actividad
  13.         for (@pending) {                                          # para todos los ficheros con cambios
  14.             my $log   = $_->{input};                              # nombre del fichero con cambios
  15.             my $linea = $_->read;                                 # nueva línea del fichero
  16.  
  17.             given ($log) {
  18.                 when (/mainlog$/) {                               # si el fichero es mainlog
  19.                     if ($linea =~ / \[([\d.]+?)\] /o) {           # si la línea contiene una '[IP]'
  20.                         $ip = $1;                                 # nos quedamos con ella
  21.                         ...;
  22.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Y ya está. No tenemos que preocuparnos de mirar en qué posición está, cada cuánto hay que mirar el registro, etc. Como ves en el código, solo me preocupo de preguntar qué fichero ha cambiado, y a continuación extraigo una línea para procesarla. (El par given/when y el say son de Perl v5.10 o superior. El operador ... es de Perl v5.12 o superior.)

File::Tail ajustará los tiempos de lectura según el fichero vaya cambiando: cuantos más cambios, con más frecuencia mirará por más cambios. También, por defecto cada 60 segundos, mirará a ver si el fichero ha sido puesto a cero, y en ese caso, lo volverá a abrir como si empezara de nuevo.

Esta es una forma de usar el módulo. Otra sería usar read() en lugar de select(). El programa quedaría bloqueado hasta que, efectivamente, hubiera una nueva.
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: Parseo de log dansguardian

Notapor Fernando » 2011-04-19 14:07 @629

Buenas..

Primero quiero agradecerte la respuesta. Excelente. Muy buena explicación.

Me queda una duda al respecto:

- el script va a funcionar perfecto siempre y cuando se mantenga en ejecución. ¿Qué pasaría si genera un error (X, no importa, cualquiera) y el script sale? El/los log seguiría creciendo y al volver a ejecutar el script las líneas pasadas se perderían, ¿no?

Sé que se podría solucionar armando otro script que cuide que el primero esté siempre en ejecución. Mi comentario viene más a tu experiencia: ¿cómo se suele solucionar este tipo de cuestiones?

¡Muchas gracias desde ya!
Fernando
Perlero nuevo
Perlero nuevo
 
Mensajes: 15
Registrado: 2011-04-16 08:10 @382

Re: Parseo de log dansguardian

Notapor explorer » 2011-04-19 16:34 @732

Fernando escribiste:el script va a funcionar perfecto siempre y cuando se mantenga en ejecución. ¿Qué pasaría si genera un error (X, no importa, cualquiera) y el script sale? El/los log seguiría creciendo y al volver a ejecutar el script las líneas pasadas se perderían, ¿no?
Sí, así es, pero, yo, por ejemplo, tengo un proceso así funcionando desde el pasado 25 de febrero; y podía ser más tiempo si no lo hubiera parado manualmente :) Estando en Linux, no tengo miedo a que pase nada raro.

Es claro que depende del nivel de seguridad que quieras tener. Como mis exigencias son pequeñas, no me preocupa que el proceso se pare.

En tu caso, si necesitas un sistema muy robusto, debes prever todas las circunstancias.

Fernando escribiste:Sé que se podría solucionar armando otro script que cuide que el primero esté siempre en ejecución.
Y además, se debería ocupar de arrancarle de nuevo, pasándole como argumento el cálculo de la última línea leída, de tal manera que, antes de entrar en el bucle sin fin, procese las líneas pendientes. Incluso la posición puede ser almacenada por el propio bucle, de tal manera que el programa lanzador/monitor no tiene más preocupación que tenerlo siempre arrancado.

Existen programas que hacen este tipo de tareas de monitorización, que pueden relanzar servicios, en caso de fallo. En alguna ocasión los usé, pero cuando tienes una máquina bien afinada, con más de 700 días de funcionamiento normal, las posibilidades de caída son muy bajas.
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 28 invitados