Blog

All Blog Posts  |  Next Post  |  Previous Post

Audit Log using TMS Aurelius events

Friday, April 24, 2015

The latest release of TMS Aurelius introduces an event system that allows you to subscribe listeners to some events that might be fired while you are using Aurelius, especially the TObjectManager.

One key difference between regular Delphi events and Aurelius events is that the latter are multicast events, meaning you can add listeners (handlers) to the events without worrying if you are replacing a listener that was already set to event. This way it's possible to create "plugins" to Aurelius that perform additional logic. For example you can easily add code that will be executed whenever an entity is inserted in the database:
TMappingExplorer.Default.Events.OnInserted.Subscribe(
  procedure(Args: TInsertedArgs)
  begin
    // Use Args.Entity to retrieve the inserted entity
  end
);

One very common use of that feature is implementing Audit Trail, a mechanism where you can log every entity (or database record, if you prefer to see it that way) that is created (inserted), deleted or modified (updated). The Music Library demo included in TMS Aurelius distribution was updated to include a simple Audit Log Viewer that illustrates how to use the events.

In the demo, the Audit Log Viewer just listen to the events and log them in a memo component in a form. You can enable/disable the logging. In real applications, you will just log the modifications to another place, like a text log file, or even the database itself, using Aurelius, if you prefer.

TMS Software Delphi  Components

You can check the demo for the full source code. In this blog post, I will show only the relevant parts, for the OnInserted and OnUpdated events. Other parts of the code even inside procedures were removed for simplicity. Here is how we subscribe to the events:
  TfmAuditLogViewer = class(TForm)
  private
    FInsertedProc: TInsertedProc;
    FUpdatedProc: TUpdatedProc;
    procedure InsertedHandler(Args: TInsertedArgs);
    procedure UpdatedHandler(Args: TUpdatedArgs);
    procedure SubscribeListeners;
    procedure UnsubscribeListeners;
    {...}
  end;

constructor TfmAuditLogViewer.Create(AOwner: TComponent);
begin
  inherited;
  FInsertedProc := InsertedHandler;
  FUpdatedProc := UpdatedHandler;
end;

procedure TfmAuditLogViewer.SubscribeListeners;
var
  E: TManagerEvents;
begin
  E := TMappingExplorer.Default.Events;
  E.OnInserted.Subscribe(FInsertedProc);
  E.OnUpdated.Subscribe(FUpdatedProc);
end;


Note that we set the method reference in field variables so that we can later unsubscribe them if we want to:

procedure TfmAuditLogViewer.UnsubscribeListeners;
var
  E: TManagerEvents;
begin
  E := TMappingExplorer.Default.Events;
  E.OnInserted.Unsubscribe(FInsertedProc);
  E.OnUpdated.Unsubscribe(FUpdatedProc);
end;


And here is how we implemented our event listeners:

procedure TfmAuditLogViewer.InsertedHandler(Args: TInsertedArgs);
begin
  Log(Format('Inserted: %s', [EntityDesc(Args.Entity, Args.Manager)]));
  BreakLine;
end;

procedure TfmAuditLogViewer.UpdatedHandler(Args: TUpdatedArgs);
var
  Pair: TPair<string, Variant>;
  OldValue: Variant;
begin
  Log(Format('Updated: %s', [EntityDesc(Args.Entity, Args.Manager)]));
  for Pair in Args.NewColumnValues do
    if not (Args.OldColumnValues.TryGetValue(Pair.Key, OldValue) and (OldValue = Pair.Value)) then
      Log(Format('   %s Changed from %s to %s',
        [Pair.Key, TUtils.VariantToString(OldValue), TUtils.VariantToString(Pair.Value)]));
  BreakLine;
end;


Some different methods are called from those event handlers, but they are just helper methods. EntityDesc just retrieves a string representation of the entity being logged (class name and id), and Log and BreakLine just add text to the memo component:

function TfmAuditLogViewer.EntityDesc(Entity, Manager: TObject): string;
var
  IdValue: Variant;
  IdString: string;
begin
  IdValue :=  TObjectManager(Manager).Explorer.GetIdValue(Entity);
  IdString := TUtils.VariantToString(IdValue);
  Result := Format('%s(%s)', [Entity.ClassName, IdString]);
end;

procedure TfmAuditLogViewer.Log(const S: string);
begin
  Memo.Lines.Add(S);
end;

procedure TfmAuditLogViewer.BreakLine;
begin
  Memo.Lines.Add('================================================');
end;


After playing with Music Library demo for a while, adding and updating entities, we have our audit log results:

TMS Software Delphi  Components



Wagner Landgraf




This blog post has received 2 comments.


1. Saturday, March 19, 2022 at 11:55:21 PM

This event control works fine as a Windows version. If I create an event for mobile devices (Android) in this way, I do not get an info window displayed. Instead of "Log" I set the "Toast" function to output the info window. Has anyone ever used the Aurelius Events in Android?

Stapel Andreas


2. Sunday, March 20, 2022 at 12:50:31 PM

It should work fine. In any case, use the Support Center to ask technical questions, it''s the best place: https://support.tmssoftware.com.

Wagner Landgraf




Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.



All Blog Posts  |  Next Post  |  Previous Post