2009-10-14 10 views
0

J'utilise Delphi 2007 et j'ai une application qui lit les fichiers journaux de plusieurs endroits sur un réseau interne et affiche des exceptions. Ces répertoires contiennent parfois des milliers de fichiers journaux. L'application dispose d'une option pour lire uniquement les fichiers journaux à partir des derniers jours. Elle peut également être dans n'importe quelle plage de date/heure.Comment lire les fichiers journaux sur le réseau très rapidement?

Le problème est que la première fois que le répertoire du journal est lu, il peut être très lent (plusieurs minutes). La deuxième fois c'est beaucoup plus rapide.

Je me demande comment optimiser mon code pour lire les fichiers journaux le plus rapidement possible? J'utilise vCurrentFile: TStringList pour stocker le fichier en mémoire. Cela est mis à jour à partir d'un FileStream car je pense que c'est plus rapide.

Voici un code:

Actualiser: La boucle principale pour lire des fichiers Log

// 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éthode de UpdateView: Ajouter une ligne à 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éthode pour décider si la ligne est en 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 si la filedate est à l'intérieur gamme:

{: 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; 

Répondre

0

À quelle vitesse voulez-vous être? Si vous voulez être vraiment rapide, alors vous devez utiliser quelque chose en plus de Windows Networking pour lire les fichiers. La raison en est que si vous voulez lire la dernière ligne d'un fichier journal (ou toutes les lignes depuis la dernière fois que vous l'avez lu), vous devez lire à nouveau le fichier entier.

Dans votre question, vous avez dit que le problème est qu'il est lent à énumérer votre liste de répertoires. C'est votre premier goulot d'étranglement. Si vous voulez être très rapide, vous devez soit basculer vers HTTP, soit ajouter un serveur de journalisation sur la machine sur laquelle les fichiers journaux sont stockés. L'avantage d'utiliser HTTP est que vous pouvez faire une requête de plage et obtenir simplement les nouvelles lignes du fichier journal qui ont été ajoutées depuis votre dernière demande. Cela améliorera réellement les performances puisque vous transférez moins de données (en particulier si vous activez la compression HTTP) et que vous avez également moins de données à traiter côté client. Si vous ajoutez un serveur de journal quelconque, ce serveur peut effectuer le traitement côté serveur, où il a un accès natif aux données, et renvoyer uniquement les lignes situées dans la plage de dates.Une façon simple de le faire peut être de simplement mettre vos logs dans une base de données SQL de quelque sorte, puis d'exécuter des requêtes à son encontre.

Alors, à quelle vitesse voulez-vous aller?

+0

Oui, j'avais peur qu'il n'y ait pas grand-chose à faire dans le code du programme. Nous avions en fait un projet il y a quelques années pour stocker les logs dans une base de données et y accéder en tant que SQL mais il n'a jamais été fini. Mais comme vous l'avez dit, si nous allouons beaucoup de temps pour préparer les journaux sur les serveurs, nous pourrions probablement l'obtenir assez rapidement. Mais ce n'est pas un projet prioritaire :) Merci quand même pour votre réponse. –

+0

Avec plus d'efforts de codage, vous pouvez éviter de lire le fichier entier à chaque fois. Si vous n'êtes intéressé que par la dernière partie du fichier, vous pouvez vous rendre directement aux dernières Ko et y lire. –

2

Le problème n'est pas votre logique, mais le système de fichiers sous-jacent.

La plupart des systèmes de fichiers deviennent très lents lorsque vous placez plusieurs fichiers dans un répertoire. C'est très mauvais avec FAT, mais NTFS en souffre aussi, surtout si vous avez des milliers de fichiers dans un répertoire. Le mieux que vous pouvez faire est de réorganiser ces structures de répertoires, par exemple en fonction de l'ancienneté.

Ensuite, ayez au plus quelques centaines de fichiers dans chaque répertoire.

--jeroen

+0

C'est vrai, mais c'est la réalité. Dans mon cas, les fichiers de plus de 30 jours sont rarement recherchés, donc je devrais peut-être déplacer les fichiers plus anciens vers un répertoire d'archive par un fichier chauve-souris planifié, accélérer la recherche. –

+0

La raison pour laquelle votre deuxième fois est plus rapide, c'est que le serveur a maintenant mis en cache les informations en mémoire. Il n'y a donc rien que vous puissiez faire du côté Delphi pour vraiment l'accélérer (vous pourriez peut-être accélérer légèrement les choses, mais les utilisateurs ne remarqueront pas l'effet). Le mieux est donc d'archiver les fichiers qui ne sont plus nécessaires. –

Questions connexes