5

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.

NGLN
  • 43,011
  • 8
  • 105
  • 200
Yuriy
  • 257
  • 5
  • 12
  • 2
    You have all those features (and also additional features, like exception tracing, method profiling, thread-safety or an optional per-thread logging which may be interesting) in [our OpenSource `TSynLog` class](http://blog.synopse.info/post/2011/04/14/Enhanced-logging-in-SynCommons). For Delphi 5 up to XE2. Including a log viewer, and a set-based level (not one global level, but a set of customized levels). – Arnaud Bouchez Nov 15 '11 at 11:57
  • 2
    @Arnaud Isn't that an answer? – NGLN Nov 15 '11 at 12:09
  • See also: [Logging and Synchronization](http://stackoverflow.com/questions/659094), [Which logging library is better?](http://stackoverflow.com/questions/72983) and all other past [Delphi logging](http://stackoverflow.com/search?q=%5Bdelphi%5D+logging) related Q&A. – NGLN Nov 15 '11 at 14:23
  • If many applications need to be logged, maybe with concurrent sessions, logging to a central socket-based (UDP) server can help to keep all logs in one file – mjn Nov 16 '11 at 11:51
  • @Yura You asked three questions and have not accepted an answer in these three questions. Do you know you can accpet the most helpful answer? This gives both you and the person who answered reputation and makes the question more useful to others. – jpfollenius Nov 16 '11 at 22:08
  • @ArnaudBouchez - links to TSynLog no longer valid. – Toby Dec 06 '21 at 18:45

2 Answers2

15

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.

jpfollenius
  • 16,456
  • 10
  • 90
  • 156
4

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

gabr
  • 26,580
  • 9
  • 75
  • 141