diff --git a/NamedPipeClient/X2LogNamedPipeClient.dpr b/NamedPipeClient/X2LogNamedPipeClient.dpr index 2734716..bbdfc32 100644 --- a/NamedPipeClient/X2LogNamedPipeClient.dpr +++ b/NamedPipeClient/X2LogNamedPipeClient.dpr @@ -2,7 +2,9 @@ program X2LogNamedPipeClient; uses // FastMM4, + System.Classes, Vcl.Forms, + Vcl.Imaging.jpeg, X2Log.Intf, X2Log.Client.NamedPipe, X2Log.Observer.MonitorForm; @@ -13,6 +15,9 @@ var client: IX2LogObservable; begin + { To deserialize the graphic, make sure GetClass succeeds } + RegisterClass(TJPEGImage); + ReportMemoryLeaksOnShutdown := True; Application.Initialize; diff --git a/Test/source/MainFrm.dfm b/Test/source/MainFrm.dfm index 7b565f6..97fda3f 100644 --- a/Test/source/MainFrm.dfm +++ b/Test/source/MainFrm.dfm @@ -228,8 +228,6 @@ object MainForm: TMainForm Caption = 'Monitor Form Observer' TabOrder = 0 OnClick = btnMonitorFormClick - ExplicitLeft = 151 - ExplicitTop = -6 end object btnLock: TButton AlignWithMargins = True @@ -246,7 +244,6 @@ object MainForm: TMainForm Caption = 'Lock' TabOrder = 2 OnClick = btnLockClick - ExplicitLeft = 149 end object btnUnlock: TButton AlignWithMargins = True @@ -263,7 +260,6 @@ object MainForm: TMainForm Caption = 'Unlock' TabOrder = 3 OnClick = btnUnlockClick - ExplicitLeft = 359 end end object pcDispatch: TPageControl @@ -337,6 +333,15 @@ object MainForm: TMainForm Text = 'Hello world!' OnKeyDown = edtMessageKeyDown end + object btnCategory: TButton + Left = 416 + Top = 39 + Width = 101 + Height = 21 + Caption = 'With category' + TabOrder = 5 + OnClick = btnCategoryClick + end end object tsException: TTabSheet Caption = 'Exception' @@ -458,7 +463,7 @@ object MainForm: TMainForm Left = 552 Top = 176 Bitmap = { - 494C01010200140048000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 494C01010200140050000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 0000000000003600000028000000300000000C00000001002000000000000009 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 diff --git a/Test/source/MainFrm.pas b/Test/source/MainFrm.pas index 2016f03..a9a2376 100644 --- a/Test/source/MainFrm.pas +++ b/Test/source/MainFrm.pas @@ -61,6 +61,7 @@ type lbNamedPipeServers: TListBox; btnLock: TButton; btnUnlock: TButton; + btnCategory: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); @@ -81,13 +82,14 @@ type procedure btnBinaryRawByteStringClick(Sender: TObject); procedure btnGraphicClick(Sender: TObject); procedure btnNamedPipeRefreshClick(Sender: TObject); + procedure btnCategoryClick(Sender: TObject); private FLog: IX2Log; FEventObserver: IX2LogObserver; FFileObserver: IX2LogObserver; FNamedPipeObserver: IX2LogObserver; 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; @@ -149,13 +151,13 @@ begin 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 text: string; logDetailsText: IX2LogDetailsText; begin - text := GetLogLevelText(Level) + ': ' + Msg; + text := GetLogLevelText(Level) + ': ' + Category + ': ' + Msg; if Supports(Details, IX2LogDetailsText, logDetailsText) then text := text + ' (' + logDetailsText.AsString + ')'; @@ -183,6 +185,7 @@ begin end; end; + procedure TMainForm.btnCloseClick(Sender: TObject); begin Close; @@ -243,6 +246,12 @@ begin end; +procedure TMainForm.btnCategoryClick(Sender: TObject); +begin + FLog.Category('Test').Info(edtMessage.Text); +end; + + procedure TMainForm.btnMonitorFormClick(Sender: TObject); begin TX2LogObserverMonitorForm.ShowInstance(FLog); diff --git a/X2Log.Client.Base.pas b/X2Log.Client.Base.pas index 16c7e40..5e8d5fa 100644 --- a/X2Log.Client.Base.pas +++ b/X2Log.Client.Base.pas @@ -19,8 +19,8 @@ type destructor Destroy; override; { IX2LogBase } - procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); overload; virtual; - procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; 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; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload; virtual; { IX2LogObservable } procedure Attach(AObserver: IX2LogObserver); @@ -64,23 +64,23 @@ begin end; -procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); +procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails); var observer: IX2LogObserver; begin for observer in Observers do - observer.Log(ALevel, AMessage, ADetails); + observer.Log(ALevel, AMessage, ACategory, ADetails); 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 observer: IX2LogObserver; begin for observer in Observers do - observer.Log(ALevel, ADateTime, AMessage, ADetails); + observer.Log(ALevel, ADateTime, AMessage, ACategory, ADetails); end; end. diff --git a/X2Log.Client.NamedPipe.pas b/X2Log.Client.NamedPipe.pas index 169a89d..2715280 100644 --- a/X2Log.Client.NamedPipe.pas +++ b/X2Log.Client.NamedPipe.pas @@ -345,6 +345,7 @@ var detailsSize: Cardinal; detailsStream: TMemoryStream; serializer: IX2LogDetailsSerializer; + category: string; begin if MessageData.Size > 0 then @@ -365,6 +366,9 @@ begin end else if headerDiff < 0 then raise EReadError.Create('Header too small'); + { Category } + category := TStreamUtil.ReadString(MessageData); + { Message } msg := TStreamUtil.ReadString(MessageData); @@ -393,7 +397,7 @@ begin end; end; - Client.Log(header.Level, header.DateTime, msg, details); + Client.Log(header.Level, header.DateTime, msg, category, details); except on E:EReadError do ClosePipe; diff --git a/X2Log.Constants.pas b/X2Log.Constants.pas index 52bd8d1..09b6b94 100644 --- a/X2Log.Constants.pas +++ b/X2Log.Constants.pas @@ -11,6 +11,11 @@ resourcestring LogLevelWarning = 'Warning'; LogLevelError = 'Error'; + LogCategorySeparator = ' - '; + + // Note: currently not translatable + LogCategoryDefault = ''; + { X2Log.Observer.LogFile @@ -36,6 +41,7 @@ resourcestring { Caption of the columns in the live log view } LogMonitorFormColumnTime = 'Time'; + LogMonitorFormColumnCategory = 'Category'; LogMonitorFormColumnMessage = 'Message'; { Caption of the toolbar buttons } diff --git a/X2Log.Global.pas b/X2Log.Global.pas index d1098ad..5e4d180 100644 --- a/X2Log.Global.pas +++ b/X2Log.Global.pas @@ -24,8 +24,10 @@ type class procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy); { Facade for IX2LogBase } - class procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); overload; - class procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; 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; 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 VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil); @@ -82,15 +84,21 @@ begin end; -class procedure TX2GlobalLog.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); +class function TX2GlobalLog.Category(const ACategory: string): IX2Log; begin - Instance.Log(ALevel, AMessage, ADetails); + Result := Instance.Category(ACategory); 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 - 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; diff --git a/X2Log.Intf.pas b/X2Log.Intf.pas index e6e0ad3..3bdb134 100644 --- a/X2Log.Intf.pas +++ b/X2Log.Intf.pas @@ -48,8 +48,8 @@ type { Logging } IX2LogBase = interface ['{1949E8DC-6DC5-43DC-B678-55CF8274E79D}'] - procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); overload; - procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; 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; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload; end; @@ -75,6 +75,8 @@ type ['{A6FF38F9-EDA8-4C76-9C95-2C0317560D78}'] 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); @@ -102,6 +104,8 @@ type { Payload: + CategoryLength: Cardinal + Category: WideString MessageLength: Cardinal Message: WideString DetailsLength: Cardinal @@ -109,6 +113,7 @@ type } end; + TX2LogMessageHeader = TX2LogMessageHeaderV1; const diff --git a/X2Log.Observer.Custom.pas b/X2Log.Observer.Custom.pas index d2a8b1c..82dd49d 100644 --- a/X2Log.Observer.Custom.pas +++ b/X2Log.Observer.Custom.pas @@ -15,15 +15,15 @@ type private FLogLevels: TX2LogLevels; 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; public constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); { IX2LogBase } - procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); overload; - procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; 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; const ACategory: string = ''; ADetails: IX2LogDetails = nil); overload; end; @@ -39,16 +39,16 @@ begin end; -procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); +procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails); begin - Log(ALevel, Now, AMessage, ADetails); + Log(ALevel, Now, AMessage, ACategory, ADetails); 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 if ALevel in LogLevels then - DoLog(ALevel, ADateTime, AMessage, ADetails); + DoLog(ALevel, ADateTime, AMessage, ACategory, ADetails); end; end. diff --git a/X2Log.Observer.CustomThreaded.pas b/X2Log.Observer.CustomThreaded.pas index 2dd83ff..fa68a07 100644 --- a/X2Log.Observer.CustomThreaded.pas +++ b/X2Log.Observer.CustomThreaded.pas @@ -20,7 +20,7 @@ type protected 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; public @@ -34,9 +34,10 @@ type FDetails: IX2LogDetails; FLevel: TX2LogLevel; FDateTime: TDateTime; + FCategory: string; FMessage: string; 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; procedure Assign(Source: TPersistent); override; @@ -44,6 +45,7 @@ type property DateTime: TDateTime read FDateTime; property Details: IX2LogDetails read FDetails; property Level: TX2LogLevel read FLevel; + property Category: string read FCategory; property Message: string read FMessage; end; @@ -71,7 +73,7 @@ type constructor Create; 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; @@ -97,20 +99,21 @@ begin 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 - WorkerThread.Log(ALevel, ADateTime, AMessage, ADetails); + WorkerThread.Log(ALevel, ADateTime, AMessage, ACategory, ADetails); end; { 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 inherited Create; FLevel := ALevel; FDateTime := ADateTime; + FCategory := ACategory; FMessage := AMessage; FDetails := ADetails; end; @@ -135,6 +138,7 @@ begin FLevel := entrySource.Level; FDateTime := entrySource.DateTime; + FCategory := entrySource.Category; FMessage := entrySource.Message; FDetails := entrySource.Details; end else @@ -168,11 +172,11 @@ begin 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 TMonitor.Enter(LogQueue); try - LogQueue.Enqueue(TX2LogQueueEntry.Create(ALevel, ADateTime, AMessage, ADetails)); + LogQueue.Enqueue(TX2LogQueueEntry.Create(ALevel, ADateTime, AMessage, ACategory, ADetails)); finally TMonitor.Exit(LogQueue); end; diff --git a/X2Log.Observer.Event.pas b/X2Log.Observer.Event.pas index b41635f..c24486c 100644 --- a/X2Log.Observer.Event.pas +++ b/X2Log.Observer.Event.pas @@ -7,7 +7,7 @@ uses 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) @@ -15,7 +15,7 @@ type FOnLog: TX2LogEvent; FRunInMainThread: Boolean; 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 constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload; constructor Create(AOnLog: TX2LogEvent; ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload; @@ -48,7 +48,7 @@ begin 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 if Assigned(FOnLog) then begin @@ -58,10 +58,10 @@ begin procedure begin if Assigned(FOnLog) then - FOnLog(Self, ALevel, ADateTime, AMessage, ADetails); + FOnLog(Self, ALevel, ADateTime, AMessage, ACategory, ADetails); end); end else - FOnLog(Self, ALevel, ADateTime, AMessage, ADetails); + FOnLog(Self, ALevel, ADateTime, AMessage, ACategory, ADetails); end; end; diff --git a/X2Log.Observer.MonitorForm.dfm b/X2Log.Observer.MonitorForm.dfm index e198f86..47262d4 100644 --- a/X2Log.Observer.MonitorForm.dfm +++ b/X2Log.Observer.MonitorForm.dfm @@ -19,7 +19,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm PixelsPerInch = 96 TextHeight = 13 object splDetails: TSplitter - Left = 602 + Left = 682 Top = 0 Width = 6 Height = 496 @@ -29,9 +29,9 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm ExplicitHeight = 519 end object pnlDetails: TPanel - Left = 608 + Left = 688 Top = 0 - Width = 382 + Width = 302 Height = 496 Align = alRight BevelOuter = bvNone @@ -39,7 +39,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm object tbDetails: TToolBar Left = 0 Top = 0 - Width = 382 + Width = 302 Height = 22 AutoSize = True ButtonWidth = 80 @@ -76,7 +76,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm object pnlBorder: TPanel Left = 0 Top = 22 - Width = 382 + Width = 302 Height = 474 Align = alClient BevelKind = bkFlat @@ -85,21 +85,21 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm object hcDetails: THeaderControl Left = 0 Top = 0 - Width = 378 + Width = 298 Height = 19 Sections = < item AutoSize = True ImageIndex = -1 Text = 'Details' - Width = 378 + Width = 298 end> NoSizing = True end object reDetails: TRichEdit Left = 0 Top = 19 - Width = 378 + Width = 298 Height = 451 Align = alClient BorderStyle = bsNone @@ -118,7 +118,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm object sbDetailsImage: TScrollBox Left = 0 Top = 19 - Width = 378 + Width = 298 Height = 451 HorzScrollBar.Tracking = True VertScrollBar.Tracking = True @@ -141,7 +141,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm object pnlLog: TPanel Left = 0 Top = 0 - Width = 602 + Width = 682 Height = 496 Align = alClient BevelOuter = bvNone @@ -149,10 +149,10 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm object vstLog: TVirtualStringTree Left = 0 Top = 22 - Width = 602 + Width = 682 Height = 474 Align = alClient - Header.AutoSizeIndex = 2 + Header.AutoSizeIndex = 3 Header.Font.Charset = DEFAULT_CHARSET Header.Font.Color = clWindowText Header.Font.Height = -11 @@ -182,14 +182,19 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm end item Position = 2 - Width = 428 + Width = 150 + WideText = 'Category' + end + item + Position = 3 + Width = 354 WideText = 'Message' end> end object tbLog: TToolBar Left = 0 Top = 0 - Width = 602 + Width = 682 Height = 22 AutoSize = True ButtonWidth = 67 @@ -216,7 +221,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm Left = 116 Top = 0 Width = 46 - Height = 13 + Height = 22 Caption = ' Filter: ' Layout = tlCenter end @@ -267,7 +272,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm Left = 448 Top = 48 Bitmap = { - 494C01010A004000EC0010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 494C01010A004000F40010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 0000000000003600000028000000400000003000000001002000000000000030 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 diff --git a/X2Log.Observer.MonitorForm.pas b/X2Log.Observer.MonitorForm.pas index 3e6c343..b575518 100644 --- a/X2Log.Observer.MonitorForm.pas +++ b/X2Log.Observer.MonitorForm.pas @@ -183,8 +183,8 @@ type procedure Unlock; { IX2LogObserver } - procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); overload; - procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; 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, ACategory: string; ADetails: IX2LogDetails); overload; property FreeOnClose: Boolean read FFreeOnClose write FFreeOnClose; property MaxEntries: Cardinal read FMaxEntries write FMaxEntries; @@ -212,10 +212,11 @@ type Time: TDateTime; Paused: Boolean; Level: TX2LogLevel; + Category: string; Message: string; 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; PLogEntryNodeData = ^TLogEntryNodeData; @@ -224,17 +225,19 @@ type const ColumnLevel = 0; ColumnTime = 1; - ColumnMessage = 2; + ColumnCategory = 2; + ColumnMessage = 3; LevelImageIndex: array[TX2LogLevel] of TImageIndex = (0, 1, 2, 3); { 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 Self.Time := ADateTime; Self.Paused := APaused; Self.Level := ALevel; + Self.Category := ACategory; Self.Message := AMessage; Self.Details := ADetails; end; @@ -345,6 +348,7 @@ begin vstLog.NodeDataSize := SizeOf(TLogEntryNodeData); vstLog.Header.Columns[ColumnTime].Text := GetLogResourceString(@LogMonitorFormColumnTime); + vstLog.Header.Columns[ColumnCategory].Text := GetLogResourceString(@LogMonitorFormColumnCategory); vstLog.Header.Columns[ColumnMessage].Text := GetLogResourceString(@LogMonitorFormColumnMessage); mmMainFile.Caption := GetLogResourceString(@LogMonitorFormMenuFile); @@ -456,13 +460,13 @@ begin end; -procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); +procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; const AMessage, ACategory: string; ADetails: IX2LogDetails); begin - Log(ALevel, Now, AMessage, ADetails); + Log(ALevel, Now, AMessage, ACategory, ADetails); 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 node: PVirtualNode; nodeData: PLogEntryNodeData; @@ -486,7 +490,7 @@ begin { BeginUpdate causes OnInitNode to be triggered on-demand, moved Initialize call here } 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); @@ -762,7 +766,7 @@ begin for node in vstLog.Nodes do begin 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; @@ -790,6 +794,9 @@ begin ColumnTime: CellText := DateTimeToStr(nodeData^.Time); + ColumnCategory: + CellText := nodeData^.Category; + ColumnMessage: CellText := nodeData^.Message; end; diff --git a/X2Log.Observer.NamedPipe.pas b/X2Log.Observer.NamedPipe.pas index 8ba5308..d19c4a8 100644 --- a/X2Log.Observer.NamedPipe.pas +++ b/X2Log.Observer.NamedPipe.pas @@ -220,6 +220,9 @@ begin WriteBuffer.WriteBuffer(header, SizeOf(header)); + { Category } + TStreamUtil.WriteString(WriteBuffer, AEntry.Category); + { Message } TStreamUtil.WriteString(WriteBuffer, AEntry.Message); diff --git a/X2Log.Translations.Dutch.pas b/X2Log.Translations.Dutch.pas index e6b7575..27601da 100644 --- a/X2Log.Translations.Dutch.pas +++ b/X2Log.Translations.Dutch.pas @@ -19,6 +19,7 @@ begin SetLogResourceString(@LogMonitorFormCaption, '%s - Live Log'); SetLogResourceString(@LogMonitorFormColumnTime, 'Tijd'); + SetLogResourceString(@LogMonitorFormColumnCategory, 'Categorie'); SetLogResourceString(@LogMonitorFormColumnMessage, 'Melding'); SetLogResourceString(@LogMonitorFormButtonClear, 'Wissen'); diff --git a/X2Log.pas b/X2Log.pas index 37a0278..5ffe986 100644 --- a/X2Log.pas +++ b/X2Log.pas @@ -11,10 +11,19 @@ uses 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 FExceptionStrategy: IX2LogExceptionStrategy; - private + protected + { IX2LogExceptionCategory } + procedure Exception(AException: Exception; const AMessage: string = ''; const ACategory: string = ''); overload; + property ExceptionStrategy: IX2LogExceptionStrategy read FExceptionStrategy; public constructor Create; @@ -22,6 +31,49 @@ type { IX2Log } 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 VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil); @@ -36,10 +88,12 @@ type procedure Exception(AException: Exception; const AMessage: string = ''); end; - + + implementation uses + X2Log.Constants, X2Log.Details.Default, X2Log.Exception.Default; @@ -62,55 +116,67 @@ begin end; +function TX2Log.Category(const ACategory: string): IX2Log; +begin + Result := TX2LogCategoryDecorator.Create(Self, ACategory); +end; + + procedure TX2Log.Verbose(const AMessage, ADetails: string); begin - Log(TX2LogLevel.Verbose, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); + Log(TX2LogLevel.Verbose, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); end; procedure TX2Log.VerboseEx(const AMessage: string; ADetails: IX2LogDetails); begin - Log(TX2LogLevel.Verbose, AMessage, ADetails); + Log(TX2LogLevel.Verbose, AMessage, LogCategoryDefault, ADetails); end; procedure TX2Log.Info(const AMessage, ADetails: string); begin - Log(TX2LogLevel.Info, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); + Log(TX2LogLevel.Info, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); end; procedure TX2Log.InfoEx(const AMessage: string; ADetails: IX2LogDetails); begin - Log(TX2LogLevel.Info, AMessage, ADetails); + Log(TX2LogLevel.Info, AMessage, LogCategoryDefault, ADetails); end; procedure TX2Log.Warning(const AMessage, ADetails: string); begin - Log(TX2LogLevel.Warning, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); + Log(TX2LogLevel.Warning, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); end; procedure TX2Log.WarningEx(const AMessage: string; ADetails: IX2LogDetails); begin - Log(TX2LogLevel.Warning, AMessage, ADetails); + Log(TX2LogLevel.Warning, AMessage, LogCategoryDefault, ADetails); end; procedure TX2Log.Error(const AMessage, ADetails: string); begin - Log(TX2LogLevel.Error, AMessage, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); + Log(TX2LogLevel.Error, AMessage, LogCategoryDefault, TX2LogStringDetails.CreateIfNotEmpty(ADetails)); end; procedure TX2Log.ErrorEx(const AMessage: string; ADetails: IX2LogDetails); begin - Log(TX2LogLevel.Error, AMessage, ADetails); + Log(TX2LogLevel.Error, AMessage, LogCategoryDefault, ADetails); end; procedure TX2Log.Exception(AException: Exception; const AMessage: string); +begin + Exception(AException, AMessage, LogCategoryDefault); +end; + + +procedure TX2Log.Exception(AException: Exception; const AMessage, ACategory: string); var msg: string; details: IX2LogDetails; @@ -120,7 +186,139 @@ begin details := nil; 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.