Reputation: 111
I've been asked to put something in place into our programs in order to log somewhere what's happening on forms...
Logging something such ...clicked either this or that button, context menu, events fired on components and so on..
This mostly because we are dealing with legacy code, not quite structured unfortunately that we are adjusting/reviewing (Delphi XE10) when possible and we would like to track everything is happening (or most of it) from an user's point of view in order to - being able to have a lead on what the user did when something mysterious happens - being able to review the code where necessary when we have no idea of how thing have happened - eventually speed issues
I'm not talking about exceptions or data logging...those are properly handled.
It's just about the UI.
Do you know if there is any library that might do something similar? If not, how would you try to achieve this?
Code examples are happily accepted (even simple such as one/more form/s with a few buttons etc)
Thanks all!!!
Upvotes: 0
Views: 957
Reputation: 3043
You could develop a non visual component and drop it on every form, but this would have to be a very complex component that either uses RTTI or peeks at windows messages to try to listen to what is happening to the other visual components in the form.
I see two simpler ways to do at least part of what you are asking...
Develop a component (somewhat simpler than the one I hinted at above) that hooks up to the events of the control classes you're interested in, logs the calls, and then forwards them to the original eventhandler procedures (if any).
Suppose you are interested in TWinControl.OnEnter
and OnExit
, TButton.OnClick
and TEdit.OnChange
...
TEventLogger = Class(TComponent)
Private
Procedure Hookup(Const EventName: String; Const Component: TComponent; Const LoggingHandler: TNotifyEvent; Var Handler: TNotifyEvent);
Public
Procedure Setup(Const Container: TComponent);
Procedure HandleChange(Sender: TObject);
Procedure HandleClick(Sender: TObject);
Procedure HandleEnter(Sender: TObject);
Procedure HandleExit(Sender: TObject);
End;
Procedure TEventLoggerHookup(Const EventName: String; Const Component: TComponent; Const LoggingHandler: TNotifyEvent; Var Handler: TNotifyEvent);
Begin
if Assigned(Handler) Then
Handlers.AddObject(Component.Name + ';' + EventName, Handler);
Handler := LoggingHandler;
End;
Procedure TEventLogger.Setup(Const Container: TComponent);
Var
i: Integer;
c: TComponent;
Begin
For i:=0 to Container.ComponentCount - 1 Do Begin
c := Container.Components[i];
if c Is TWinControl Then Begin
Hookup('Enter',HandleEnter,TWinControl(c).OnEnter;
Hookup('Exit',HandleExit,TWinControl(c).OnExit;
End;
If c Is TButton Then
Hookup('Click',HandleClick,TButton(c).OnClick;
If c Is TEdit Then
Hookup('Change',HandleChange,TEdit(c).OnChange;
End;
End;
Procedure TEventLogger.Procedure HandleChange(Sender: TObject);
Var
s: String;
i: Integer;
e: TNotifyEvent;
Begin
With Sender As TComponent Do Begin
s := Name + ';Change';
Log(s);
i:= Handlers.IndexOf(s);
If i <> -1 Then Begin
e := Handlers.Objects[i];
e(Sender);
End;
End;
End;
This is not tested, but I think you got the point. Things to keep in mind:
TStringList
... but you could do better with a generic TDictionary<Key,Value>
. Of course you should create it and destroy it appropriately.TNotifyEvents
. But you can easily overload the Hookup procedure to handle other types ov events.Derive a new class of visual component for each one used that you want to log. All you need to define for each of these is a Log method and override the specific "event" methods your're interested in. For example:
TLogButton = class(TButton)
private
procedure Log(Const Event: String);
protected
procedure Click; override;
end;
TLogButton.Click;
begin
Log('Click');
Inherited;
end;
You could then implement the Log method adding all the info you need (form and component class and name, or others). If you want to do something complex, then you will be better off delegating the log operation to a specialized class.
This is also not tested. Things to keep in mind:
TButton
in TLogButton
... in both pas and dfm files (I do hope
you are using text dfm, if not you better convert them to text). Overall... I'd probably go with the latter solution. It is basically easier to implement on a single class of controls, and you could probably already get some very useful tracing of user activity with just a bunch of classes. The former solution is a bit more sophisticated, and could probably be extended to cover some situations... but it is harder to test throughly and it is too risky if you do not know your code base very well.
Upvotes: 1
Reputation: 24086
As a lightweight solution you can use OutputDebugString(PChar('My logtext')), or TFile.AppendAllText('log.txt','My logtext').
If you want fancy logging with special viewers, thread-safe, etc, you could include some special logging framework. In that case it's better search online and compare features yourself instead of asking for an opinion here.
Upvotes: 1