实现线程安全日志记录

时间:2011-11-15 10:02:51

标签: multithreading delphi logging queue

我的任务是创建一个用于围绕多个应用程序收集用户活动的类。

假设我有一个类TLogging和一个名为Logging的全局对象。

应该在内存中收集用户活动(屏幕打开等等)(可能放入TLogging的(字符串)列表)并在一段时间间隔(每10分钟)后保存到日志文件中,或申请被关闭时。

最重要的是,日志记录必须处于“静默模式”,它不得以任何方式影响用户工作流程:没有屏幕挂起,没有例外。

请告诉我这项任务的方向。

2 个答案:

答案 0 :(得分:15)

这是一个涉及多个领域的非常广泛的问题。一些提示:

  • 至少考虑一个既定的日志框架。较新的Delphi版本附带CodeSiteSmartInspect是另一种选择。

  • 使用同步原语使您的类具有线程安全性:TCriticalSectionTMREWSync

  • 在尝试编写线程安全日志记录框架之前,请确保您了解多线程和同步中涉及的问题。一个好的开始是Martin Harvey的指南Multithreading - The Delphi Way

  • 使用后台线程定期将日志内容刷新到磁盘,或者缓冲了足够的数据。

  • 如果您遇到特定问题,请在此处提出更具体的问题。

答案 1 :(得分:3)

使用OmniThreadLibrary并假设Logging对象是单例,这非常简单。我还会限制等待写入的最大消息数,以便内部队列不会占用太多内存。

const
  CMaxMsgCount = 1000;
  CMaxLogTimeout_ms = 10{min}*60{sec/min}*1000{ms/sec};

type
  TLogging = class
  strict private
    FLogMsgCount: IOmniResourceCount;
    FLogQueue: IOmniBlockingCollection;
    FWriter: IOmniTaskControl;
  strict protected
    procedure Logger(const task: IOmniTask);
  public
    constructor Create;
    destructor  Destroy;
    procedure Log(const msg: string);
  end;

var
  Logging: TLogging;

constructor TLogging.Create;
begin
  FLogMsgCount := CreateResourceCount(CMaxMsgCount);
  FLogQueue := TOmniBlockingCollection.Create;
  FWriter := CreateTask(Logger, 'Logger').Run;
end;

destructor TLogging.Destroy;
begin
  FWriter.Terminate;
end;

procedure TLogging.Log(const msg: string);
begin
  FLogQueue.Add(msg);
  FLogMsgCount.Allocate;
end;

procedure TLogging.Logger(const task: IOmniTask);

  procedure Flush;
  var
    logData: TOmniValue;
  begin
    // open file, possibly with retry
    while FLogQueue.TryTake(logData) do begin
      FLogMsgCount.Release;
      // write logData.AsString
    end;
    // close file
  end;

begin
  while DSiWaitForTwoObjects(task.TerminateEvent, FLogMsgCount.Handle, false, CMaxLogTimeout_ms) <> WAIT_OBJECT_0 do
    Flush;
  Flush;
end;

(免责声明:“它在我的机器上编译”,否则未经测试。)