1
0
mirror of synced 2024-11-21 10:53:50 +00:00

FS#17 - Log export ability

Fixed: DateTime is now passed around everywhere (to accomodate the Export functionality)
This commit is contained in:
Mark van Renswoude 2014-10-08 12:33:11 +00:00
parent fabf621dc0
commit ca47ad3f4e
15 changed files with 299 additions and 52 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;
{

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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));

View File

@ -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;