More documentation and example use.
This is a set of macros used to aid with debugging code by logging messages at set points in the code.
How often have you written code the logs where your code has got to and trace variable values?
write "at this point x="_x,!
Then you have to remove them later.
These macros replace this sort of thing with:
$$$DEBUG("at this point x="_x)
When your code is run the macros will do nothing unless you have turned debug on. To do this call the macro:
$$$DEBUGNew("")
Once this macro is run the debugging macro will be active.
zpm "install pxw-debug"
set ^PXW.Debuggers("ENABLED")=1 ; enable the macros
This will install:
Object | Use |
---|---|
PXW.Debuggers.Macros.inc | Include this in classes that need to be debugged and the test harness. |
PXW.Debuggers.Basic.cls | A basic debugger object, output goes to tha current device. The macros will make use of this. |
PXW.Debuggers.BasicToFile.cls | Extends the Basic debug to send the results to a file. |
PXW.Debuggers.Console.cls | Extends the Basic debug to send the results to the system console log. |
The macros only work if ^PXW.Debuggers(“ENABLED”)=1.
The $$$DEBUGNew macro sets a %variable to an object.
The $$$DEBUG macro checks the variable is set, if it is then calls the .DEBUG() method of the object.
The $$$DEBUGNew(””) macro by default sets the object to PXW.Debuggers.Basic which will just write the message to the current device.
You could, instead, use another debug log object that creates a file and writes the message there.
$$$DEBUGNew("PXW.Debuggers.BasicToFile")
See the class itself to see how to control where the files are written.
You could put your debug messages to anywhere you like simply by creating a subclass of PXW.Debuggers.Basic and overwrite the DEBUG method. Eg. it would be very simple to log the messages in a global or table.
This is best used in a test harness where the test sets up the debug to use and calls the method being tested. The output from the debug can be checked. When the method runs for real no debug output will be generated because the debug will not be on.
The test harness:
Include PXW.Debuggers.Macros
classmethod RunTest(DebugTo="")
{
if DebugTo="SCREEN" {
set deblog="PXW.Debuggers.Basic"
} elseif DebugTo="FILE" {
; You may pass in an object to the New macro giving
; more control of the debug object.
set deblog=##class(PXW.Debuggers.BasicToFile).%New()
set deblog.AddDateTime=1
} elseif DebugTo="CONSOLE" {
set deblog="PXW.Debuggers.Console"
} else {
set deblog=""
}
if deblog'="" $$$DEBUGNew(deblog)set object=##class(PXW.Debuggers.UnitTests.Example).%New() do object.MethodContainingMacros() if $$$debugIsON zw $$$debugObject $$$DEBUGStop
}
The code where logging is required:
Method MethodContainingMacros()
{
$$$DEBUGMethodBegin
write "running method",!
for x=1:1:10 {
$$$DEBUG("x="_x)
}
write "ending method",!
$$$DEBUGMethodEnd
}
If you had a method (A) that uses debugging and that calls another method (B) that also uses debugging then you might want to pause debugging while the method B is running.
EG.
Class Program1 ClassMethod Method A() { $$$DEBUG("Calling B") set result=##class(Program2).B() $$$DEBUG("The result of B is "_result) }
Class Program2
ClassMethod B() as %Integer {
for i=1:1:100 {
$$$DEBUG("i="_i)
}
quit i
}
This would result in the debug output:
Calling B
i=1
i=2
...
i=100
The result of B is 100
The 100 lines of text output by B gets in the way of what you are actually trying to debug. To resolve this you can pause debugging:
ClassMethod A() {
$$$DEBUG("Calling B")
$$$DEBUGPause
set result=##class(Program2).B()
$$$DEBUGResume
$$$DEBUG("The result of B is "_result)
}
These macros would generally be used in the code being developed. They should be treated as commands.
Macro | Description |
---|---|
DEBUG(message) | Record the given message to the debug log. |
DEBUGMethodBegin | Record the the start of the current method to the debug log. |
DEBUGMethodEnd | Record the the end of the current method to the debug log. |
DEBUGStack | Record the stack to the debug log. |
DEBUGBreak | Break if debugging. |
DEBUGSC(sc,exp) | Set sc to the given expression, if the result is an error, log it to the debug log. |
These macros would be used in the test harness to switch on debugging. They should be treated as commands.
Macro | Description |
---|---|
DEBUGNew(class ) | Create a NEWed variable containing an instance of the given class. When the method quits the debug session will end. |
DEBUGSetup(class) | Create a variable instance of the given class. The class could also be a pre-defined object, where complex setup of the debug session is required. |
DEBUGStop | Stops the debug session. |
DEBUGPause | Pause the debug session. It can be resumed later. |
DEBUGResume | Resume a previously paused debug. |
These macros are used internally but may be useful to the test harness in certain situations. They are expressions, not commands.
Macro | Description |
---|---|
debugIsON | Test if the debugging is on. |
debugObject | The variable containing the debug object. |
Because the marcos are constantly checking for the existence of an object even when there is no active debug logging they take a little bit of time.
Once all the debugging is done you can disable all the macros with:
SET ^PXW.Debuggers("ENABLED")=0
or
KILL ^PXW.Debuggers("ENABLED")
Then recompile everything. The macros will then not compile any code and will add that little bit more speed.
Note to self: After a bit of to-ing and fro-ing I decided on ENABLING rather than DISABLING, but that may change… The current thinking is: debug would be ENABLED in a dev environment. When the code is delivered to a new environment (eg live) the debugging will not be enabled and no debug code will be generated.
Make sure you have git and Docker desktop installed.
Clone/git pull the repo into any local directory
$ git clone https://github.com/pxw-paul/pxw-debug.git
Open the terminal in this directory and call the command to build and run InterSystems IRIS in container:
Note: Users running containers on a Linux CLI, should use “docker compose” instead of “docker-compose”
See Install the Compose plugin
$ docker-compose up -d
To open IRIS Terminal do:
$ docker-compose exec iris iris session iris -U IRISAPP
IRISAPP>
To exit the terminal, do any of the following:
Enter HALT or H (not case-sensitive)
Use ZPM to run the tests
zpm:IRISAPP>test pxw-debug
[IRISAPP|pxw-debug] Reload START (/home/irisowner/dev/)
[IRISAPP|pxw-debug] Reload SUCCESS
[pxw-debug] Module object refreshed.
[IRISAPP|pxw-debug] Validate START
[IRISAPP|pxw-debug] Validate SUCCESS
[IRISAPP|pxw-debug] Compile START
[IRISAPP|pxw-debug] Compile SUCCESS
[IRISAPP|pxw-debug] Activate START
[IRISAPP|pxw-debug] Configure START
[IRISAPP|pxw-debug] Configure SUCCESS
[IRISAPP|pxw-debug] Activate SUCCESS
[IRISAPP|pxw-debug] Test START
Use the following URL to view the result:
http://172.23.0.4:52773/csp/sys/%25UnitTest.Portal.Indices.cls?Index=5&$NAMESPACE=IRISAPP
All PASSED
[IRISAPP|pxw-debug] Test SUCCESS
The test cover the basic and the basic to file classes.