![]() |
Tail -f en delphi
Buenas,
Aunque llevaba bastante tiempo sin programar ya en delphi, mi jefe me ha pedido que me ponga el mono de trabajo de nuevo :-) Estoy un poco pegao con lo que tengo que hacer y es por lo que busco vuestra ayuda. Se trata de lo siguiente: Debo hacer un programa que inspeccione un archivo de log abierto por otra aplicación y al que ésta última le esta añadiendo líneas contínuamente (hay rotación de logs diaria). Por cada línea debo realizar un análisis por temas de seguridad. No se muy bien por donde empezar. Me podeis echar una mano por favor? Muchas gracias |
Hola paquechu.
Si tenes acceso al código fuente de la aplicación que genera el archivo (log), podes enviar la última línea generada mediante la estructura COPYDATASTRUCT y luego capturar el mensaje WM_COPYDATA desde la aplicación que monitorea. En estos enlaces tenes algunos ejemplos:
Saludos. :) |
Hola ecfisa,
Pues lo cierto es que no tengo acceso al codigo de la aplicación. Se trata de logs generados en formato texto por un servidor ISA Server de Microsoft. Debo localizar el archivo, abrirlo y empezar a leer desde el final línea a línea... Saludos y gracias :-) |
Hola paquechu.
Entonces, en principio depende de con que "sharing options" haya abierto el archivo la otra aplicacion. ¿ Ya intentaste abrirlo para lectura, estando activa la aplicación generadora y te mostró algún error ? Saludos. :) |
Hola de nuevo,
Pues no, la verdad es que no se muy bien como enfocarlo, más que por el modo en el que está abierto el archivo por como leer el archivo que se está alimentando constantemente por otra aplicación y gestionar esas entradas en vivo. Saludos. |
Hola paquechu.
Para leer el archivo de texto podes hacer: 1)
2)
Un ejemplo que devuelve la última línea del archivo log: De este modo es mucho mas rápido que leer todo el archivo si te decidis por obtener datos con, por ejemplo, un TTimer. Pero el último código es sólo una aproximación, con muchos supuestos, podría funcionar o no dependiendo del formato con que guarda las líneas la aplicación. Sería muy útil si pudieras poner textualmente (copiar/pegar) un trozo del texto que se genera en el archivo log. Ahora si lo que estas buscando es obtener la última línea en tiempo real y la aplicación generadora no envía ningún mensaje cuando terminó de guardar una línea, no conozco como lograrlo. Espero que alguna opción te sea útil. Saludos.:) |
He echado una mirada en como se hace en python:
http://code.activestate.com/recipes/...l-f-in-python/ y veo que es necesario ademas usar sleep (muy corto) para poder leer de forma continua. |
Hola ecfisa,
Si, conocia los metodos señalados en los puntos 1 y 2. Mi consulta va mas en la línea de lo que indicas en el punto 3, en la parte de identificar cuando se genera una nueva línea en el archivo (en tiempo real) y entonces leer la linea completa y procesarla. Es un buen comienzo el que me indicas asi que empezaré por ahí :-) Gracias ecfisa. |
Hola mamcx,
tomo nota del retardo necesario aunque de phyton no tengo ni papa, je,je Al final de la pagina que referencias aparece este codigo, si controlas delphy y phyton podrias orientarme en la adaptación del código?: Muchas gracias. def tail_f(file): interval = 1.0 while True: where = file.tell() line = file.readline() if not line: time.sleep(interval) file.seek(where) else: yield line Which then allows you to write code like: for line in tail_f(open(sys.argv[1])): print line, |
paquechu,
Cita:
El código anterior lee de forma continua por medio de un control TTimer cada 100 ms la última línea de un archivo de texto y la adiciona a un control TListBox para su posterior procesamiento. Nota: Este código fue probado con un archivo de texto de 2.000.000 de líneas de forma satisfactoria. Espero sea útil :) Nelson. |
Seria bueno almacenar cuantas lineas habian en la lectura anterior y luego restarlas, porque un log puede recibir varias lineas nuevas mientras esta el timer esperando.
|
Vaya!, perdonad, estaba enfrascado con el tema de marras todo este tiempo....
Le he echado un vistazo al código. Dices que puedes cargar 2.000.000 de lineas en el tstringlist, aunque no se el tamaño maximo (no de lineas) sino de memoria que se puede cargar con un tstringlist. Lo voy a probar, me preocupa el tiempo de carga y el consumo excesivo de memoria. Muchas gracias :-) |
Hola de nuevo,
Os pego el trozo de codigo (aparecen variables declaradas globalmente) en el que estoy trabajando que de momento parece que me funciona aunque seguro que se puede mejorar/optimizar, pero es por donde voy :-)
|
Para eso lo mejor puede ser usar el api de windows, que permite colgar un hook o como se llame que "avisa" cada vez que se modifique el archivo que quieras. No me acuerdo que función es, pero existe.
EDITO: Una busqueda en google por "delphi file notification" devielve un motón de entradas parecidas a esta: http://www.delphi-central.com/compon...ification.aspx |
Gracias Julian, lo estudiaré :-)
Saludos. |
paquechu,
Cita:
Cita:
Te sugiero revisar detalladamente el código del Msg #6 (function GetLastLine), el código es altamente eficiente dado que solo obtiene la última línea del archivo de forma directa, lo cual la hace ideal como parte de un control TTimer y sin sobrecarga de recursos. Como comentario, funciono de forma optima con el archivo de pruebas mencionado. Espero sea útil :) Nelson. |
Hola paquechu.
Dado que la aplicación que genera el log añade líneas, este cambiará su tamaño. Entonces, una forma de detectar si se agregó una linea usando un TTimer, podría ser: No sé si es la forma mas optima, pero es simple y en mis pruebas funciona. Hasta que encuentres algo mejor tal vez te sirva... Saludos. |
Hola nlsgarcia,
Gracias por las aclaraciones, respecto al codigo del mensaje numero 6 (GetLastLine), efectívamente, es el que he tomado como base y he logrado adaptar a la idea que iba buscando. Muchas gracias :-) Cita:
|
Hola Ecfisa,
COmo comentaba en mi anterior mensaje, el código que he utilizado se basa en tu funcion GetLastLine. Ya veo que lo has mejorado. Lo voy a probar (aunque la adaptación que os puse también me funciona). Un saludo y gracias. Cita:
|
Por mi parte, me doy más que satisfecho con la acogida y ayuda que me habeis prestado y doy por cerrado este asunto.
Estaba un poco agobiadete con este tema. Muchisimas gracias a todos :) Un cordial saludo |
Lo mas optimo se supone que sería usar el gestlastline que estais comentando, pero en lugar de con un ttimer, con el evento del api de windows (el que te comenté en mi mensaje anterior) que se lanza sólo cuando se modifica el fichero.
Un saludo! |
Hola Julian, coincido contigo totalmente.
Solo me queda una duda, que es donde falla el procedimiento que he adaptado basado en la funcion getlastline, y es la forma en la que se está generando el archivo de log. Si al generar las líneas del archivo de log se hace un flush por cada línea generada, funciona OK, pero si el flush se hace cada ciertas líneas entonces no funciona bien. No se si este componente contemplaria este caso... Saludos. Cita:
|
Cita:
Pues yo entiendo que son dos cosas distintas: 1. Saber cuando determinado archivo *.log ha sido modificado. 2. leer la ultima(s) líneas Para 1 puedes usarse un timer o el api de windows, y creo que hemos quedadi que lo mas elegante y correcto es el api de win. Para 2 pues puedes echar mano de cualquier funcion de leer archivos que tenga el delphi, tal como indican algunos ejemplos que nos han puesto en este mismo hilo. Lo que puedes hacer es poner aquí el cóodigo que te da error a ver que tal, pues con eso del flush no entiendo muy bien que es lo que te falla. Un saludo! |
Hola Julián,
Correcto, son dos cosas. En cuanto a la primera mi duda es saber si tanto el timer como la api de windows se dan por enterados de que un archivo determinado se ha modificado o no desde que se lanza una instrucción write o solo se enteran en el momento en que se hace una instrucción flush del bufer a volcar a disco. (Esto tengo pendiente de probar en cuanto pueda). En lo referente al segundo punto, pues no hay ninguna duda todo está claro. En el mensaje #13 puse el codigo que estoy utilizadno, que no es que me de un error, sino que simplemente no funciona bien cuando desde el momento en que se hace un write para escribir el log si no se vuelca inmediatamente la línea a disco (con un flush) el código no se entera de que se ha enviado mas datos al archivo de log. Aqui te pongo un enlace a la función flush a ver si se entiende mejor lo que quiero comentar :-) : http://www.delphibasics.co.uk/RTL.asp?Name=Flush Cita:
|
Cita:
http://www.delphibasics.co.uk/RTL.asp?Name=Flush Lee la descripcion de la ayuda. Medita sobre lo que dice. Alcanza la iluminación. |
Cita:
En realidad no mejoré la función, es la misma del mensaje #6 :), sólo la integré a un TTimer para darle funcionalidad. ¿ Probaste el código del mensaje #17 y no te resultó ? Funcionó correctamente en todas mis pruebas, obteniendo en mi equipo, promedios de aprox. 850 microsegundos para conseguir la última de 100000 líneas. Te adjunto el ejemplo debajo; la primera ejecución demorará un poco ya que creará un archivo con 100000 líneas de texto aleatorio en la carpeta donde lo ejecutes. En cuanto a capturar la escritura en tiempo real, Delphi posee el componente TShellChangeNotifier que informa sobre las modificaciones sucedidas en determinada carpeta y de forma análoga podes obtenerlas usando API mediante la funcion SHChangeNotifyRegister. Pero de ambos modos se notifica sobre un cambio del archivo como tál (creacion, borrado, etc) y no sobre una alteración en los datos internos del mismo. No quiero decir con esto que no es posible conseguir la información, sino que yo no pude encontrar el modo todavía. Julián ya te puso un enlace y la sugerencia de utilizar un hook. Tal vez tengas que encarar ese punto por ese lado. Saludos. :) |
¡Eso que dice ecfisa es lo que yo decía: un hook!
Es que ya no me acordaba de esas cosas, pues hace como 8 años que no uso el puto Windows, :D :D :D |
Buenas,
He probado el código que proponias, sin embargo al ejecutarlo sobre un archivo abierto de log se "queja" de que está abierto por otra aplicación y no puede continuar..... seguiré probando el resto de vuestras propuestas :-) Saludos. Cita:
|
Cita:
Muy probablemente tengas ese problema con cualquiera de las sugerencias de este hilo, eso es a lo que me refería en el mensaje (#4). Saludos.:) |
Si, eso es, pero se soluciona añadiendo el flag fmShareDenyNone en el momento de abrir el archivo
Saludos. :-) Cita:
|
Ecfisa,
Por otro lado he probado el codigo que pones en el mensaje #17, pero tampoco contempla todas las líneas añadidas desde la última modificación y se "traga" algunas... sigo pensando que es lo del flush, je,je Gracias :-) EDITO: Otra cosa mas.... he intentado descargarme el ejemplo del mensaje #26 y no puedo, no se si será por el control de contenido de mi trabajo, pero al abrir el zip se queja de error crc (lo he intentado varias veces. lo probaré desde casa).... |
Cita:
Saludos.:) |
Cita:
Saludos.:) |
1 Archivos Adjunto(s)
Acabo de hacer pruebas con el "notificador" que tiene la api de windows y creo que tampoco soluciona el problema de los cambios sufridos por un archivo en tiempo real.
He encontrado un archivo donde figura el componente y un ejemplo de uso que he probado de la siguiente forma: En mi PC: he ejecutado el programa de demo que incluye el archivo que os dejo en el post y cuando hay una modificacion en un archivo del directorio que se elija, el cambio de tamaño de archivo se refleja bien, pero cuando hago lo mismo en el servidor donde se genera el log del isa server (que es un servidor virtual) ya no parece enterarse de los cambios. NO se, creo que voy a investigar mas en la linea de guardar la posicion de la ultima línea leida y la próxima vez que el codigo detecte modificación leer a partir de esa posición..... Saludos. :-) |
paquechu,
Cita:
El código anterior permite detectar cambios a nivel de Creación, Modificación y Eliminación de Archivos y Directorios dentro de un Directorio específico (Directorio objetivo de Monitoreo) y sus subdirectorios por medio de la función ReadDirectoryChangesW. Este código fue probado en tres Máquinas Virtuales con Windows XP Professional x32, Windows 7 Professional x32 y x64 y una Máquina Física con Windows 7 Professional x32 y en todos los casos funcionó correctamente. El código esta disponible en el siguiente link: http://terawiki.clubdelphi.com/Delph...oryMonitor.rar Nota: Una forma simple de probar el programa es monitorear el directorio C:\ Revisa estos links: Cita:
Nelson. |
paquechu,
Cita:
Cita:
Cita:
El código anterior permite monitorear un archivo específico (Función ReadDirectoryChangesW) y por medio de un archivo de control determinar las líneas que se han adicionado durante el monitoreo. El código es una especialización de lo sugerido en los Msg #6 y #35. Nota: El código anterior asume el monitoreo de archivos de texto en el cual las lineas finalizan con los caracteres de control CRLF. Solo se puede monitorear un archivo a la vez en esta versión la cual permite: 1- Iniciar el Hilo de Monitoreo a un Archivo Específico. 2- Finalizar el Hilo de Monitoreo a un Archivo Específico. 3- Salvar los cambios registrados en el Archivo Monitoreado a un archivo de texto. 4- Solo se monitorean las líneas adicionadas. 5- El archivo de control es creado cada vez que se selecciona un archivo específico para monitoreo. El código esta disponible en el siguiente link: http://terawiki.clubdelphi.com/Delph...ileMonitor.rar Espero sea útil :) Nelson. |
paquechu,
Nota: La función GetFileLastLine del código del Msg #36 se modifico para adaptarse a posibles operaciones de Delete en el archivo monitoreado por medio de la Actualización del Valor de Control (Tamaño del archivo monitoreado), como se muestra a continuación: Esta modificación solo ajusta el valor de control en el caso de que el archivo monitoreado disminuya su tamaño, no se registra el cambio en el control TListBox. El nuevo código modificado esta disponible en el link: http://terawiki.clubdelphi.com/Delph...i%F3n+2%29.rar Espero sea útil :) Nelson. |
Hola nlsgarcia,
He probado el código y funciona a la perfección.... lee las líneas correctamente, se añadan una a una o varias de golpe. Gran curro :-) Muchisimas gracias por vuestra gran ayuda :-) Un abrazo. |
La franja horaria es GMT +2. Ahora son las 05:11:54. |
Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi