Frage

Ich bin mit Delphi 2007 und eine Anwendung, die Log-Dateien von mehreren Stellen über ein internes Netzwerk und Anzeige Ausnahmen lesen. Diese Verzeichnisse enthalten manchmal Tausende von Log-Dateien. Die Anwendung eine Option, um nur Log-Files aus den n neuesten Tagen Lesebit es auch in jedem Datetime-Bereich sein kann.

Das Problem ist, dass das erste Mal, das Log-Verzeichnis gelesen wird es langsam sehr sein kann (mehr Minuten). Das zweite Mal, es ist wesentlich schneller.

Ich frage mich, wie ich meinen Code optimieren kann die Log-Dateien so schnell wie möglich zu lesen? Ich bin mit einem vCurrentFile: TStringList die Datei im Speicher zu speichern. Das wird aus einem Filestream aktualisiert, wie ich denke, das schneller ist.

Hier ist ein Code:

Aktualisieren : Die Hauptschleife Log-Dateien lesen

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

UpdateView Methode : In einer Reihe zur 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;

Methode zu entscheiden, ob Zeile 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;

Test, wenn das Dateidatum innerhalb Bereichs ist:

{: 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;
War es hilfreich?

Lösung

Wie schnell wollen Sie sein? Wenn Sie wirklich schnell sein wollen, dann müssen Sie etwas anderes Fenster verwenden, die Vernetzung der Dateien zu lesen. Der Grund dafür ist, wenn Sie die letzte Zeile einer Protokolldatei (oder alle Linien seit dem letzten Mal, wenn Sie es lesen) lesen möchten, dann müssen Sie wieder die gesamte Datei lesen.

In Ihrer Frage gesagt, Sie das Problem ist, dass es langsam ist Ihre Verzeichnisliste aufzuzählen. Das ist Ihre erste Engpass. Wenn Sie wirklich schnell sein wollen, dann müssen Sie entweder Schalter auf HTTP oder irgendeine Art von Log-Server an die Maschine hinzufügen, in dem die Protokolldateien gespeichert werden.

Der Vorteil der Verwendung von HTTP werden Sie eine Reihe Antrag tun und bekommen nur die neuen Zeilen der Protokolldatei, die hinzugefügt wurden seit dem letzten angefordert. Das wird wirklich die Leistung verbessern, da weniger Daten übertragen (vor allem, wenn Sie aktivieren HTTP-Komprimierung), und Sie haben auch weniger Daten zu verarbeiten auf der Client-Seite.

Wenn Sie einen Log-Server irgendeine Art hinzufügen, dann diesem Server auf der Serverseite die Verarbeitung tun, wo es nativen Zugriff auf die Daten hat, und nur die Zeilen zurück, in dem Datumsbereich sind. Eine einfache Möglichkeit, dies zu tun nur sein kann Ihre Protokolle in eine SQL-Datenbank von einer Art zu setzen, und dann Ausführen von Abfragen dagegen.

So, wie schnell wollen Sie gehen?

Andere Tipps

Das Problem ist nicht die Logik, aber das zugrunde liegende Dateisystem.

Die meisten Dateisysteme werden sehr langsam, wenn Sie viele Dateien in einem Verzeichnis. Das ist sehr schlecht mit FAT, aber NTFS leidet auch an sie, vor allem, wenn Sie Tausende von Dateien in einem Verzeichnis.

Das Beste, was Sie tun können, ist diese Verzeichnisstrukturen neu zu organisieren, zum Beispiel durch das Alter.

Dann höchstens hat ein paar 100 Dateien in jedem Verzeichnis.

- jeroen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top