1
0
mirror of synced 2024-11-23 03:43:51 +00:00

Added: Category support (breaking change)

Fixed: graphic didn't show up in test Named Pipe client due to TJPEGImage not being referenced
This commit is contained in:
Mark van Renswoude 2014-10-20 12:07:44 +00:00
parent ca47ad3f4e
commit 53220780db
16 changed files with 341 additions and 81 deletions

View File

@ -2,7 +2,9 @@ program X2LogNamedPipeClient;
uses uses
// FastMM4, // FastMM4,
System.Classes,
Vcl.Forms, Vcl.Forms,
Vcl.Imaging.jpeg,
X2Log.Intf, X2Log.Intf,
X2Log.Client.NamedPipe, X2Log.Client.NamedPipe,
X2Log.Observer.MonitorForm; X2Log.Observer.MonitorForm;
@ -13,6 +15,9 @@ var
client: IX2LogObservable; client: IX2LogObservable;
begin begin
{ To deserialize the graphic, make sure GetClass succeeds }
RegisterClass(TJPEGImage);
ReportMemoryLeaksOnShutdown := True; ReportMemoryLeaksOnShutdown := True;
Application.Initialize; Application.Initialize;

View File

@ -228,8 +228,6 @@ object MainForm: TMainForm
Caption = 'Monitor Form Observer' Caption = 'Monitor Form Observer'
TabOrder = 0 TabOrder = 0
OnClick = btnMonitorFormClick OnClick = btnMonitorFormClick
ExplicitLeft = 151
ExplicitTop = -6
end end
object btnLock: TButton object btnLock: TButton
AlignWithMargins = True AlignWithMargins = True
@ -246,7 +244,6 @@ object MainForm: TMainForm
Caption = 'Lock' Caption = 'Lock'
TabOrder = 2 TabOrder = 2
OnClick = btnLockClick OnClick = btnLockClick
ExplicitLeft = 149
end end
object btnUnlock: TButton object btnUnlock: TButton
AlignWithMargins = True AlignWithMargins = True
@ -263,7 +260,6 @@ object MainForm: TMainForm
Caption = 'Unlock' Caption = 'Unlock'
TabOrder = 3 TabOrder = 3
OnClick = btnUnlockClick OnClick = btnUnlockClick
ExplicitLeft = 359
end end
end end
object pcDispatch: TPageControl object pcDispatch: TPageControl
@ -337,6 +333,15 @@ object MainForm: TMainForm
Text = 'Hello world!' Text = 'Hello world!'
OnKeyDown = edtMessageKeyDown OnKeyDown = edtMessageKeyDown
end end
object btnCategory: TButton
Left = 416
Top = 39
Width = 101
Height = 21
Caption = 'With category'
TabOrder = 5
OnClick = btnCategoryClick
end
end end
object tsException: TTabSheet object tsException: TTabSheet
Caption = 'Exception' Caption = 'Exception'
@ -458,7 +463,7 @@ object MainForm: TMainForm
Left = 552 Left = 552
Top = 176 Top = 176
Bitmap = { Bitmap = {
494C01010200140048000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 494C01010200140050000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
0000000000003600000028000000300000000C00000001002000000000000009 0000000000003600000028000000300000000C00000001002000000000000009
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000

View File

@ -61,6 +61,7 @@ type
lbNamedPipeServers: TListBox; lbNamedPipeServers: TListBox;
btnLock: TButton; btnLock: TButton;
btnUnlock: TButton; btnUnlock: TButton;
btnCategory: TButton;
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject); procedure FormDestroy(Sender: TObject);
@ -81,13 +82,14 @@ type
procedure btnBinaryRawByteStringClick(Sender: TObject); procedure btnBinaryRawByteStringClick(Sender: TObject);
procedure btnGraphicClick(Sender: TObject); procedure btnGraphicClick(Sender: TObject);
procedure btnNamedPipeRefreshClick(Sender: TObject); procedure btnNamedPipeRefreshClick(Sender: TObject);
procedure btnCategoryClick(Sender: TObject);
private private
FLog: IX2Log; FLog: IX2Log;
FEventObserver: IX2LogObserver; FEventObserver: IX2LogObserver;
FFileObserver: IX2LogObserver; FFileObserver: IX2LogObserver;
FNamedPipeObserver: IX2LogObserver; FNamedPipeObserver: IX2LogObserver;
protected protected
procedure DoLog(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg: string; Details: IX2LogDetails); procedure DoLog(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg, Category: string; Details: IX2LogDetails);
end; end;
@ -149,13 +151,13 @@ begin
end; end;
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg: string; Details: IX2LogDetails); procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg, Category: string; Details: IX2LogDetails);
var var
text: string; text: string;
logDetailsText: IX2LogDetailsText; logDetailsText: IX2LogDetailsText;
begin begin
text := GetLogLevelText(Level) + ': ' + Msg; text := GetLogLevelText(Level) + ': ' + Category + ': ' + Msg;
if Supports(Details, IX2LogDetailsText, logDetailsText) then if Supports(Details, IX2LogDetailsText, logDetailsText) then
text := text + ' (' + logDetailsText.AsString + ')'; text := text + ' (' + logDetailsText.AsString + ')';
@ -183,6 +185,7 @@ begin
end; end;
end; end;
procedure TMainForm.btnCloseClick(Sender: TObject); procedure TMainForm.btnCloseClick(Sender: TObject);
begin begin
Close; Close;
@ -243,6 +246,12 @@ begin
end; end;
procedure TMainForm.btnCategoryClick(Sender: TObject);
begin
FLog.Category('Test').Info(edtMessage.Text);
end;
procedure TMainForm.btnMonitorFormClick(Sender: TObject); procedure TMainForm.btnMonitorFormClick(Sender: TObject);
begin begin
TX2LogObserverMonitorForm.ShowInstance(FLog); TX2LogObserverMonitorForm.ShowInstance(FLog);

View File

@ -19,8 +19,8 @@ type
destructor Destroy; override; destructor Destroy; override;
{ IX2LogBase } { IX2LogBase }
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); overload; virtual; procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload; virtual;
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails = nil); overload; virtual; procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload; virtual;
{ IX2LogObservable } { IX2LogObservable }
procedure Attach(AObserver: IX2LogObserver); procedure Attach(AObserver: IX2LogObserver);
@ -64,23 +64,23 @@ begin
end; end;
procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails);
var var
observer: IX2LogObserver; observer: IX2LogObserver;
begin begin
for observer in Observers do for observer in Observers do
observer.Log(ALevel, AMessage, ADetails); observer.Log(ALevel, AMessage, ACategory, ADetails);
end; end;
procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
var var
observer: IX2LogObserver; observer: IX2LogObserver;
begin begin
for observer in Observers do for observer in Observers do
observer.Log(ALevel, ADateTime, AMessage, ADetails); observer.Log(ALevel, ADateTime, AMessage, ACategory, ADetails);
end; end;
end. end.

View File

@ -345,6 +345,7 @@ var
detailsSize: Cardinal; detailsSize: Cardinal;
detailsStream: TMemoryStream; detailsStream: TMemoryStream;
serializer: IX2LogDetailsSerializer; serializer: IX2LogDetailsSerializer;
category: string;
begin begin
if MessageData.Size > 0 then if MessageData.Size > 0 then
@ -365,6 +366,9 @@ begin
end else if headerDiff < 0 then end else if headerDiff < 0 then
raise EReadError.Create('Header too small'); raise EReadError.Create('Header too small');
{ Category }
category := TStreamUtil.ReadString(MessageData);
{ Message } { Message }
msg := TStreamUtil.ReadString(MessageData); msg := TStreamUtil.ReadString(MessageData);
@ -393,7 +397,7 @@ begin
end; end;
end; end;
Client.Log(header.Level, header.DateTime, msg, details); Client.Log(header.Level, header.DateTime, msg, category, details);
except except
on E:EReadError do on E:EReadError do
ClosePipe; ClosePipe;

View File

@ -11,6 +11,11 @@ resourcestring
LogLevelWarning = 'Warning'; LogLevelWarning = 'Warning';
LogLevelError = 'Error'; LogLevelError = 'Error';
LogCategorySeparator = ' - ';
// Note: currently not translatable
LogCategoryDefault = '';
{ {
X2Log.Observer.LogFile X2Log.Observer.LogFile
@ -36,6 +41,7 @@ resourcestring
{ Caption of the columns in the live log view } { Caption of the columns in the live log view }
LogMonitorFormColumnTime = 'Time'; LogMonitorFormColumnTime = 'Time';
LogMonitorFormColumnCategory = 'Category';
LogMonitorFormColumnMessage = 'Message'; LogMonitorFormColumnMessage = 'Message';
{ Caption of the toolbar buttons } { Caption of the toolbar buttons }

View File

@ -24,8 +24,10 @@ type
class procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy); class procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
{ Facade for IX2LogBase } { Facade for IX2LogBase }
class procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); overload; class procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
class procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); overload; class procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
class function Category(const ACategory: string): IX2Log;
class procedure Verbose(const AMessage: string; const ADetails: string = ''); class procedure Verbose(const AMessage: string; const ADetails: string = '');
class procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil); class procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil);
@ -82,15 +84,21 @@ begin
end; end;
class procedure TX2GlobalLog.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); class function TX2GlobalLog.Category(const ACategory: string): IX2Log;
begin begin
Instance.Log(ALevel, AMessage, ADetails); Result := Instance.Category(ACategory);
end; end;
class procedure TX2GlobalLog.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); class procedure TX2GlobalLog.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
Instance.Log(ALevel, ADateTime, AMessage, ADetails); Instance.Log(ALevel, AMessage, ACategory, ADetails);
end;
class procedure TX2GlobalLog.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin
Instance.Log(ALevel, ADateTime, AMessage, ACategory, ADetails);
end; end;

View File

@ -48,8 +48,8 @@ type
{ Logging } { Logging }
IX2LogBase = interface IX2LogBase = interface
['{1949E8DC-6DC5-43DC-B678-55CF8274E79D}'] ['{1949E8DC-6DC5-43DC-B678-55CF8274E79D}']
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); overload; procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails = nil); overload; procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
end; end;
@ -75,6 +75,8 @@ type
['{A6FF38F9-EDA8-4C76-9C95-2C0317560D78}'] ['{A6FF38F9-EDA8-4C76-9C95-2C0317560D78}']
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy); procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
function Category(const ACategory: string): IX2Log;
procedure Verbose(const AMessage: string; const ADetails: string = ''); procedure Verbose(const AMessage: string; const ADetails: string = '');
procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil); procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil);
@ -102,6 +104,8 @@ type
{ {
Payload: Payload:
CategoryLength: Cardinal
Category: WideString
MessageLength: Cardinal MessageLength: Cardinal
Message: WideString Message: WideString
DetailsLength: Cardinal DetailsLength: Cardinal
@ -109,6 +113,7 @@ type
} }
end; end;
TX2LogMessageHeader = TX2LogMessageHeaderV1; TX2LogMessageHeader = TX2LogMessageHeaderV1;
const const

View File

@ -15,15 +15,15 @@ type
private private
FLogLevels: TX2LogLevels; FLogLevels: TX2LogLevels;
protected protected
procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); virtual; abstract; procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails); virtual; abstract;
property LogLevels: TX2LogLevels read FLogLevels; property LogLevels: TX2LogLevels read FLogLevels;
public public
constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault);
{ IX2LogBase } { IX2LogBase }
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); overload; procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails = nil); overload; procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
end; end;
@ -39,16 +39,16 @@ begin
end; end;
procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
Log(ALevel, Now, AMessage, ADetails); Log(ALevel, Now, AMessage, ACategory, ADetails);
end; end;
procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
if ALevel in LogLevels then if ALevel in LogLevels then
DoLog(ALevel, ADateTime, AMessage, ADetails); DoLog(ALevel, ADateTime, AMessage, ACategory, ADetails);
end; end;
end. end.

View File

@ -20,7 +20,7 @@ type
protected protected
function CreateWorkerThread: TX2LogObserverWorkerThread; virtual; abstract; function CreateWorkerThread: TX2LogObserverWorkerThread; virtual; abstract;
procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); override; procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails); override;
property WorkerThread: TX2LogObserverWorkerThread read FWorkerThread; property WorkerThread: TX2LogObserverWorkerThread read FWorkerThread;
public public
@ -34,9 +34,10 @@ type
FDetails: IX2LogDetails; FDetails: IX2LogDetails;
FLevel: TX2LogLevel; FLevel: TX2LogLevel;
FDateTime: TDateTime; FDateTime: TDateTime;
FCategory: string;
FMessage: string; FMessage: string;
public public
constructor Create(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); overload; constructor Create(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails); overload;
constructor Create(AEntry: TX2LogQueueEntry); overload; constructor Create(AEntry: TX2LogQueueEntry); overload;
procedure Assign(Source: TPersistent); override; procedure Assign(Source: TPersistent); override;
@ -44,6 +45,7 @@ type
property DateTime: TDateTime read FDateTime; property DateTime: TDateTime read FDateTime;
property Details: IX2LogDetails read FDetails; property Details: IX2LogDetails read FDetails;
property Level: TX2LogLevel read FLevel; property Level: TX2LogLevel read FLevel;
property Category: string read FCategory;
property Message: string read FMessage; property Message: string read FMessage;
end; end;
@ -71,7 +73,7 @@ type
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
end; end;
@ -97,20 +99,21 @@ begin
end; end;
procedure TX2LogCustomThreadedObserver.DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogCustomThreadedObserver.DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
WorkerThread.Log(ALevel, ADateTime, AMessage, ADetails); WorkerThread.Log(ALevel, ADateTime, AMessage, ACategory, ADetails);
end; end;
{ TX2LogQueueEntry } { TX2LogQueueEntry }
constructor TX2LogQueueEntry.Create(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); constructor TX2LogQueueEntry.Create(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
inherited Create; inherited Create;
FLevel := ALevel; FLevel := ALevel;
FDateTime := ADateTime; FDateTime := ADateTime;
FCategory := ACategory;
FMessage := AMessage; FMessage := AMessage;
FDetails := ADetails; FDetails := ADetails;
end; end;
@ -135,6 +138,7 @@ begin
FLevel := entrySource.Level; FLevel := entrySource.Level;
FDateTime := entrySource.DateTime; FDateTime := entrySource.DateTime;
FCategory := entrySource.Category;
FMessage := entrySource.Message; FMessage := entrySource.Message;
FDetails := entrySource.Details; FDetails := entrySource.Details;
end else end else
@ -168,11 +172,11 @@ begin
end; end;
procedure TX2LogObserverWorkerThread.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogObserverWorkerThread.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
TMonitor.Enter(LogQueue); TMonitor.Enter(LogQueue);
try try
LogQueue.Enqueue(TX2LogQueueEntry.Create(ALevel, ADateTime, AMessage, ADetails)); LogQueue.Enqueue(TX2LogQueueEntry.Create(ALevel, ADateTime, AMessage, ACategory, ADetails));
finally finally
TMonitor.Exit(LogQueue); TMonitor.Exit(LogQueue);
end; end;

View File

@ -7,7 +7,7 @@ uses
type type
TX2LogEvent = procedure(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg: string; Details: IX2LogDetails) of object; TX2LogEvent = procedure(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg, Category: string; Details: IX2LogDetails) of object;
TX2LogEventObserver = class(TX2LogCustomObserver) TX2LogEventObserver = class(TX2LogCustomObserver)
@ -15,7 +15,7 @@ type
FOnLog: TX2LogEvent; FOnLog: TX2LogEvent;
FRunInMainThread: Boolean; FRunInMainThread: Boolean;
protected protected
procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); override; procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails); override;
public public
constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload; constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
constructor Create(AOnLog: TX2LogEvent; ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload; constructor Create(AOnLog: TX2LogEvent; ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
@ -48,7 +48,7 @@ begin
end; end;
procedure TX2LogEventObserver.DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogEventObserver.DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
if Assigned(FOnLog) then if Assigned(FOnLog) then
begin begin
@ -58,10 +58,10 @@ begin
procedure procedure
begin begin
if Assigned(FOnLog) then if Assigned(FOnLog) then
FOnLog(Self, ALevel, ADateTime, AMessage, ADetails); FOnLog(Self, ALevel, ADateTime, AMessage, ACategory, ADetails);
end); end);
end else end else
FOnLog(Self, ALevel, ADateTime, AMessage, ADetails); FOnLog(Self, ALevel, ADateTime, AMessage, ACategory, ADetails);
end; end;
end; end;

View File

@ -19,7 +19,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
PixelsPerInch = 96 PixelsPerInch = 96
TextHeight = 13 TextHeight = 13
object splDetails: TSplitter object splDetails: TSplitter
Left = 602 Left = 682
Top = 0 Top = 0
Width = 6 Width = 6
Height = 496 Height = 496
@ -29,9 +29,9 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
ExplicitHeight = 519 ExplicitHeight = 519
end end
object pnlDetails: TPanel object pnlDetails: TPanel
Left = 608 Left = 688
Top = 0 Top = 0
Width = 382 Width = 302
Height = 496 Height = 496
Align = alRight Align = alRight
BevelOuter = bvNone BevelOuter = bvNone
@ -39,7 +39,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
object tbDetails: TToolBar object tbDetails: TToolBar
Left = 0 Left = 0
Top = 0 Top = 0
Width = 382 Width = 302
Height = 22 Height = 22
AutoSize = True AutoSize = True
ButtonWidth = 80 ButtonWidth = 80
@ -76,7 +76,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
object pnlBorder: TPanel object pnlBorder: TPanel
Left = 0 Left = 0
Top = 22 Top = 22
Width = 382 Width = 302
Height = 474 Height = 474
Align = alClient Align = alClient
BevelKind = bkFlat BevelKind = bkFlat
@ -85,21 +85,21 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
object hcDetails: THeaderControl object hcDetails: THeaderControl
Left = 0 Left = 0
Top = 0 Top = 0
Width = 378 Width = 298
Height = 19 Height = 19
Sections = < Sections = <
item item
AutoSize = True AutoSize = True
ImageIndex = -1 ImageIndex = -1
Text = 'Details' Text = 'Details'
Width = 378 Width = 298
end> end>
NoSizing = True NoSizing = True
end end
object reDetails: TRichEdit object reDetails: TRichEdit
Left = 0 Left = 0
Top = 19 Top = 19
Width = 378 Width = 298
Height = 451 Height = 451
Align = alClient Align = alClient
BorderStyle = bsNone BorderStyle = bsNone
@ -118,7 +118,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
object sbDetailsImage: TScrollBox object sbDetailsImage: TScrollBox
Left = 0 Left = 0
Top = 19 Top = 19
Width = 378 Width = 298
Height = 451 Height = 451
HorzScrollBar.Tracking = True HorzScrollBar.Tracking = True
VertScrollBar.Tracking = True VertScrollBar.Tracking = True
@ -141,7 +141,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
object pnlLog: TPanel object pnlLog: TPanel
Left = 0 Left = 0
Top = 0 Top = 0
Width = 602 Width = 682
Height = 496 Height = 496
Align = alClient Align = alClient
BevelOuter = bvNone BevelOuter = bvNone
@ -149,10 +149,10 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
object vstLog: TVirtualStringTree object vstLog: TVirtualStringTree
Left = 0 Left = 0
Top = 22 Top = 22
Width = 602 Width = 682
Height = 474 Height = 474
Align = alClient Align = alClient
Header.AutoSizeIndex = 2 Header.AutoSizeIndex = 3
Header.Font.Charset = DEFAULT_CHARSET Header.Font.Charset = DEFAULT_CHARSET
Header.Font.Color = clWindowText Header.Font.Color = clWindowText
Header.Font.Height = -11 Header.Font.Height = -11
@ -182,14 +182,19 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
end end
item item
Position = 2 Position = 2
Width = 428 Width = 150
WideText = 'Category'
end
item
Position = 3
Width = 354
WideText = 'Message' WideText = 'Message'
end> end>
end end
object tbLog: TToolBar object tbLog: TToolBar
Left = 0 Left = 0
Top = 0 Top = 0
Width = 602 Width = 682
Height = 22 Height = 22
AutoSize = True AutoSize = True
ButtonWidth = 67 ButtonWidth = 67
@ -216,7 +221,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
Left = 116 Left = 116
Top = 0 Top = 0
Width = 46 Width = 46
Height = 13 Height = 22
Caption = ' Filter: ' Caption = ' Filter: '
Layout = tlCenter Layout = tlCenter
end end
@ -267,7 +272,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
Left = 448 Left = 448
Top = 48 Top = 48
Bitmap = { Bitmap = {
494C01010A004000EC0010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 494C01010A004000F40010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
0000000000003600000028000000400000003000000001002000000000000030 0000000000003600000028000000400000003000000001002000000000000030
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000

View File

@ -183,8 +183,8 @@ type
procedure Unlock; procedure Unlock;
{ IX2LogObserver } { IX2LogObserver }
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); overload; procedure Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails); overload;
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); overload; procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails); overload;
property FreeOnClose: Boolean read FFreeOnClose write FFreeOnClose; property FreeOnClose: Boolean read FFreeOnClose write FFreeOnClose;
property MaxEntries: Cardinal read FMaxEntries write FMaxEntries; property MaxEntries: Cardinal read FMaxEntries write FMaxEntries;
@ -212,10 +212,11 @@ type
Time: TDateTime; Time: TDateTime;
Paused: Boolean; Paused: Boolean;
Level: TX2LogLevel; Level: TX2LogLevel;
Category: string;
Message: string; Message: string;
Details: IX2LogDetails; Details: IX2LogDetails;
procedure Initialize(APaused: Boolean; ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure Initialize(APaused: Boolean; ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
end; end;
PLogEntryNodeData = ^TLogEntryNodeData; PLogEntryNodeData = ^TLogEntryNodeData;
@ -224,17 +225,19 @@ type
const const
ColumnLevel = 0; ColumnLevel = 0;
ColumnTime = 1; ColumnTime = 1;
ColumnMessage = 2; ColumnCategory = 2;
ColumnMessage = 3;
LevelImageIndex: array[TX2LogLevel] of TImageIndex = (0, 1, 2, 3); LevelImageIndex: array[TX2LogLevel] of TImageIndex = (0, 1, 2, 3);
{ TLogEntryNode } { TLogEntryNode }
procedure TLogEntryNodeData.Initialize(APaused: Boolean; ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure TLogEntryNodeData.Initialize(APaused: Boolean; ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
Self.Time := ADateTime; Self.Time := ADateTime;
Self.Paused := APaused; Self.Paused := APaused;
Self.Level := ALevel; Self.Level := ALevel;
Self.Category := ACategory;
Self.Message := AMessage; Self.Message := AMessage;
Self.Details := ADetails; Self.Details := ADetails;
end; end;
@ -345,6 +348,7 @@ begin
vstLog.NodeDataSize := SizeOf(TLogEntryNodeData); vstLog.NodeDataSize := SizeOf(TLogEntryNodeData);
vstLog.Header.Columns[ColumnTime].Text := GetLogResourceString(@LogMonitorFormColumnTime); vstLog.Header.Columns[ColumnTime].Text := GetLogResourceString(@LogMonitorFormColumnTime);
vstLog.Header.Columns[ColumnCategory].Text := GetLogResourceString(@LogMonitorFormColumnCategory);
vstLog.Header.Columns[ColumnMessage].Text := GetLogResourceString(@LogMonitorFormColumnMessage); vstLog.Header.Columns[ColumnMessage].Text := GetLogResourceString(@LogMonitorFormColumnMessage);
mmMainFile.Caption := GetLogResourceString(@LogMonitorFormMenuFile); mmMainFile.Caption := GetLogResourceString(@LogMonitorFormMenuFile);
@ -456,13 +460,13 @@ begin
end; end;
procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin begin
Log(ALevel, Now, AMessage, ADetails); Log(ALevel, Now, AMessage, ACategory, ADetails);
end; end;
procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
var var
node: PVirtualNode; node: PVirtualNode;
nodeData: PLogEntryNodeData; nodeData: PLogEntryNodeData;
@ -486,7 +490,7 @@ begin
{ BeginUpdate causes OnInitNode to be triggered on-demand, { BeginUpdate causes OnInitNode to be triggered on-demand,
moved Initialize call here } moved Initialize call here }
Initialize(nodeData^); Initialize(nodeData^);
nodeData^.Initialize(Paused, ALevel, ADateTime, AMessage, ADetails); nodeData^.Initialize(Paused, ALevel, ADateTime, AMessage, ACategory, ADetails);
vstLog.IsVisible[node] := (not Paused) and (ALevel in VisibleLevels); vstLog.IsVisible[node] := (not Paused) and (ALevel in VisibleLevels);
@ -762,7 +766,7 @@ begin
for node in vstLog.Nodes do for node in vstLog.Nodes do
begin begin
nodeData := vstLog.GetNodeData(node); nodeData := vstLog.GetNodeData(node);
ALog.Log(nodeData^.Level, nodeData^.Time, nodeData^.Message, nodeData^.Details); ALog.Log(nodeData^.Level, nodeData^.Time, nodeData^.Message, nodeData^.Category, nodeData^.Details);
end; end;
end; end;
@ -790,6 +794,9 @@ begin
ColumnTime: ColumnTime:
CellText := DateTimeToStr(nodeData^.Time); CellText := DateTimeToStr(nodeData^.Time);
ColumnCategory:
CellText := nodeData^.Category;
ColumnMessage: ColumnMessage:
CellText := nodeData^.Message; CellText := nodeData^.Message;
end; end;

View File

@ -220,6 +220,9 @@ begin
WriteBuffer.WriteBuffer(header, SizeOf(header)); WriteBuffer.WriteBuffer(header, SizeOf(header));
{ Category }
TStreamUtil.WriteString(WriteBuffer, AEntry.Category);
{ Message } { Message }
TStreamUtil.WriteString(WriteBuffer, AEntry.Message); TStreamUtil.WriteString(WriteBuffer, AEntry.Message);

View File

@ -19,6 +19,7 @@ begin
SetLogResourceString(@LogMonitorFormCaption, '%s - Live Log'); SetLogResourceString(@LogMonitorFormCaption, '%s - Live Log');
SetLogResourceString(@LogMonitorFormColumnTime, 'Tijd'); SetLogResourceString(@LogMonitorFormColumnTime, 'Tijd');
SetLogResourceString(@LogMonitorFormColumnCategory, 'Categorie');
SetLogResourceString(@LogMonitorFormColumnMessage, 'Melding'); SetLogResourceString(@LogMonitorFormColumnMessage, 'Melding');
SetLogResourceString(@LogMonitorFormButtonClear, 'Wissen'); SetLogResourceString(@LogMonitorFormButtonClear, 'Wissen');

220
X2Log.pas
View File

@ -11,10 +11,19 @@ uses
type type
TX2Log = class(TX2LogBaseClient, IX2Log) IX2LogExceptionCategory = interface
['{ED076237-65AA-4B0C-8A25-4A6B28D2F0CB}']
procedure Exception(AException: Exception; const AMessage: string = ''; const ACategory: string = '');
end;
TX2Log = class(TX2LogBaseClient, IX2Log, IX2LogExceptionCategory)
private private
FExceptionStrategy: IX2LogExceptionStrategy; FExceptionStrategy: IX2LogExceptionStrategy;
private protected
{ IX2LogExceptionCategory }
procedure Exception(AException: Exception; const AMessage: string = ''; const ACategory: string = ''); overload;
property ExceptionStrategy: IX2LogExceptionStrategy read FExceptionStrategy; property ExceptionStrategy: IX2LogExceptionStrategy read FExceptionStrategy;
public public
constructor Create; constructor Create;
@ -22,6 +31,49 @@ type
{ IX2Log } { IX2Log }
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy); procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
function Category(const ACategory: string): IX2Log;
procedure Verbose(const AMessage: string; const ADetails: string = '');
procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil);
procedure Info(const AMessage: string; const ADetails: string = '');
procedure InfoEx(const AMessage: string; ADetails: IX2LogDetails = nil);
procedure Warning(const AMessage: string; const ADetails: string = '');
procedure WarningEx(const AMessage: string; ADetails: IX2LogDetails = nil);
procedure Error(const AMessage: string; const ADetails: string = '');
procedure ErrorEx(const AMessage: string; ADetails: IX2LogDetails = nil);
procedure Exception(AException: Exception; const AMessage: string = ''); overload;
end;
TX2LogCategoryDecorator = class(TInterfacedObject, IX2Log)
private
FCategoryName: string;
FDecoratedLog: IX2Log;
protected
function GetCategory(const ACategory: string = ''): string;
property CategoryName: string read FCategoryName write FCategoryName;
property DecoratedLog: IX2Log read FDecoratedLog;
public
constructor Create(ADecoratedLog: IX2Log; const ACategory: string);
{ IX2LogBase }
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload;
{ IX2LogObservable }
procedure Attach(AObserver: IX2LogObserver);
procedure Detach(AObserver: IX2LogObserver);
{ IX2Log }
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
function Category(const ACategory: string): IX2Log;
procedure Verbose(const AMessage: string; const ADetails: string = ''); procedure Verbose(const AMessage: string; const ADetails: string = '');
procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil); procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil);
@ -38,8 +90,10 @@ type
end; end;
implementation implementation
uses uses
X2Log.Constants,
X2Log.Details.Default, X2Log.Details.Default,
X2Log.Exception.Default; X2Log.Exception.Default;
@ -62,55 +116,67 @@ begin
end; end;
function TX2Log.Category(const ACategory: string): IX2Log;
begin
Result := TX2LogCategoryDecorator.Create(Self, ACategory);
end;
procedure TX2Log.Verbose(const AMessage, ADetails: string); procedure TX2Log.Verbose(const AMessage, ADetails: string);
begin begin
Log(TX2LogLevel.Verbose, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); Log(TX2LogLevel.Verbose, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end; end;
procedure TX2Log.VerboseEx(const AMessage: string; ADetails: IX2LogDetails); procedure TX2Log.VerboseEx(const AMessage: string; ADetails: IX2LogDetails);
begin begin
Log(TX2LogLevel.Verbose, AMessage, ADetails); Log(TX2LogLevel.Verbose, AMessage, LogCategoryDefault, ADetails);
end; end;
procedure TX2Log.Info(const AMessage, ADetails: string); procedure TX2Log.Info(const AMessage, ADetails: string);
begin begin
Log(TX2LogLevel.Info, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); Log(TX2LogLevel.Info, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end; end;
procedure TX2Log.InfoEx(const AMessage: string; ADetails: IX2LogDetails); procedure TX2Log.InfoEx(const AMessage: string; ADetails: IX2LogDetails);
begin begin
Log(TX2LogLevel.Info, AMessage, ADetails); Log(TX2LogLevel.Info, AMessage, LogCategoryDefault, ADetails);
end; end;
procedure TX2Log.Warning(const AMessage, ADetails: string); procedure TX2Log.Warning(const AMessage, ADetails: string);
begin begin
Log(TX2LogLevel.Warning, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); Log(TX2LogLevel.Warning, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end; end;
procedure TX2Log.WarningEx(const AMessage: string; ADetails: IX2LogDetails); procedure TX2Log.WarningEx(const AMessage: string; ADetails: IX2LogDetails);
begin begin
Log(TX2LogLevel.Warning, AMessage, ADetails); Log(TX2LogLevel.Warning, AMessage, LogCategoryDefault, ADetails);
end; end;
procedure TX2Log.Error(const AMessage, ADetails: string); procedure TX2Log.Error(const AMessage, ADetails: string);
begin begin
Log(TX2LogLevel.Error, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); Log(TX2LogLevel.Error, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end; end;
procedure TX2Log.ErrorEx(const AMessage: string; ADetails: IX2LogDetails); procedure TX2Log.ErrorEx(const AMessage: string; ADetails: IX2LogDetails);
begin begin
Log(TX2LogLevel.Error, AMessage, ADetails); Log(TX2LogLevel.Error, AMessage, LogCategoryDefault, ADetails);
end; end;
procedure TX2Log.Exception(AException: Exception; const AMessage: string); procedure TX2Log.Exception(AException: Exception; const AMessage: string);
begin
Exception(AException, AMessage, LogCategoryDefault);
end;
procedure TX2Log.Exception(AException: Exception; const AMessage, ACategory: string);
var var
msg: string; msg: string;
details: IX2LogDetails; details: IX2LogDetails;
@ -120,7 +186,139 @@ begin
details := nil; details := nil;
ExceptionStrategy.Execute(AException, msg, details); ExceptionStrategy.Execute(AException, msg, details);
Log(TX2LogLevel.Error, msg, details); Log(TX2LogLevel.Error, msg, LogCategoryDefault, details);
end;
{ TX2LogCategoryDecorator }
constructor TX2LogCategoryDecorator.Create(ADecoratedLog: IX2Log; const ACategory: string);
begin
inherited Create;
FDecoratedLog := ADecoratedLog;
FCategoryName := ACategory;
end;
procedure TX2LogCategoryDecorator.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(ALevel, AMessage, GetCategory(ACategory), ADetails);
end;
procedure TX2LogCategoryDecorator.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage, ACategory: string; ADetails: IX2LogDetails);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(ALevel, AMessage, GetCategory(ACategory), ADetails);
end;
procedure TX2LogCategoryDecorator.Attach(AObserver: IX2LogObserver);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Attach(AObserver);
end;
procedure TX2LogCategoryDecorator.Detach(AObserver: IX2LogObserver);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Detach(AObserver);
end;
procedure TX2LogCategoryDecorator.SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
begin
if Assigned(DecoratedLog) then
DecoratedLog.SetExceptionStrategy(AStrategy);
end;
function TX2LogCategoryDecorator.Category(const ACategory: string): IX2Log;
begin
if Assigned(DecoratedLog) then
Result := Self.Create(DecoratedLog, GetCategory(ACategory));
end;
procedure TX2LogCategoryDecorator.Verbose(const AMessage, ADetails: string);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Verbose, AMessage, CategoryName, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end;
procedure TX2LogCategoryDecorator.VerboseEx(const AMessage: string; ADetails: IX2LogDetails);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Verbose, AMessage, CategoryName, ADetails);
end;
procedure TX2LogCategoryDecorator.Info(const AMessage, ADetails: string);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Info, AMessage, CategoryName, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end;
procedure TX2LogCategoryDecorator.InfoEx(const AMessage: string; ADetails: IX2LogDetails);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Info, AMessage, CategoryName, ADetails);
end;
procedure TX2LogCategoryDecorator.Warning(const AMessage, ADetails: string);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Warning, AMessage, CategoryName, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end;
procedure TX2LogCategoryDecorator.WarningEx(const AMessage: string; ADetails: IX2LogDetails);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Warning, AMessage, CategoryName, ADetails);
end;
procedure TX2LogCategoryDecorator.Error(const AMessage, ADetails: string);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Error, AMessage, CategoryName, TX2LogStringDetails.CreateIfNotEmpty(ADetails));
end;
procedure TX2LogCategoryDecorator.ErrorEx(const AMessage: string; ADetails: IX2LogDetails);
begin
if Assigned(DecoratedLog) then
DecoratedLog.Log(TX2LogLevel.Error, AMessage, CategoryName, ADetails);
end;
procedure TX2LogCategoryDecorator.Exception(AException: Exception; const AMessage: string);
var
exceptionCategory: IX2LogExceptionCategory;
begin
if not Assigned(DecoratedLog) then
exit;
if Supports(DecoratedLog, IX2LogExceptionCategory, exceptionCategory) then
exceptionCategory.Exception(AException, AMessage, CategoryName)
else
DecoratedLog.Log(TX2LogLevel.Error, AMessage, CategoryName);
end;
function TX2LogCategoryDecorator.GetCategory(const ACategory: string): string;
begin
Result := CategoryName;
if Length(ACategory) > 0 then
Result := Result + LogCategorySeparator + ACategory;
end; end;
end. end.