Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Varios
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 21-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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
Responder Con Cita
  #2  
Antiguo 21-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
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.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....
Responder Con Cita
  #3  
Antiguo 21-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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 :-)
Responder Con Cita
  #4  
Antiguo 22-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
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.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....

Última edición por ecfisa fecha: 22-03-2013 a las 03:31:40.
Responder Con Cita
  #5  
Antiguo 22-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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.
Responder Con Cita
  #6  
Antiguo 22-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
Hola paquechu.


Para leer el archivo de texto podes hacer:
1)
Código Delphi [-]
var
  F  : TextFile;
  str: string;
begin
  AssignFile(F, FNAME);
  Reset(F);
  while not Eof(F) do
  begin
    Readln(F,str);
    // aquí lo que hagas con str
  end;
  CloseFile(F);
end;

2)
Código Delphi [-]
var
 SL: TStrings;
begin
  SL := TStringList.Create;
  try
    SL.LoadFromFile(FNAME);
    // aquí lo que hagas con SL[n]
  finally
    SL.Free
  end
end;

Un ejemplo que devuelve la última línea del archivo log:
Código Delphi [-]
function GetLastLine(const aFileName: string): string;
var
  FS  : TFileStream;
  buf : Char;
  i   : Integer;
begin
  FS := TFileStream.Create(aFileName, fmOpenRead);
  try
    Result := '';
    i := FS.Size + 2; 
    repeat
      FS.Seek(FS.Size-i, soEnd);
      FS.Read(buf, SizeOf(buf));
      Insert(buf, Result, 1);
      Inc(i);
    until buf = #10;
  finally
    FS.Free;
  end;
end;

// llamada ejemplo
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GetLastLine('C:\ARCHIVO_LOG.LOG'))
end;
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.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....
Responder Con Cita
  #7  
Antiguo 22-03-2013
Avatar de mamcx
mamcx mamcx is offline
Moderador
 
Registrado: sep 2004
Ubicación: Medellín - Colombia
Posts: 3.911
Poder: 25
mamcx Tiene un aura espectacularmamcx Tiene un aura espectacularmamcx Tiene un aura espectacular
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.
__________________
El malabarista.
Responder Con Cita
  #8  
Antiguo 22-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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.
Responder Con Cita
  #9  
Antiguo 22-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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,
Responder Con Cita
  #10  
Antiguo 23-03-2013
Avatar de nlsgarcia
[nlsgarcia] nlsgarcia is offline
Miembro Premium
 
Registrado: feb 2007
Ubicación: Caracas, Venezuela
Posts: 2.206
Poder: 21
nlsgarcia Tiene un aura espectacularnlsgarcia Tiene un aura espectacular
paquechu,

Cita:
Empezado por paquechu
...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.....Debo localizar el archivo, abrirlo y empezar a leer desde el final línea a línea...
Revisa este código:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    ListBox1: TListBox;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function GetFileLastLine(const Filename : String) : String;
var
   StrFile : TStringList;
begin
   try
      StrFile := TStringList.Create;
      StrFile.LoadFromFile(Filename);
      Result := StrFile[StrFile.Count-1];
   except
      on E: Exception do
      begin
         MessageDlg(E.Message, mtError, [mbOk],0);
         Result := '';
      end;
   end;
   StrFile.Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   ListBox1.Items.Add(GetFileLastLine('C:\Test_File.txt'));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   Timer1.Interval := 100;
end;

end.
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.

Última edición por nlsgarcia fecha: 23-03-2013 a las 03:26:08.
Responder Con Cita
  #11  
Antiguo 23-03-2013
Avatar de mamcx
mamcx mamcx is offline
Moderador
 
Registrado: sep 2004
Ubicación: Medellín - Colombia
Posts: 3.911
Poder: 25
mamcx Tiene un aura espectacularmamcx Tiene un aura espectacularmamcx Tiene un aura espectacular
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.
__________________
El malabarista.
Responder Con Cita
  #12  
Antiguo 24-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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 :-)
Responder Con Cita
  #13  
Antiguo 24-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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 :-)
Código Delphi [-]

procedure TfrmPrinc.bProcesarClick(Sender: TObject);
var
  vLinea:String;
begin
    bSalir:=False;
    PosUltimaLinea:=0;
    while Not bSalir do
    begin
      vLinea:=GetLastLine(sDirTrab+'\'+sArchLog);
      mLog.lines.add(vLinea);
      // Aqui procesar la línea
    end;
end;

function TfrmPrinc.GetLastLine(const aFileName: string): string;
var
  buf : Char;
  i,j,PosiLinea   : LongInt;

begin
  FSPrin := TFileStream.Create(aFileName, fmOpenRead + fmShareDenyNone );
  try
    bLinea:=False;
    while Not bLinea do
    begin
       // Ir al final de fichero
       i := FSPrin.Size-nCarLi;
       PosUltimaAux:=i+nCarLi;
       if PosUltimaAux<>PosUltimaLinea then
       begin
         try
           PosUltimaLinea:=PosUltimaAux;
           buf:=#0;
           FSPrin.Seek(i, soBeginning);
           FSPrin.Read(buf, SizeOf(buf));
           if (buf = #10) or (buf = #13) then
           begin
             bLinea:=True;
             PosiLinea:=i;
           end;
         except;
         end;
         Application.ProcessMessages;
         if bSalir then begin break; exit; end;
       end;
       Application.ProcessMessages;
       if bSalir then begin break; exit; end;
       Sleep(strtoint(dfRetardo.Text));
    end;
    // Se localiza retorno de carro al final del archivo y se entiende
    // que hay una línea.
    // Hay que leer la linea hacia atras hasta encontrar retorno de carro
    // de la anterior linea o inicio de fichero.

    Result := '';
    Buf:=#0;
    i := 1;
    while (buf <> #10) and (buf <> #13) and (FSPrin.position<>0) do
    begin
      try
        buf:=#0;
        //FSPrin.Seek(PosiLinea-i, soBeginning);
        FSPrin.Position:=PosiLinea-i;
        FSPrin.Read(buf, SizeOf(buf));
        if (buf <> #10) and (buf <> #13) then
           Insert(buf, Result, 1);
        Inc(i);
      except;
      end;
      Application.ProcessMessages;
      if bSalir then exit;
    end;
  finally
    FSPrin.Free;
  end;
end;
Responder Con Cita
  #14  
Antiguo 24-03-2013
Avatar de Julián
Julián Julián is offline
Merodeador
 
Registrado: may 2003
Ubicación: en mi casa
Posts: 2.019
Poder: 10
Julián Va por buen camino
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
__________________
"la única iglesia que ilumina es la que arde"
Anonimo
Responder Con Cita
  #15  
Antiguo 24-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Gracias Julian, lo estudiaré :-)
Saludos.
Responder Con Cita
  #16  
Antiguo 24-03-2013
Avatar de nlsgarcia
[nlsgarcia] nlsgarcia is offline
Miembro Premium
 
Registrado: feb 2007
Ubicación: Caracas, Venezuela
Posts: 2.206
Poder: 21
nlsgarcia Tiene un aura espectacularnlsgarcia Tiene un aura espectacular
paquechu,

Cita:
Empezado por paquechu
...no se el tamaño maximo (no de lineas) sino de memoria que se puede cargar con un tstringlist...
Un String en Delphi 7 (AnsiString, WideString) tiene un tamaño máximo de 2 GB, revisa este link: Limitation of TStringList.

Cita:
Empezado por paquechu
...me preocupa el tiempo de carga y el consumo excesivo de memoria...
EL código propuesto debería funcionar correctamente en archivos de texto que no sean de un tamaño notable (Logs rotativos), el ejemplo mencionado de 2.000.000 de lineas de texto fue solo a efectos de prueba de la función.

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.

Última edición por nlsgarcia fecha: 24-03-2013 a las 23:04:51.
Responder Con Cita
  #17  
Antiguo 25-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
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:
Código Delphi [-]
  ...
  private
    FPreviousSize: Int64; // Ultimo tamaño monitoreado   
  end;

...

implementation

const
   NOMARCH = 'C:\LOG.TXT'; // Ruta hasta y nombre del archivo

(* Obtener tamaño del archivo *)
function FileLongSize(const aFileName: string): Int64;
var
  FindData: TWin32FindData;
begin
  Windows.FindClose(FindFirstFile(PChar(aFileName), FindData));
  Result := FindData.nFileSizeHigh shl 32 + FindData.nFileSizeLow;
end;

(* Obtener última línea *)
function GetLastLine(const aFileName: string): string;
var
  FS  : TFileStream;
  buf : Char;
  i   : Integer;
begin
  FS := TFileStream.Create(aFileName, fmOpenRead);
  try
    Result := '';
    i := FS.Size + 2;
    repeat
      FS.Seek(FS.Size-i, soEnd);
      FS.Read(buf, SizeOf(buf));
      Insert(buf, Result, 1);
      Inc(i);
    until (buf = #10);
  finally
    FS.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Tamaño al inicio del monitoreo
  FPreviousSize:= FileLongSize(NOMARCH); 
  //Timer1.Interval := ??? (a tu necesidad)
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  NewSize: Int64;
begin
  NewSize:= FileLongSize(NOMARCH);  // Nuevo tamaño
  if FPreviousSize <>  NewSize then 
  begin
    FPreviousSize:= NewSize;
    Timer1.Enabled:= False;
    // Aca lo que gustes hacer con la última línea, para el ejemplo la muestro.
    MessageBox(Handle,PChar(GetLastLine(NOMARCH)),'NUEVA LINEA',MB_ICONEXCLAMATION);
    Timer1.Enabled:= True;
  end;
end;
...
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.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....
Responder Con Cita
  #18  
Antiguo 25-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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:
Empezado por nlsgarcia Ver Mensaje
paquechu,


Un String en Delphi 7 (AnsiString, WideString) tiene un tamaño máximo de 2 GB, revisa este link: Limitation of TStringList.


EL código propuesto debería funcionar correctamente en archivos de texto que no sean de un tamaño notable (Logs rotativos), el ejemplo mencionado de 2.000.000 de lineas de texto fue solo a efectos de prueba de la función.

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.
Responder Con Cita
  #19  
Antiguo 25-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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:
Empezado por ecfisa Ver Mensaje
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:

Código Delphi [-]
...
private
FPreviousSize: Int64; // Ultimo tamaño monitoreado
end;

...

implementation

const
NOMARCH = 'C:\LOG.TXT'; // Ruta hasta y nombre del archivo

(* Obtener tamaño del archivo *)
function FileLongSize(const aFileName: string): Int64;
var
FindData: TWin32FindData;
begin
Windows.FindClose(FindFirstFile(PChar(aFileName), FindData));
Result := FindData.nFileSizeHigh shl 32 + FindData.nFileSizeLow;
end;

(* Obtener última línea *)
function GetLastLine(const aFileName: string): string;
var
FS : TFileStream;
buf : Char;
i : Integer;
begin
FS := TFileStream.Create(aFileName, fmOpenRead);
try
Result := '';
i := FS.Size + 2;
repeat
FS.Seek(FS.Size-i, soEnd);
FS.Read(buf, SizeOf(buf));
Insert(buf, Result, 1);
Inc(i);
until (buf = #10);
finally
FS.Free;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
// Tamaño al inicio del monitoreo
FPreviousSize:= FileLongSize(NOMARCH);
//Timer1.Interval := ??? (a tu necesidad)
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
NewSize: Int64;
begin
NewSize:= FileLongSize(NOMARCH); // Nuevo tamaño
if FPreviousSize <> NewSize then
begin
FPreviousSize:= NewSize;
Timer1.Enabled:= False;
// Aca lo que gustes hacer con la última línea, para el ejemplo la muestro.
MessageBox(Handle,PChar(GetLastLine(NOMARCH)),'NUEVA LINEA',MB_ICONEXCLAMATION);
Timer1.Enabled:= True;
end;
end;
...




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.
Responder Con Cita
  #20  
Antiguo 25-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
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
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 09:47:27.


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
Copyright 1996-2007 Club Delphi