Dynamics NAV/BC – Events Stack Logging

This post shows you how I created a logging system to know when, from where and by whom an event was called.

The following solution can be used in Dynamics NAV 2017 or later because it uses TryFunctions and the GETLASTERRORCALLSTACK command.

(In Dynamics NAV 2016 TryFunction is available but I’m not sure if GETLASTERRORCALLSTACK is).

I’ve created a new table with the following structure to keep the logging information:

Table Key: Stack Entry No.,Line No.

To populate this table I’ve created a new codeunit. I could/should have two codeunits (one with the subscribers and the other one with the logging functionality) but this is just for demo purposes.

The codeunit has three methods plus as many subscribers as you want to log:

InsertStackLogging() -> To add a new stack logging record to the logging table;

[TryFunction] ErrorTryFunction() -> Local. A TryFunction to raise an error that will be used to get the error call stack. It is inside a try so it won’t stop the running transaction;

SplitLine() -> Local. Used by the InsertStackLogging to split the result from the GETLASTERRORCALLSTACK command in more that one stack field;

Then you may have as many subscribers as you want to log. You only need to have a single line in the subscriber method:

InsertStackLogging;

For testing purposes I’ve subscribed the OnBeforeInsertEvent in SalesShipmentHeader and I’ve posted a sales order ship.

Here’s the output:

Please let me know if you have any doubts.

Below the objects:

OBJECT Table 50000 Stack Logging
{
OBJECT-PROPERTIES
{
Date=13/05/19;
Time=17:57:12;
Modified=Yes;
Version List=RPM;
}
PROPERTIES
{
}
FIELDS
{
{ 1 ; ;Stack Entry No. ;Integer ;DataClassification=ToBeClassified }
{ 2 ; ;User ID ;Code50 ;TableRelation=User."User Name";
OnLookup=VAR
UserMgt@1000 : Codeunit 418;
BEGIN
UserMgt.LookupUserID("User ID");
END;

TestTableRelation=No;
DataClassification=EndUserIdentifiableInformation;
CaptionML=ENU=User ID }
{ 3 ; ;Stack 1 ;Text250 ;DataClassification=ToBeClassified }
{ 4 ; ;Stack 2 ;Text250 ;DataClassification=ToBeClassified }
{ 5 ; ;Stack 3 ;Text250 ;DataClassification=ToBeClassified }
{ 6 ; ;Stack 4 ;Text250 ;DataClassification=ToBeClassified }
{ 7 ; ;Log Date ;Date ;DataClassification=ToBeClassified }
{ 8 ; ;Log Time ;Time ;DataClassification=ToBeClassified }
{ 9 ; ;Line No. ;Integer ;DataClassification=ToBeClassified }
}
KEYS
{
{ ;Stack Entry No.,Line No. ;Clustered=Yes }
}
FIELDGROUPS
{
}
CODE
{

BEGIN
END.
}
}

 

OBJECT Codeunit 50000 Logging Codeunit
{
OBJECT-PROPERTIES
{
Date=13/05/19;
Time=17:57:19;
Modified=Yes;
Version List=RPM;
}
PROPERTIES
{
OnRun=BEGIN
END;
}
CODE
{
LOCAL PROCEDURE InsertStackLogging@1();
VAR
StackLogging@1001 : Record 50000;
LastCallStack@1000 : Text;
SplittedStack@1002 : Text[1024];
NextLineNo@1003 : Integer;
NextStackEntryNo@1004 : Integer;
BEGIN
CLEARLASTERROR;
IF NOT ErrorTryFunction THEN BEGIN

WITH StackLogging DO BEGIN
RESET;
NextStackEntryNo := 1;
IF FINDLAST THEN
NextStackEntryNo := "Stack Entry No." + 1;
END;

NextLineNo := 10000;

LastCallStack := GETLASTERRORCALLSTACK;

REPEAT

SplittedStack := SplitLine(LastCallStack,'\');

WITH StackLogging DO BEGIN
INIT;
"Stack Entry No." := NextStackEntryNo;
"Line No." := NextLineNo;
NextLineNo += 10000;

"Stack 1" := COPYSTR(SplittedStack,1,250);
"Stack 2" := COPYSTR(SplittedStack,251,250);
"Stack 3" := COPYSTR(SplittedStack,501,250);
"Stack 4" := COPYSTR(SplittedStack,751,250);

"User ID" := USERID;
"Log Date" := TODAY;
"Log Time" := TIME;
INSERT;
END;
UNTIL LastCallStack = '';
END;
END;

[TryFunction]
LOCAL PROCEDURE ErrorTryFunction@3();
BEGIN
ERROR('');
END;

LOCAL PROCEDURE SplitLine@4(VAR Text@1000 : Text[1024];Separator@1002 : Text[1]) Token : Text[1024];
VAR
Pos@1003 : Integer;
BEGIN
Pos := STRPOS(Text,Separator);
IF Pos > 0 THEN BEGIN
Token := COPYSTR(Text,1,Pos-1);
IF Pos+1 <= STRLEN(Text) THEN
Text := COPYSTR(Text,Pos+1)
ELSE
Text := '';
END ELSE BEGIN
Token := Text;
Text := '';
END;
END;

LOCAL PROCEDURE "-- Subscribers"@5();
BEGIN
END;

[EventSubscriber(Table,110,OnBeforeInsertEvent)]
LOCAL PROCEDURE LogStack_OnBeforeInsertEvent_SalesShipmentHeader@2(VAR Rec@1000 : Record 110;RunTrigger@1001 : Boolean);
BEGIN
InsertStackLogging;
END;

BEGIN
END.
}
}


Feel free to leave your comments or ask me questions.
Share this

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.