Enabling custom End User tracing in your application
|Date:||8th December 2009|
|Product/Release:||Visual LANSA SP5 (RDMLX)|
|Abstract:||How make use of a Visual LANSA tracing feature in RDMLX applications|
|Submitted By:||LANSA Technical Support|
Visual LANSA has a tracing feature that you can embed in your own RDMLX applications. This can be added to your source during development or included when trying to determine the cause of complex issues where debugging is simply too longwinded or complicated.
Firstly, you need to create an object to act as your trace handler. An example and explanation of a typical trace handler is included later. This object implements the iTraceHandler interface, which has the following methods.
- Initialize – Called when the trace handler is installed as the active trace handler for the application
- Terminate – Called whenever the trace handler is uninstalled
- TraceMessage – Called whenever a either the TraceMessageText or TraceMessageData methods are called on Sys_Appln
- TracingState – Called whenever a either the TraceMessageText or TraceMessageData methods are called on Sys_Appln
Implementation of this interface means that a component can be installed as the system trace handler at run time.
Using the Trace Handler
Once you have defined your trace handler, in this case as a component called Tracing, it can be added to your application. This can be done as part of the initialization with a simple Define_Com as below
Define_Com Class(#Tracing) name(#TraceHandler)
If you are using the handler provided in the example, you need do nothing else. It is a self contained object that will set itself as the system trace handler if a suitable trigger condition is satisfied; in this case the existence of TracingOn.txt in the system source folder.You are now ready to start adding tracing commands to your application source.
There are two methods available on Sys_Appln; TraceMessageText & TraceMessageData
TraceMessageText has 4 variant input parameters. When used the TraceMessage method in the handler will be executed once for each of the variables specified.
#Sys_Appln.TraceMessageText(#Empno #Givename #Surname)
TraceMessageData has a Source parameter and a further 9 Value parameters. The Value parameters will be substituted in to the source parameters as if the Substitute intrinsic were being used.
#Sys_Appln.TraceMessageText(“Employee &1 Name &2 &3” #Empno #Givename #Surname)
Trace Handler Guidelines and Performance
Quite how many trace statements to include and what data to output is entirely up to you. Clearly writing a trace statement after every command is counterproductive and will produce enormous trace files, but then sporadically placed commands with no real structure will be of little use.
However, there are moments during the execution of an application that might be considered typical points of interest, for example, after a record has been read or before an update. If a subroutine or method is about to be called to manipulate some data, it may be beneficial to trace values before and after.
You might also consider flagging trace entries with some form of code to identify the nature of the processing going on at the time. This will greatly improve your understanding of the output without having to necessarily be overly familiar with the source itself.
#Sys_Appln.TraceMessageText(“UPDATE Employee &1 Name &2 &3” #Empno #Givename #Surname)
The trace feature is designed specifically such that the commands can be left in place. If the trigger for tracing is not in place, no trace handler is installed. The result is that when the call to the trace method is made there is nothing to do. This is equivalent to calling an empty method and while there must be a minor overhead it is negligible. Simple tests demonstrate that 100,000 trace statements add about a quarter of a second to the run time. If tracing is active, the same added about 45 seconds to the total execution time.
Example Use of Tracing
This is a simple form to demonstrate how the results of using both tracing options. It uses a trace handler defined from the sample supplied. To ensure that output is produced, create TracingOn.txt in the system folder (...\x_win95\x_lansa). Trace files will also be created in the system folder.
Function Options(*DIRECT) Begin_Com Role(*EXTENDS #PRIM_FORM) Caption('Doubleclick an item to test the tracing') Clientheight(322) Clientwidth(626) Height(358) Left(213) Top(160) Width(642) Define_Com Class(#PRIM_LTVW) Name(#List) Componentversion(2) Displayposition(1) Fullrowselect(True) Height(249) Keyboardpositioning(SortColumn) Left(0) Parent(#COM_OWNER) Showsortarrow(True) Tabposition(1) Top(0) Width(625) Define_Com Class(#PRIM_LVCL) Name(#LVCL_3) Displayposition(1) Parent(#List) Source(#EMPNO) Define_Com Class(#PRIM_LVCL) Name(#LVCL_4) Displayposition(2) Parent(#List) Source(#GIVENAME) Width(36) Define_Com Class(#PRIM_LVCL) Name(#LVCL_5) Displayposition(3) Parent(#List) Source(#SURNAME) Width(41) Define_Com Class(#PRIM_RDBN) Name(#TraceMessageData) Buttonchecked(True) Caption('Use TraceMessageData') Displayposition(2) Left(8) Parent(#COM_OWNER) Tabposition(2) Top(254) Width(153) Define_Com Class(#PRIM_RDBN) Name(#TraceMessageText) Caption('Use TraceMessageText') Displayposition(3) Left(8) Parent(#COM_OWNER) Tabposition(3) Top(286) Width(153) Define_Com Class(#Tracing) Name(#TraceHandler) Evtroutine Handling(#com_owner.CreateInstance) Set Com(#com_owner) Caption(*component_desc) Clr_List Named(#List) Select Fields(#List) From_File(Pslmst) Add_Entry To_List(#List) Endselect Endroutine Evtroutine Handling(#List.DoubleClick) If (#TraceMessageData.ButtonChecked) #sys_appln.TraceMessageData( "Employee &1 Name &2 &3" #Empno #Givename #Surname ) Else #sys_appln.TraceMessageText( #Empno #Givename #Surname ) Endif Endroutine End_Com
Example Trace Handler
This is a simple self contained trace handler. If a text file called TracingOn.txt exists in the ...\X_win95\lansa folder, the trace handler will install itself at run time and a trace file will be created. Any trace statements coded in to the application will be output.
Cut and paste the source in to a new reusable part and save to ensure all errors disappear.
Function Options(*DIRECT) Begin_Com Role(*EXTENDS #PRIM_OBJT *implements #prim_app.iTraceHandler) Define Field(#FilHandle) Type(*dec) Length(3) Decimals(0) Define_Com Class(#prim_alph) Name(#Tab) Evtroutine Handling(#Com_Owner.CreateInstance) #Tab := (09).asChar #Com_owner.InstallTracing Endroutine Mthroutine Name(InstallTracing) Help('Plug in the trace handler to the Application') Access(*private) * This example uses a text file as the trigger to implement user tracing. * You might also use a registry entry If (#Com_owner.FileExists( (*Sys_dir + "TracingOn.txt") )) * Set this object as the system help handler #Sys_appln.TraceHandler <= #Com_owner Endif Endroutine Mthroutine Name(Initialize) Options(*redefine) Access(*private) #Com_owner.OpenTraceFile Endroutine Mthroutine Name(Terminate) Options(*redefine) Access(*private) #Com_owner.CloseTraceFile Endroutine Mthroutine Name(TraceMessage) Help('Executed whenever #sys_appln.TraceMessageData or #sys_appln.TraceMessageText is used') Options(*redefine) Access(*private) #Com_owner.WriteToFile( #ComponentName #Description #LineNumber #MessageText ) Endroutine Mthroutine Name(OpenTraceFile) Help('Create a new trace outputfile') Access(*private) Use Builtin(Stm_File_Open) With_Args(#Com_owner.GetNextFile Append N Y) To_Get(#FilHandle #io$sts) Endroutine Mthroutine Name(WriteToFile) Help('Write an entry in the trace output file') Access(*private) Define_Map For(*Input) Class(#prim_alph) Name(#ComponentName) Define_Map For(*Input) Class(#prim_alph) Name(#Description) Define_Map For(*Input) Class(#prim_nmbr) Name(#LineNumber) Define_Map For(*Input) Class(#prim_alph) Name(#MessageText) Define_Com Class(#prim_Dat) Name(#Now) #MessageText := #Now.now.AsLocalizedDateTime.AsString + #Tab + #ComponentName + #Tab + #LineNumber.asstring + #Tab + #MessageText Use Builtin(Stm_File_Write) With_Args(#FilHandle #MessageText) To_Get(#io$sts) Endroutine Mthroutine Name(CloseTraceFile) Access(*private) Use Builtin(Stm_File_Close) With_Args(#FilHandle) To_Get(#io$sts) Endroutine Mthroutine Name(TracingState) Options(*redefine) #MessageTracingActive := True Endroutine Mthroutine Name(GetNextFile) Access(*Private) Define_Map For(*Result) Class(#Prim_alph) Name(#Result) Define_Com Class(#prim_nmbr) Name(#Extension) Begin_Loop Using(#Extension) #Result := *Sys_dir + "UserTrace" + "." + #Extension.asstring.rightAdjust( 3 "0" ) Leave If(*Not #Com_owner.FileExists( #Result )) End_Loop Endroutine Mthroutine Name(FileExists) Access(*private) Define_Map For(*input) Class(#prim_alph) Name(#Path) Define_Map For(*result) Class(#prim_boln) Name(#Result) Use Builtin(OV_FILE_SERVICE) With_Args(Check_File #Path) To_Get(#io$sts) #Result := (#io$sts = OK) Endroutine End_Com