Comment lire les fichiers log-sur le réseau réel rapide?
-
21-09-2019 - |
Question
J'utilise Delphi 2007 et ont une application qui journal de lecture-fichiers de plusieurs emplacements sur un réseau interne et d'affichage des exceptions. Ces répertoires contiennent parfois des milliers de fichiers log. L'application dispose d'une option de lecture seule fichiers log des n derniers jours bit il peut aussi être dans une plage datetime.
Le problème est que la première fois le répertoire du journal est lu, il peut être très lent (plusieurs minutes). La deuxième fois, il est beaucoup plus rapide.
Je me demande comment je peux optimiser mon code pour lire les fichiers log aussi vite que possible? J'utilise un vCurrentFile: TStringList pour stocker le fichier dans la mémoire. Cela est mis à jour à partir d'un FileStream que je pense que cela est plus rapide.
Voici un code:
Actualiser : La boucle principale pour lire les 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 de 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 le filedate est à l'intérieur plage:
{: 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;
La solution
À quelle vitesse voulez-vous être? Si vous voulez être vraiment rapide, vous devez utiliser autre chose que les fenêtres en réseau pour lire les fichiers. La raison 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 lisez) alors vous avez besoin de relire tout le fichier.
Dans votre question, vous avez dit que le problème est qu'il est lent à énumérer la liste des répertoires. C'est votre premier goulot d'étranglement. Si vous voulez être vraiment rapide, vous devez soit passer à HTTP ou ajouter une sorte de serveur de journal à la machine où les fichiers journaux sont stockés.
L'avantage d'utiliser HTTP est que vous pouvez faire une demande de plage et juste obtenir les nouvelles lignes du fichier journal qui ont été ajoutés depuis la dernière l'a demandé. Cela va vraiment améliorer les performances puisque vous transférez moins de données (en particulier si vous activez la compression HTTP) et vous avez également moins de données à traiter sur le côté client.
Si vous ajoutez un serveur de journal de quelque sorte, ce serveur peut faire le traitement du côté du serveur, où il dispose d'un accès natif aux données, et seulement renvoyer les lignes qui se trouvent dans la plage de dates. Une façon simple de le faire peut-être juste à mettre vos journaux dans une base de données SQL de quelque sorte, puis exécuter des requêtes contre.
Alors, comment voulez-rapide voulez-vous aller?
Autres conseils
Le problème est pas votre logique, mais le système de fichiers sous-jacent.
La plupart des systèmes de fichiers deviennent très lent lorsque vous mettez beaucoup de fichiers dans un répertoire. Ceci est très mauvais avec FAT, NTFS, mais souffre aussi d'elle, surtout si vous avez des milliers de fichiers dans un répertoire.
Le mieux que vous pouvez faire est de réorganiser les structures de répertoires, par exemple par âge.
Ensuite, avoir au plus un couple de 100 fichiers dans chaque répertoire.
- jeroen