FS#17 - Log export ability
Fixed: DateTime is now passed around everywhere (to accomodate the Export functionality)
This commit is contained in:
parent
fabf621dc0
commit
ca47ad3f4e
@ -11,7 +11,6 @@ uses
|
||||
|
||||
var
|
||||
client: IX2LogObservable;
|
||||
observerForm: TX2LogObserverMonitorForm;
|
||||
|
||||
begin
|
||||
ReportMemoryLeaksOnShutdown := True;
|
||||
@ -22,8 +21,12 @@ begin
|
||||
|
||||
client := TX2LogNamedPipeClient.Create('X2LogTest');
|
||||
try
|
||||
observerForm := TX2LogObserverMonitorForm.Instance(client);
|
||||
observerForm.ShowModal;
|
||||
with TX2LogObserverMonitorForm.Create(nil, client) do
|
||||
try
|
||||
ShowModal;
|
||||
finally
|
||||
Free;
|
||||
end;
|
||||
finally
|
||||
client := nil;
|
||||
end;
|
||||
|
@ -87,7 +87,7 @@ type
|
||||
FFileObserver: IX2LogObserver;
|
||||
FNamedPipeObserver: IX2LogObserver;
|
||||
protected
|
||||
procedure DoLog(Sender: TObject; Level: TX2LogLevel; const Msg: string; Details: IX2LogDetails);
|
||||
procedure DoLog(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg: string; Details: IX2LogDetails);
|
||||
end;
|
||||
|
||||
|
||||
@ -149,7 +149,7 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; const Msg: string; Details: IX2LogDetails);
|
||||
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg: string; Details: IX2LogDetails);
|
||||
var
|
||||
text: string;
|
||||
logDetailsText: IX2LogDetailsText;
|
||||
|
@ -19,8 +19,8 @@ type
|
||||
destructor Destroy; override;
|
||||
|
||||
{ IX2LogBase }
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); virtual;
|
||||
|
||||
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;
|
||||
|
||||
{ IX2LogObservable }
|
||||
procedure Attach(AObserver: IX2LogObserver);
|
||||
@ -73,5 +73,15 @@ begin
|
||||
observer.Log(ALevel, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
var
|
||||
observer: IX2LogObserver;
|
||||
|
||||
begin
|
||||
for observer in Observers do
|
||||
observer.Log(ALevel, ADateTime, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -22,7 +22,7 @@ type
|
||||
end;
|
||||
|
||||
|
||||
TX2LogNamedPipeClient = class(TX2LogBaseClient, IX2LogBase)
|
||||
TX2LogNamedPipeClient = class(TX2LogBaseClient)
|
||||
private
|
||||
FWorkerThread: TThread;
|
||||
protected
|
||||
@ -393,7 +393,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
Client.Log(header.Level, msg, details);
|
||||
Client.Log(header.Level, header.DateTime, msg, details);
|
||||
except
|
||||
on E:EReadError do
|
||||
ClosePipe;
|
||||
|
@ -43,15 +43,29 @@ resourcestring
|
||||
LogMonitorFormButtonPause = 'Pause';
|
||||
LogMonitorFormButtonCopyDetails = 'Copy';
|
||||
LogMonitorFormButtonSaveDetails = 'Save';
|
||||
LogMonitorFormButtonWordWrapDetails = 'Word wrap';
|
||||
|
||||
LogMonitorFormButtonFilter = 'Filter:';
|
||||
|
||||
{ Caption of the menu items which are not on the toolbar }
|
||||
LogMonitorFormMenuFile = 'File';
|
||||
LogMonitorFormMenuFileSaveAs = 'Save as...';
|
||||
LogMonitorFormMenuFileClose = 'Close';
|
||||
LogMonitorFormMenuLog = 'Log';
|
||||
LogMonitorFormMenuDetails = 'Details';
|
||||
LogMonitorFormMenuWindow = 'Window';
|
||||
LogMonitorFormMenuWindowAlwaysOnTop = 'Always on top';
|
||||
|
||||
|
||||
{ Status messages }
|
||||
LogMonitorFormStatusPaused = 'Paused: %d log message(s) skipped';
|
||||
|
||||
{ Filter for Save details buttons }
|
||||
LogMonitorFormSaveDetailsFilter = 'All files (*.*)|*.*';
|
||||
|
||||
{ Filter for Save as menu item }
|
||||
LogMonitorFormSaveDetailsSaveAs = 'Log files (*.log)|*.log|All files (*.*)|*.*';
|
||||
|
||||
|
||||
function GetLogLevelText(ALogLevel: TX2LogLevel): string;
|
||||
|
||||
|
@ -24,7 +24,8 @@ type
|
||||
class procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||
|
||||
{ Facade for IX2LogBase }
|
||||
class procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
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 Verbose(const AMessage: string; const ADetails: string = '');
|
||||
class procedure VerboseEx(const AMessage: string; ADetails: IX2LogDetails = nil);
|
||||
@ -87,6 +88,12 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
class procedure TX2GlobalLog.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
Instance.Log(ALevel, ADateTime, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
class procedure TX2GlobalLog.Verbose(const AMessage, ADetails: string);
|
||||
begin
|
||||
Instance.Verbose(AMessage, ADetails);
|
||||
|
@ -49,6 +49,7 @@ type
|
||||
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;
|
||||
end;
|
||||
|
||||
|
||||
@ -95,6 +96,7 @@ type
|
||||
ID: Word;
|
||||
Version: Byte;
|
||||
Size: Word;
|
||||
DateTime: TDateTime;
|
||||
Level: TX2LogLevel;
|
||||
|
||||
{
|
||||
|
@ -15,14 +15,15 @@ type
|
||||
private
|
||||
FLogLevels: TX2LogLevels;
|
||||
protected
|
||||
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); virtual; abstract;
|
||||
procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: 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);
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails = nil); overload;
|
||||
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails = nil); overload;
|
||||
end;
|
||||
|
||||
|
||||
@ -39,9 +40,15 @@ end;
|
||||
|
||||
|
||||
procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
Log(ALevel, Now, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
if ALevel in LogLevels then
|
||||
DoLog(ALevel, AMessage, ADetails);
|
||||
DoLog(ALevel, ADateTime, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -20,7 +20,7 @@ type
|
||||
protected
|
||||
function CreateWorkerThread: TX2LogObserverWorkerThread; virtual; abstract;
|
||||
|
||||
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); override;
|
||||
procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); override;
|
||||
|
||||
property WorkerThread: TX2LogObserverWorkerThread read FWorkerThread;
|
||||
public
|
||||
@ -33,13 +33,15 @@ type
|
||||
private
|
||||
FDetails: IX2LogDetails;
|
||||
FLevel: TX2LogLevel;
|
||||
FDateTime: TDateTime;
|
||||
FMessage: string;
|
||||
public
|
||||
constructor Create(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); overload;
|
||||
constructor Create(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); overload;
|
||||
constructor Create(AEntry: TX2LogQueueEntry); overload;
|
||||
|
||||
procedure Assign(Source: TPersistent); override;
|
||||
|
||||
property DateTime: TDateTime read FDateTime;
|
||||
property Details: IX2LogDetails read FDetails;
|
||||
property Level: TX2LogLevel read FLevel;
|
||||
property Message: string read FMessage;
|
||||
@ -51,6 +53,7 @@ type
|
||||
FFileName: string;
|
||||
FLogQueue: TObjectQueue<TX2LogQueueEntry>;
|
||||
FLogQueueSignal: TEvent;
|
||||
FThreadStartSignal: TEvent;
|
||||
protected
|
||||
procedure Execute; override;
|
||||
procedure TerminatedSet; override;
|
||||
@ -68,7 +71,7 @@ type
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
end;
|
||||
|
||||
|
||||
@ -94,19 +97,20 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogCustomThreadedObserver.DoLog(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
procedure TX2LogCustomThreadedObserver.DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
WorkerThread.Log(ALevel, AMessage, ADetails);
|
||||
WorkerThread.Log(ALevel, ADateTime, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{ TX2LogQueueEntry }
|
||||
constructor TX2LogQueueEntry.Create(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
constructor TX2LogQueueEntry.Create(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FLevel := ALevel;
|
||||
FDateTime := ADateTime;
|
||||
FMessage := AMessage;
|
||||
FDetails := ADetails;
|
||||
end;
|
||||
@ -130,6 +134,7 @@ begin
|
||||
entrySource := TX2LogQueueEntry(Source);
|
||||
|
||||
FLevel := entrySource.Level;
|
||||
FDateTime := entrySource.DateTime;
|
||||
FMessage := entrySource.Message;
|
||||
FDetails := entrySource.Details;
|
||||
end else
|
||||
@ -140,6 +145,7 @@ end;
|
||||
{ TX2LogObserverWorkerThread }
|
||||
constructor TX2LogObserverWorkerThread.Create;
|
||||
begin
|
||||
FThreadStartSignal := TEvent.Create(nil, True, False, '');
|
||||
FLogQueueSignal := TEvent.Create(nil, False, False, '');
|
||||
FLogQueue := TObjectQueue<TX2LogQueueEntry>.Create(True);
|
||||
|
||||
@ -149,18 +155,24 @@ end;
|
||||
|
||||
destructor TX2LogObserverWorkerThread.Destroy;
|
||||
begin
|
||||
{ For very short-lived observers (for example, the "Save as" functionality
|
||||
of the observer form) the WorkerThread can be destroyed before the thread
|
||||
has a chance to properly start and clear out it's queue. }
|
||||
FThreadStartSignal.WaitFor(INFINITE);
|
||||
|
||||
inherited Destroy;
|
||||
|
||||
FreeAndNil(FLogQueue);
|
||||
FreeAndNil(FLogQueueSignal);
|
||||
FreeAndNil(FThreadStartSignal);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverWorkerThread.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
procedure TX2LogObserverWorkerThread.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
TMonitor.Enter(LogQueue);
|
||||
try
|
||||
LogQueue.Enqueue(TX2LogQueueEntry.Create(ALevel, AMessage, ADetails));
|
||||
LogQueue.Enqueue(TX2LogQueueEntry.Create(ALevel, ADateTime, AMessage, ADetails));
|
||||
finally
|
||||
TMonitor.Exit(LogQueue);
|
||||
end;
|
||||
@ -174,17 +186,17 @@ var
|
||||
entry: TX2LogQueueEntry;
|
||||
|
||||
begin
|
||||
FThreadStartSignal.SetEvent;
|
||||
NameThreadForDebugging('TX2LogObserverWorkerThread');
|
||||
|
||||
Setup;
|
||||
try
|
||||
while not Terminated do
|
||||
while True do
|
||||
begin
|
||||
{ When Terminated, flush the queue }
|
||||
if not Terminated then
|
||||
WaitForEntry;
|
||||
|
||||
if Terminated then
|
||||
break;
|
||||
|
||||
entry := nil;
|
||||
TMonitor.Enter(LogQueue);
|
||||
try
|
||||
@ -199,7 +211,8 @@ begin
|
||||
ProcessEntry(entry);
|
||||
finally
|
||||
FreeAndNil(entry);
|
||||
end;
|
||||
end else if Terminated then
|
||||
break;
|
||||
end;
|
||||
finally
|
||||
Cleanup;
|
||||
|
@ -7,7 +7,7 @@ uses
|
||||
|
||||
|
||||
type
|
||||
TX2LogEvent = procedure(Sender: TObject; Level: TX2LogLevel; const Msg: string; Details: IX2LogDetails) of object;
|
||||
TX2LogEvent = procedure(Sender: TObject; Level: TX2LogLevel; DateTime: TDateTime; const Msg: string; Details: IX2LogDetails) of object;
|
||||
|
||||
|
||||
TX2LogEventObserver = class(TX2LogCustomObserver)
|
||||
@ -15,7 +15,7 @@ type
|
||||
FOnLog: TX2LogEvent;
|
||||
FRunInMainThread: Boolean;
|
||||
protected
|
||||
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); override;
|
||||
procedure DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: 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; const AMessage: string; ADetails: IX2LogDetails);
|
||||
procedure TX2LogEventObserver.DoLog(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
if Assigned(FOnLog) then
|
||||
begin
|
||||
@ -58,10 +58,10 @@ begin
|
||||
procedure
|
||||
begin
|
||||
if Assigned(FOnLog) then
|
||||
FOnLog(Self, ALevel, AMessage, ADetails);
|
||||
FOnLog(Self, ALevel, ADateTime, AMessage, ADetails);
|
||||
end);
|
||||
end else
|
||||
FOnLog(Self, ALevel, AMessage, ADetails);
|
||||
FOnLog(Self, ALevel, ADateTime, AMessage, ADetails);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
@ -122,7 +122,7 @@ begin
|
||||
if Supports(AEntry.Details, IX2LogDetailsStreamable, logDetailsStreamable) then
|
||||
begin
|
||||
detailsExtension := ExtractFileExt(FileName);
|
||||
baseReportFileName := ChangeFileExt(FileName, '_' + FormatDateTime(GetLogResourceString(@LogFileNameDateFormat), Now));
|
||||
baseReportFileName := ChangeFileExt(FileName, '_' + FormatDateTime(GetLogResourceString(@LogFileNameDateFormat), AEntry.DateTime));
|
||||
detailsFileName := baseReportFileName + detailsExtension;
|
||||
detailsNumber := 0;
|
||||
|
||||
@ -170,7 +170,7 @@ begin
|
||||
{ Append line to log file }
|
||||
writer := TFile.AppendText(FileName);
|
||||
try
|
||||
writer.WriteLine('[' + FormatDateTime(GetLogResourceString(@LogFileLineDateFormat), Now) + '] ' +
|
||||
writer.WriteLine('[' + FormatDateTime(GetLogResourceString(@LogFileLineDateFormat), AEntry.DateTime) + '] ' +
|
||||
GetLogLevelText(AEntry.Level) + ': ' + errorMsg);
|
||||
finally
|
||||
FreeAndNil(writer);
|
||||
|
@ -10,6 +10,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
||||
Font.Height = -11
|
||||
Font.Name = 'Tahoma'
|
||||
Font.Style = []
|
||||
Menu = mmMain
|
||||
OldCreateOrder = False
|
||||
Position = poScreenCenter
|
||||
ShowHint = True
|
||||
@ -181,7 +182,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
||||
end
|
||||
item
|
||||
Position = 2
|
||||
Width = 424
|
||||
Width = 428
|
||||
WideText = 'Message'
|
||||
end>
|
||||
end
|
||||
@ -198,24 +199,24 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
||||
ShowCaptions = True
|
||||
TabOrder = 1
|
||||
OnCustomDraw = ToolbarCustomDraw
|
||||
object tbClear: TToolButton
|
||||
Left = 0
|
||||
Top = 0
|
||||
Action = actClear
|
||||
AutoSize = True
|
||||
end
|
||||
object tbPause: TToolButton
|
||||
Left = 56
|
||||
Left = 0
|
||||
Top = 0
|
||||
Action = actPause
|
||||
AutoSize = True
|
||||
Style = tbsCheck
|
||||
end
|
||||
object tbClear: TToolButton
|
||||
Left = 60
|
||||
Top = 0
|
||||
Action = actClear
|
||||
AutoSize = True
|
||||
end
|
||||
object lblFilter: TLabel
|
||||
Left = 116
|
||||
Top = 0
|
||||
Width = 46
|
||||
Height = 22
|
||||
Height = 13
|
||||
Caption = ' Filter: '
|
||||
Layout = tlCenter
|
||||
end
|
||||
@ -266,7 +267,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
||||
Left = 448
|
||||
Top = 48
|
||||
Bitmap = {
|
||||
494C01010A004000D00010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
494C01010A004000EC0010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
0000000000003600000028000000400000003000000001002000000000000030
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
@ -722,15 +723,97 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
||||
end
|
||||
object actWordWrap: TAction
|
||||
AutoCheck = True
|
||||
Caption = '&Word wrap'
|
||||
Caption = 'Word wrap'
|
||||
Enabled = False
|
||||
ImageIndex = 9
|
||||
OnExecute = actWordWrapExecute
|
||||
end
|
||||
object actClose: TAction
|
||||
Caption = '&Close'
|
||||
OnExecute = actCloseExecute
|
||||
end
|
||||
object actAlwaysOnTop: TAction
|
||||
Caption = 'Always on top'
|
||||
OnExecute = actAlwaysOnTopExecute
|
||||
end
|
||||
object actSaveAs: TAction
|
||||
Caption = '&Save as...'
|
||||
ImageIndex = 5
|
||||
OnExecute = actSaveAsExecute
|
||||
end
|
||||
end
|
||||
object sdDetails: TSaveDialog
|
||||
Options = [ofOverwritePrompt, ofHideReadOnly, ofEnableSizing]
|
||||
Left = 512
|
||||
Top = 112
|
||||
end
|
||||
object mmMain: TMainMenu
|
||||
Images = ilsLog
|
||||
Left = 448
|
||||
Top = 112
|
||||
object mmMainFile: TMenuItem
|
||||
Caption = 'File'
|
||||
object mmMainFileSaveAs: TMenuItem
|
||||
Action = actSaveAs
|
||||
end
|
||||
object mmMainFileSep1: TMenuItem
|
||||
Caption = '-'
|
||||
end
|
||||
object mmMainFileClose: TMenuItem
|
||||
Action = actClose
|
||||
end
|
||||
end
|
||||
object mmMainLog: TMenuItem
|
||||
Caption = 'Log'
|
||||
object mmMainLogPause: TMenuItem
|
||||
Action = actPause
|
||||
AutoCheck = True
|
||||
end
|
||||
object mmMainLogClear: TMenuItem
|
||||
Action = actClear
|
||||
end
|
||||
object mmMainLogSep1: TMenuItem
|
||||
Caption = '-'
|
||||
end
|
||||
object mmMainLogVerbose: TMenuItem
|
||||
Action = actShowVerbose
|
||||
end
|
||||
object mmMainLogInfo: TMenuItem
|
||||
Action = actShowInfo
|
||||
end
|
||||
object mmMainLogWarning: TMenuItem
|
||||
Action = actShowWarning
|
||||
end
|
||||
object mmMainLogError: TMenuItem
|
||||
Action = actShowError
|
||||
end
|
||||
end
|
||||
object mmMainDetails: TMenuItem
|
||||
Caption = 'Details'
|
||||
object mmMainDetailsCopy: TMenuItem
|
||||
Action = actCopyDetails
|
||||
end
|
||||
object mmMainDetailsSave: TMenuItem
|
||||
Action = actSaveDetails
|
||||
end
|
||||
object mmMainDetailsSep1: TMenuItem
|
||||
Caption = '-'
|
||||
end
|
||||
object mmMainDetailsWordWrap: TMenuItem
|
||||
Action = actWordWrap
|
||||
AutoCheck = True
|
||||
end
|
||||
end
|
||||
object mmMainWindow: TMenuItem
|
||||
Caption = 'Window'
|
||||
object mmMainWindowAlwaysOnTop: TMenuItem
|
||||
Action = actAlwaysOnTop
|
||||
end
|
||||
end
|
||||
end
|
||||
object sdSaveAs: TSaveDialog
|
||||
Options = [ofOverwritePrompt, ofHideReadOnly, ofEnableSizing]
|
||||
Left = 512
|
||||
Top = 176
|
||||
end
|
||||
end
|
||||
|
@ -18,7 +18,7 @@ uses
|
||||
Winapi.Messages,
|
||||
|
||||
X2Log.Details.Intf,
|
||||
X2Log.Intf;
|
||||
X2Log.Intf, Vcl.Menus;
|
||||
|
||||
|
||||
const
|
||||
@ -70,6 +70,30 @@ type
|
||||
tbWordWrap: TToolButton;
|
||||
tbDetailsSep1: TToolButton;
|
||||
actWordWrap: TAction;
|
||||
mmMain: TMainMenu;
|
||||
mmMainFile: TMenuItem;
|
||||
mmMainLog: TMenuItem;
|
||||
mmMainDetails: TMenuItem;
|
||||
mmMainWindow: TMenuItem;
|
||||
actClose: TAction;
|
||||
mmMainFileClose: TMenuItem;
|
||||
mmMainLogClear: TMenuItem;
|
||||
mmMainLogPause: TMenuItem;
|
||||
mmMainLogSep1: TMenuItem;
|
||||
mmMainLogVerbose: TMenuItem;
|
||||
mmMainLogInfo: TMenuItem;
|
||||
mmMainLogWarning: TMenuItem;
|
||||
mmMainLogError: TMenuItem;
|
||||
mmMainDetailsCopy: TMenuItem;
|
||||
mmMainDetailsSave: TMenuItem;
|
||||
mmMainDetailsWordWrap: TMenuItem;
|
||||
mmMainDetailsSep1: TMenuItem;
|
||||
actAlwaysOnTop: TAction;
|
||||
mmMainWindowAlwaysOnTop: TMenuItem;
|
||||
actSaveAs: TAction;
|
||||
mmMainFileSep1: TMenuItem;
|
||||
mmMainFileSaveAs: TMenuItem;
|
||||
sdSaveAs: TSaveDialog;
|
||||
|
||||
procedure FormShow(Sender: TObject);
|
||||
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
||||
@ -78,6 +102,7 @@ type
|
||||
procedure vstLogGetHint(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var LineBreakStyle: TVTTooltipLineBreakStyle; var HintText: string);
|
||||
procedure vstLogGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
|
||||
procedure vstLogFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
|
||||
procedure actCloseExecute(Sender: TObject);
|
||||
procedure actClearExecute(Sender: TObject);
|
||||
procedure actCopyDetailsExecute(Sender: TObject);
|
||||
procedure actSaveDetailsExecute(Sender: TObject);
|
||||
@ -88,6 +113,8 @@ type
|
||||
procedure actShowWarningExecute(Sender: TObject);
|
||||
procedure actShowErrorExecute(Sender: TObject);
|
||||
procedure actWordWrapExecute(Sender: TObject);
|
||||
procedure actAlwaysOnTopExecute(Sender: TObject);
|
||||
procedure actSaveAsExecute(Sender: TObject);
|
||||
private class var
|
||||
FInstances: TMonitorFormDictionary;
|
||||
private
|
||||
@ -128,6 +155,8 @@ type
|
||||
procedure SetVisibleDetails(AControl: TControl);
|
||||
procedure SetWordWrap(AValue: Boolean);
|
||||
|
||||
procedure ExportLog(ALog: IX2LogBase);
|
||||
|
||||
property Closed: Boolean read FClosed;
|
||||
property Details: IX2LogDetails read FDetails;
|
||||
property LockCount: Integer read FLockCount;
|
||||
@ -154,7 +183,8 @@ type
|
||||
procedure Unlock;
|
||||
|
||||
{ IX2LogObserver }
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails); overload;
|
||||
procedure Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails); overload;
|
||||
|
||||
property FreeOnClose: Boolean read FFreeOnClose write FFreeOnClose;
|
||||
property MaxEntries: Cardinal read FMaxEntries write FMaxEntries;
|
||||
@ -170,7 +200,8 @@ uses
|
||||
Vcl.Themes,
|
||||
Winapi.Windows,
|
||||
|
||||
X2Log.Constants;
|
||||
X2Log.Constants,
|
||||
X2Log.Observer.LogFile;
|
||||
|
||||
|
||||
{$R *.dfm}
|
||||
@ -184,7 +215,7 @@ type
|
||||
Message: string;
|
||||
Details: IX2LogDetails;
|
||||
|
||||
procedure Initialize(APaused: Boolean; ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
procedure Initialize(APaused: Boolean; ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
end;
|
||||
|
||||
PLogEntryNodeData = ^TLogEntryNodeData;
|
||||
@ -199,9 +230,9 @@ const
|
||||
|
||||
|
||||
{ TLogEntryNode }
|
||||
procedure TLogEntryNodeData.Initialize(APaused: Boolean; ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
procedure TLogEntryNodeData.Initialize(APaused: Boolean; ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
Self.Time := Now;
|
||||
Self.Time := ADateTime;
|
||||
Self.Paused := APaused;
|
||||
Self.Level := ALevel;
|
||||
Self.Message := AMessage;
|
||||
@ -316,12 +347,21 @@ begin
|
||||
vstLog.Header.Columns[ColumnTime].Text := GetLogResourceString(@LogMonitorFormColumnTime);
|
||||
vstLog.Header.Columns[ColumnMessage].Text := GetLogResourceString(@LogMonitorFormColumnMessage);
|
||||
|
||||
mmMainFile.Caption := GetLogResourceString(@LogMonitorFormMenuFile);
|
||||
mmMainLog.Caption := GetLogResourceString(@LogMonitorFormMenuLog);
|
||||
mmMainDetails.Caption := GetLogResourceString(@LogMonitorFormMenuDetails);
|
||||
mmMainWindow.Caption := GetLogResourceString(@LogMonitorFormMenuWindow);
|
||||
|
||||
actSaveAs.Caption := GetLogResourceString(@LogMonitorFormMenuFileSaveAs);
|
||||
actClose.Caption := GetLogResourceString(@LogMonitorFormMenuFileClose);
|
||||
actClear.Caption := GetLogResourceString(@LogMonitorFormButtonClear);
|
||||
actPause.Caption := GetLogResourceString(@LogMonitorFormButtonPause);
|
||||
actCopyDetails.Caption := GetLogResourceString(@LogMonitorFormButtonCopyDetails);
|
||||
actSaveDetails.Caption := GetLogResourceString(@LogMonitorFormButtonSaveDetails);
|
||||
actAlwaysOnTop.Caption := GetLogResourceString(@LogMonitorFormMenuWindowAlwaysOnTop);
|
||||
|
||||
sdDetails.Filter := GetLogResourceString(@LogMonitorFormSaveDetailsFilter);
|
||||
sdSaveAs.Filter := GetLogResourceString(@LogMonitorFormSaveDetailsSaveAs);
|
||||
|
||||
lblFilter.Caption := ' ' + GetLogResourceString(@LogMonitorFormButtonFilter) + ' ';
|
||||
actShowVerbose.Caption := GetLogLevelText(TX2LogLevel.Verbose);
|
||||
@ -417,6 +457,12 @@ end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; const AMessage: string; ADetails: IX2LogDetails);
|
||||
begin
|
||||
Log(ALevel, Now, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.Log(ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; ADetails: IX2LogDetails);
|
||||
var
|
||||
node: PVirtualNode;
|
||||
nodeData: PLogEntryNodeData;
|
||||
@ -440,7 +486,7 @@ begin
|
||||
{ BeginUpdate causes OnInitNode to be triggered on-demand,
|
||||
moved Initialize call here }
|
||||
Initialize(nodeData^);
|
||||
nodeData^.Initialize(Paused, ALevel, AMessage, ADetails);
|
||||
nodeData^.Initialize(Paused, ALevel, ADateTime, AMessage, ADetails);
|
||||
|
||||
vstLog.IsVisible[node] := (not Paused) and (ALevel in VisibleLevels);
|
||||
|
||||
@ -484,6 +530,7 @@ end;
|
||||
procedure TX2LogObserverMonitorForm.UpdateUI;
|
||||
begin
|
||||
actClear.Enabled := (vstLog.RootNodeCount > 0);
|
||||
actSaveAs.Enabled := (vstLog.RootNodeCount > 0);
|
||||
end;
|
||||
|
||||
|
||||
@ -706,6 +753,20 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.ExportLog(ALog: IX2LogBase);
|
||||
var
|
||||
node: PVirtualNode;
|
||||
nodeData: PLogEntryNodeData;
|
||||
|
||||
begin
|
||||
for node in vstLog.Nodes do
|
||||
begin
|
||||
nodeData := vstLog.GetNodeData(node);
|
||||
ALog.Log(nodeData^.Level, nodeData^.Time, nodeData^.Message, nodeData^.Details);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.vstLogFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
||||
var
|
||||
nodeData: PLogEntryNodeData;
|
||||
@ -787,6 +848,12 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.actCloseExecute(Sender: TObject);
|
||||
begin
|
||||
Close;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.actClearExecute(Sender: TObject);
|
||||
begin
|
||||
vstLog.Clear;
|
||||
@ -889,6 +956,33 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.actAlwaysOnTopExecute(Sender: TObject);
|
||||
begin
|
||||
actAlwaysOnTop.Checked := not actAlwaysOnTop.Checked;
|
||||
|
||||
if actAlwaysOnTop.Checked then
|
||||
SetWindowPos(Self.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE)
|
||||
else
|
||||
SetWindowPos(Self.Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.actSaveAsExecute(Sender: TObject);
|
||||
var
|
||||
logFile: IX2LogBase;
|
||||
|
||||
begin
|
||||
if sdSaveAs.Execute then
|
||||
begin
|
||||
{ Default behaviour of the LogFile observer is to append }
|
||||
System.SysUtils.DeleteFile(sdSaveAs.FileName);
|
||||
|
||||
logFile := TX2LogFileObserver.Create(sdSaveAs.FileName, X2LogLevelsAll);
|
||||
ExportLog(logFile);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverMonitorForm.ToolbarCustomDraw(Sender: TToolBar; const ARect: TRect; var DefaultDraw: Boolean);
|
||||
var
|
||||
element: TThemedElementDetails;
|
||||
|
@ -216,6 +216,7 @@ begin
|
||||
header.Version := X2LogMessageVersion;
|
||||
header.Size := SizeOf(header);
|
||||
header.Level := AEntry.Level;
|
||||
header.DateTime := AEntry.DateTime;
|
||||
|
||||
WriteBuffer.WriteBuffer(header, SizeOf(header));
|
||||
|
||||
|
@ -25,9 +25,22 @@ begin
|
||||
SetLogResourceString(@LogMonitorFormButtonPause, 'Pauzeren');
|
||||
SetLogResourceString(@LogMonitorFormButtonCopyDetails, 'Kopiëren');
|
||||
SetLogResourceString(@LogMonitorFormButtonSaveDetails, 'Opslaan');
|
||||
SetLogResourceString(@LogMonitorFormButtonWordWrapDetails, 'Terugloop');
|
||||
|
||||
SetLogResourceString(@LogMonitorFormButtonFilter, 'Filter:');
|
||||
|
||||
SetLogResourceString(@LogMonitorFormMenuFile, 'Bestand');
|
||||
SetLogResourceString(@LogMonitorFormMenuFileSaveAs, 'Opslaan als...');
|
||||
SetLogResourceString(@LogMonitorFormMenuFileClose, 'Sluiten');
|
||||
SetLogResourceString(@LogMonitorFormMenuLog, 'Log');
|
||||
SetLogResourceString(@LogMonitorFormMenuDetails, 'Details');
|
||||
SetLogResourceString(@LogMonitorFormMenuWindow, 'Venster');
|
||||
SetLogResourceString(@LogMonitorFormMenuWindowAlwaysOnTop, 'Altijd op voorgrond');
|
||||
|
||||
SetLogResourceString(@LogMonitorFormStatusPaused, 'Gepauzeerd: %d melding(en) overgeslagen');
|
||||
|
||||
SetLogResourceString(@LogMonitorFormSaveDetailsFilter, 'Alle bestanden (*.*)|*.*');
|
||||
SetLogResourceString(@LogMonitorFormSaveDetailsSaveAs, 'Log bestanden (*.log)|*.log|Alle bestanden (*.*)|*.*');
|
||||
end;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user