Home Applications pxw-debug

pxw-debug

InterSystems does not provide technical support for this project. Please contact its developer for the technical assistance.
5
1 reviews
0
Awards
65
Views
6
IPM installs
0
0
Details
Releases (3)
Reviews (1)
Issues
Log debug output

What's new in this version

More documentation and example use.

pxw-debug

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.

Install via ZPM.

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.

How does it work?

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.

What happens to the message?

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.

Debug to other places.

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.

Example usage.

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
}

Pause/Resume.

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)
}

Full list of macros.

Logging.

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.

Control.

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.

Others.

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.

Finally.

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.

Docker.

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)

Running unit tests.

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.

Made with
Install
zpm install pxw-debug download archive
Version
1.0.208 Mar, 2025
ObjectScript quality test
Category
Developer Environment
Works with
InterSystems IRIS
First published
06 Oct, 2024
Last edited
08 Mar, 2025
Last checked by moderator
31 Jan, 2025Works