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 |
Description:
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.
Prim_App.iTraceHandler Interface
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
|