My task is to create a class which will gather user activity around several applications.
Let's say I have a class TLogging
and a global object called Logging
.
User activity (screen open, etc...) should be collected in memory (maybe put into a (string)list of TLogging
) and saved into log file after some time interval (each 10 minutes), or when application is closed.
The most important is that logging must be in "silent mode", it mustn't affect user workflow in any way: no screen hanging, no exceptions.
Please give me the direction for this task.
This is a very broad question that touches several areas. A few hints:
At least consider an established logging framework for this. Newer Delphi versions come with CodeSite. SmartInspect is another alternative.
Use synchronization primitives to make your class thread-safe: TCriticalSection, TMREWSync
Make sure you understand the issues involved in multithreading and synchronization before attempting to write a thread-safe logging framework. A good start is Martin Harvey's guide Multithreading - The Delphi Way.
Use a background thread that flushes the log content to disk in regular intervals or if enough data has been buffered.
Ask more specific questions here on SO if you encounter specific problems.
Using OmniThreadLibrary and assuming that the Logging object is a singleton, this is really simple. I would also limit maximum number of messages waiting to be written so that internal queue cannot use too much memory.
const
CMaxMsgCount = 1000;
CMaxLogTimeout_ms = 10{min}*60{sec/min}*1000{ms/sec};
type
TLogging = class
strict private
FLogMsgCount: IOmniResourceCount;
FLogQueue: IOmniBlockingCollection;
FWriter: IOmniTaskControl;
strict protected
procedure Logger(const task: IOmniTask);
public
constructor Create;
destructor Destroy;
procedure Log(const msg: string);
end;
var
Logging: TLogging;
constructor TLogging.Create;
begin
FLogMsgCount := CreateResourceCount(CMaxMsgCount);
FLogQueue := TOmniBlockingCollection.Create;
FWriter := CreateTask(Logger, 'Logger').Run;
end;
destructor TLogging.Destroy;
begin
FWriter.Terminate;
end;
procedure TLogging.Log(const msg: string);
begin
FLogQueue.Add(msg);
FLogMsgCount.Allocate;
end;
procedure TLogging.Logger(const task: IOmniTask);
procedure Flush;
var
logData: TOmniValue;
begin
// open file, possibly with retry
while FLogQueue.TryTake(logData) do begin
FLogMsgCount.Release;
// write logData.AsString
end;
// close file
end;
begin
while DSiWaitForTwoObjects(task.TerminateEvent, FLogMsgCount.Handle, false, CMaxLogTimeout_ms) <> WAIT_OBJECT_0 do
Flush;
Flush;
end;
(Disclaimer: "It compiles on my machine", otherwise untested.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With