Pregunta

Estoy utilizando Delphi 2007 y tener una aplicación que lee archivos de registro desde varios lugares a través de una red y la pantalla interna excepciones. Esos directorios veces contiene miles de archivos de registro. La aplicación tiene la opción de leer solamente archivos de registro de los últimos n días bit también puede estar en cualquier rango de fecha y hora.

El problema es que la primera vez que se lee el directorio de registro puede ser muy lenta (varios minutos). La segunda vez que es considerablemente más rápido.

Me pregunto cómo puedo optimizar mi código para leer los archivos de registro lo más rápido posible? Estoy usando un vCurrentFile: TStringList para almacenar el archivo en la memoria. Que se actualiza desde un FileStream como creo que esto es más rápido.

Aquí hay un código:

Actualizar : El bucle principal para leer archivos de registro

// In this function the logfiles are searched for exceptions. If a exception is found it is stored in a object.
// The exceptions are then shown in the grid
procedure TfrmMain.Refresh;
var
  FileData : TSearchRec;  // Used for the file searching. Contains data of the file
  vPos, i, PathIndex : Integer;
  vCurrentFile: TStringList;
  vDate: TDateTime;
  vFileStream: TFileStream;
begin
  tvMain.DataController.RecordCount := 0;
  vCurrentFile := TStringList.Create;
  memCallStack.Clear;

  try
    for PathIndex := 0 to fPathList.Count - 1 do                      // Loop 0. This loops until all directories are searched through
    begin
      if (FindFirst (fPathList[PathIndex] + '\*.log', faAnyFile, FileData) = 0) then
      repeat                                                      // Loop 1. This loops while there are .log files in Folder (CurrentPath)
        vDate := FileDateToDateTime(FileData.Time);

        if chkLogNames.Items[PathIndex].Checked and FileDateInInterval(vDate) then
        begin
          tvMain.BeginUpdate;       // To speed up the grid - delays the guichange until EndUpdate

          fPathPlusFile := fPathList[PathIndex] + '\' + FileData.Name;
          vFileStream := TFileStream.Create(fPathPlusFile, fmShareDenyNone);
          vCurrentFile.LoadFromStream(vFileStream);

          fUser := FindDataInRow(vCurrentFile[0], 'User');          // FindData Returns the string after 'User' until ' '
          fComputer := FindDataInRow(vCurrentFile[0], 'Computer');  // FindData Returns the string after 'Computer' until ' '

          Application.ProcessMessages;                  // Give some priority to the User Interface

          if not CancelForm.IsCanceled then
          begin
            if rdException.Checked then
              for i := 0 to vCurrentFile.Count - 1 do
              begin
                vPos := AnsiPos(MainExceptionToFind, vCurrentFile[i]);
                if vPos > 0 then
                  UpdateView(vCurrentFile[i], i, MainException);

                vPos := AnsiPos(ExceptionHintToFind, vCurrentFile[i]);
                if vPos > 0 then
                  UpdateView(vCurrentFile[i], i, HintException);
              end
            else if rdOtherText.Checked then
              for i := 0 to vCurrentFile.Count - 1 do
              begin
                vPos := AnsiPos(txtTextToSearch.Text, vCurrentFile[i]);
                if vPos > 0 then
                  UpdateView(vCurrentFile[i], i, TextSearch)
              end
          end;

          vFileStream.Destroy;
          tvMain.EndUpdate;     // Now the Gui can be updated
        end;
      until(FindNext(FileData) <> 0) or (CancelForm.IsCanceled);     // End Loop 1
    end;                                                          // End Loop 0
  finally
    FreeAndNil(vCurrentFile);
  end;
end;

Método UpdateView : Añadir una fila a la displaygrid

{: Update the grid with one exception}
procedure TfrmMain.UpdateView(aLine: string; const aIndex, aType: Integer);
var
  vExceptionText: String;
  vDate: TDateTime;
begin
  if ExceptionDateInInterval(aLine, vDate) then     // Parse the date from the beginning of date
  begin
    if aType = MainException then
      vExceptionText := 'Exception'
    else if aType = HintException then
      vExceptionText := 'Exception Hint'
    else if aType = TextSearch then
      vExceptionText := 'Text Search';

    SetRow(aIndex, vDate, ExtractFilePath(fPathPlusFile), ExtractFileName(fPathPlusFile), fUser, fComputer, aLine, vExceptionText);
  end;
end;

Método para decidir si está en fila daterange:

{: This compare exact exception time against the filters
@desc 2 cases:    1. Last n days
                  2. From - to range}
function TfrmMain.ExceptionDateInInterval(var aTestLine: String; out aDateTime: TDateTime): Boolean;
var
  vtmpDate, vTmpTime: String;
  vDate, vTime: TDateTime;
  vIndex: Integer;
begin
  aDateTime := 0;
  vtmpDate := Copy(aTestLine, 0, 8);
  vTmpTime := Copy(aTestLine, 10, 9);

  Insert(DateSeparator, vtmpDate, 5);
  Insert(DateSeparator, vtmpDate, 8);

  if TryStrToDate(vtmpDate, vDate, fFormatSetting) and TryStrToTime(vTmpTime, vTime) then
    aDateTime := vDate + vTime;

  Result := (rdLatest.Checked and (aDateTime >= (Now - spnDateLast.Value))) or
            (rdInterval.Checked and (aDateTime>= dtpDateFrom.Date) and (aDateTime <= dtpDateTo.Date));

  if Result then
  begin
    vIndex := AnsiPos(']', aTestLine);

    if vIndex > 0 then
      Delete(aTestLine, 1, vIndex + 1);
  end;
end;

Comprueba si la FileDate está dentro de rango:

{: Returns true if the logtime is within filters range
@desc Purpose is to sort out logfiles that are no idea to parse (wrong day)}
function TfrmMain.FileDateInInterval(aFileTimeStamp: TDate): Boolean;
begin
  Result := (rdLatest.Checked and (Int(aFileTimeStamp) >= Int(Now - spnDateLast.Value))) or
            (rdInterval.Checked and (Int(aFileTimeStamp) >= Int(dtpDateFrom.Date)) and (Int(aFileTimeStamp) <= Int(dtpDateTo.Date)));
end;
¿Fue útil?

Solución

¿Qué tan rápido quieres ser? Si quieres ser muy rápido, entonces es necesario utilizar algo más que las ventanas de red para leer los archivos. La razón es que si quieres leer la última línea de un archivo de registro (o todas las líneas desde la última vez que lo lea), entonces usted necesita leer todo el archivo de nuevo.

En su pregunta usted dijo que el problema es que es lento para enumerar el listado de directorios. Esa es su primer cuello de botella. Si quieres ser muy rápido entonces usted necesita para cambiar a HTTP o añadir algún tipo de servidor de registro de la máquina donde se almacenan los archivos de registro.

La ventaja de utilizar HTTP es que puede hacer una solicitud gama y acaba de obtener las nuevas líneas del archivo de registro que se han añadido desde la última vez que solicitó. Que realmente va a mejorar el rendimiento ya que se está transfiriendo menos datos (sobre todo si se habilita la compresión HTTP) y también tiene menos datos para su procesamiento en el lado del cliente.

Si se agrega un servidor de registro de algún tipo, entonces ese servidor puede hacer el procesamiento del lado del servidor, donde tiene acceso nativo a los datos, y sólo devolver las filas que están en el intervalo de fechas. Una forma sencilla de hacerlo puede ser simplemente poner sus registros en una base de datos SQL de algún tipo, y luego ejecutar consultas en contra de ella.

Por lo tanto, qué tan rápido quieres ir?

Otros consejos

El problema no es su lógica, pero el sistema de archivos subyacente.

La mayoría de los sistemas de archivos se ponen muy lenta cuando se pone muchos archivos en un directorio. Esto es muy malo con FAT, NTFS, pero también sufre de ella, especialmente si usted tiene miles de archivos en un directorio.

Lo mejor que puede hacer es reorganizar las estructuras de directorios, por ejemplo, por la edad.

A continuación, tendrá como máximo un par de 100 archivos en cada directorio.

- Jeroen

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top