最终确定一个日志单元太早了

时间:2014-06-18 11:38:02

标签: delphi logging isapi finalization

我正在运行一个ISAPI服务,该服务使用IdHTTPWebBrokerBridge运行(用于作为独立的EXE进行调试)以及运行mod_isapi的Apache(在生产环境中)。

在破坏Web模块时记录一些内容时,我发现了以下问题:

unit LogFactory;

...

initialization
  GlobalLogFactory := TMyLogFactory.Create;
finalization
  FreeAndNil(GlobalLogFactory);
end.

-

unit MyWebModuleUnit;

...

uses LogFactory;

procedure TMyWebModule.WebModuleDestroy(Sender: TObject);
begin
  Assert(Assigned(GlobalLogFactory)); // <-- failure
  GlobalLogFactory.GetLogger('D:\test.txt').LogLine('test'); // <-- Nullpointer Exception
end;

LogFactory.pas在初始化时创建对象GlobalLogFactory并在其最终确定时销毁它。

但是LogFactory.pas:finalization被称为TMyWebModule.WebModuleDestroy,因为它仅包含在此单元中,因此最终确定将以相反的顺序进行。

如何确保我的GlobalLogFactory被正确释放(即FastMM不会警告内存泄漏),但与此同时,我想让所有销毁/终结程序都有机会记录什么?

一种解决方法是将LogFactory.pas明确地包含在DPR文件中作为第一个单元。但是我不太喜欢这个,因为这个日志单元应该在很多项目中使用,只需将它包含在你需要记录的单元中就可以使用它。将此日志单元放在可能希望将来记录某些内容的每个DPR中是一项很大的工作,如果忘记它可能会给那些不知道我做了什么/需要的开发人员带来问题。

2 个答案:

答案 0 :(得分:2)

不使用全局变量中的类实例,而是使用接口和函数来获取它。

unit LogFactory;

interface

type
  ILogFactory = interface
    [{GUID}]
    function GetLogger(...) : TLogger;
    ...
  end;

  TLogFactory = class( TInterfacedObject, ILogFactory )
    function GetLogger(...) : TLogger;
  end;

function GlobalLogFactory : ILogFactory;

implementation

var
  _LogFactory : ILogFactory;

function GlobalLogFactory : ILogFactory;
begin
  if not Assigned( _LogFactory ) then
    _LogFactory := TLogFactory.Create;
  Result := _LogFactory;
end;

{ TLogFactory Implementation }

initialization

// nothing needed

finalization

// nothing needed

end.

根本不需要任何initializationfinalization

答案 1 :(得分:0)

如果你想要

  1. 程序中的任何单元都可以记录,
  2. 记录在最终确定期间可用,
  3. 需要整理的日志记录代码,
  4. 那么你没有多少选择。一种选择是在.dpr文件中尽早包含日志单元。我不知道为什么你认为这是一个问题。这是实现目标的标准方法。例如,它是外部存储器管理器使用的技术。 Delphi开发人员熟悉这个概念。您告诉开发人员他们需要做什么,如果他们不遵守您的指示,该程序将无法运作。这是他们的问题。

    如果你仍然不能这样做,那么我会看到另一种选择。安排记录器初始化在代码中的任何其他初始化之前发生。所有其他定稿后的最终确定。您可以通过将记录器实现放入与加载时链接链接的外部模块来实现。