Domanda

Sto usando Delphi 2007 e dispone di un'applicazione che leggere log-files provenienti da diversi luoghi sopra un interni eccezioni di rete e di visualizzazione. Queste directory a volte contiene migliaia di log-files. L'applicazione dispone di un'opzione per sola lettura log-files dai n ultimi giorni bit può anche essere in qualsiasi fascia di datetime.

Il problema è che la prima volta che la directory del registro viene letto può essere molto lento (alcuni minuti). La seconda volta è notevolmente più veloce.

Mi chiedo come posso ottimizzare il mio codice per leggere i log-files più velocemente possibile? Io sto usando un vCurrentFile: TStringList per memorizzare il file nella memoria. Che viene aggiornato da un FileStream come credo che questo è più veloce.

Un po 'di codice:

Aggiorna : Il ciclo principale per leggere log-files

// 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;

metodo UpdateView : Aggiungere una riga alla 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;

Metodo per decidere se riga è in 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;

Prova se il filedate è dentro gamma:

{: 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;
È stato utile?

Soluzione

In quanto tempo vuoi essere? Se si vuole essere veramente veloce, allora avete bisogno di utilizzare qualcosa oltre le finestre di networking per leggere i file. La ragione è che se si desidera leggere l'ultima riga di un file di log (o tutte le linee dall'ultima volta che lo si legge), allora avete bisogno di leggere di nuovo l'intero file.

Nella tua domanda hai detto il problema è che è lento per enumerare il vostro elenco di directory. Questo è il tuo primo collo di bottiglia. Se vuoi essere veloce reale, allora è necessario o passare a HTTP o aggiungere una sorta di log server alla macchina in cui sono memorizzati i file di log.

Il vantaggio di utilizzare HTTP è che si può fare una richiesta di gamma e solo ottenere le nuove linee del file di log che sono stati aggiunti dall'ultima volta che hai richiesto. In grado davvero di migliorare le prestazioni dal momento che si stanno trasferendo meno dati (soprattutto se si attiva la compressione HTTP) e si ha anche meno dati da elaborare sul lato client.

Se si aggiunge un server di log di qualche tipo, allora il server può fare l'elaborazione sul lato server, dove ha accesso nativo ai dati, e restituire solo le righe che si trovano nell'intervallo di date. Un modo semplice per farlo potrebbe essere quello di mettere solo il vostro log in un database SQL di qualche tipo, e quindi eseguire query contro di esso.

Quindi, quanto velocemente vuoi andare?

Altri suggerimenti

Il problema non è la logica, ma il file system sottostante.

La maggior parte dei sistemi di file diventano molto lento quando si mette molti file in una directory. Questo è molto male con FAT, NTFS, ma soffre anche di esso, soprattutto se si dispone di migliaia di file in una directory.

Il meglio che puoi fare è riorganizzare quelle strutture di directory, ad esempio, per età.

Quindi avere al massimo un paio di 100 file in ogni directory.

- Jeroen

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top