Delphi 如何通过网络快速读取日志文件?

Delphi 如何通过网络快速读取日志文件?,delphi,logfiles,logfile-analysis,Delphi,Logfiles,Logfile Analysis,我使用的是Delphi2007,有一个应用程序可以通过内部网络从多个地方读取日志文件并显示异常。这些目录有时包含数千个日志文件。应用程序有一个选项,可以从n个最新日期位中只读日志文件,它也可以在任何日期时间范围内 问题是第一次读取日志目录时速度可能非常慢(几分钟)。第二次速度要快得多 我想知道如何优化代码以尽可能快地读取日志文件? 我正在使用vCurrentFile:TStringList将文件存储在内存中。 这是从文件流更新的,因为我认为这样更快 下面是一些代码: 刷新:读取日志文件的主循环

我使用的是Delphi2007,有一个应用程序可以通过内部网络从多个地方读取日志文件并显示异常。这些目录有时包含数千个日志文件。应用程序有一个选项,可以从n个最新日期位中只读日志文件,它也可以在任何日期时间范围内

问题是第一次读取日志目录时速度可能非常慢(几分钟)。第二次速度要快得多

我想知道如何优化代码以尽可能快地读取日志文件? 我正在使用vCurrentFile:TStringList将文件存储在内存中。 这是从文件流更新的,因为我认为这样更快

下面是一些代码:

刷新:读取日志文件的主循环

// 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;
用于确定行是否在日期范围内的方法:

{: 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;
{:这将根据筛选器比较准确的异常时间
@描述2例:1.最后n天
2.从-到范围}
函数TfrmMain.ExceptionDateInInterval(var-aTestLine:String;out-aDateTime:TDateTime):布尔值;
变量
vtmpDate,vtmtime:字符串;
vDate,vTime:TDateTime;
vIndex:整数;
开始
aDateTime:=0;
vtmpDate:=副本(aTestLine,0,8);
vtmtime:=复制(aTestLine,10,9);
插入(日期分隔符,vtmpDate,5);
插入(日期分隔符,vtmpDate,8);
如果是TryStrToDate(vtmpDate、vDate、fFormatSetting)和TryStrToTime(vtmtime、vTime),则
aDateTime:=vDate+vTime;
结果:=(rdLatest.Checked和(aDateTime>=(Now-spndalatest.Value)))或
(rdInterval.Checked and(aDateTime>=dtpDateFrom.Date)和(aDateTime 0)
删除(aTestLine,1,vIndex+1);
结束;
结束;
测试filedate是否在范围内:

{: 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;
{:如果日志时间在筛选器范围内,则返回true
@desc的目的是整理不知道要解析的日志文件(错误日期)}
函数TfrmMain.FileDateInInterval(aFileTimeStamp:TDate):布尔值;
开始
结果:=(rdLatest.Checked和(Int(aFileTimeStamp)>=Int(Now-spndalatest.Value)))或

(rdInterval.Checked和(Int(aFileTimeStamp)>=Int(dtpDateFrom.Date))和(Int(aFileTimeStamp)问题不在于逻辑,而在于底层文件系统

当你把许多文件放在一个目录中时,大多数文件系统都会变得非常慢。这对于FAT来说是非常糟糕的,但是NTFS也会受到影响,特别是当你在一个目录中有数千个文件时

您所能做的最好的事情就是重新组织这些目录结构,例如按年龄

然后在每个目录中最多有100个文件


--jeroen

你想多快?如果你想真的快,那么你需要使用windows网络以外的工具来读取文件。原因是如果你想读取日志文件的最后一行(或自上次读取以来的所有行),那么你需要再次读取整个文件

在你的问题中,你说问题在于枚举目录列表的速度很慢。这是你的第一个瓶颈。如果你想真正快速,那么你需要切换到HTTP或向存储日志文件的机器添加某种日志服务器

使用HTTP的优点是,您可以执行范围请求,只需获取自上次请求以来添加的日志文件的新行即可。这将真正提高性能,因为您传输的数据更少(尤其是启用HTTP压缩的情况下),而且在客户端处理的数据也更少

如果添加某种类型的日志服务器,则该服务器可以在服务器端进行处理,在服务器端它可以对数据进行本机访问,并且只返回日期范围内的行。一种简单的方法是将日志放入某种类型的SQL数据库中,然后对其运行查询


那么,您希望以多快的速度进行搜索?

这是事实,但这是事实。在我的情况下,很少搜索超过30天的文件,因此,也许我应该通过计划的bat文件将较旧的文件移动到存档目录,加快搜索速度。您第二次速度更快的原因是服务器现在已将信息缓存在内存中。因此,没有任何内容您可以从Delphi方面做一些事情来真正加快速度(您可以稍微加快速度,但用户不会注意到效果)。因此,最好的做法确实是归档不再需要的文件。是的,我担心程序代码中没有太多工作要做。几年前,我们实际上有一个项目,将日志存储在数据库中,并以SQL方式访问,但从未完成。但正如您所说,如果我们分配大量时间在服务器上准备日志,我们可以可能会很快完成。但这不是一个高优先级的项目:)无论如何,谢谢你的回答。通过更多的编码工作,你可以避免每次都阅读整个文件。如果你只对文件的最后一部分感兴趣,你可以直接查找到最后几KB并在那里阅读。
{: 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;