Article
· Feb 26 1m read

How Do You Display %Status Error in Terminal Or Let's Have a New Helpful Terminal Alias!

Hi colleagues!

Yet another time I figured that there is no super-simple way to display error from %Status variable, but I need it relatively often in a terminal.

Yes, I know about $$$ Macro, but they are not superhelpful in a terminal.

My usual behavior is to try to remember by heart or copy from somewhere the formula:

USER>w $System.Status.DisplayError(st)

So introduced the alias:

:alias err d $System.Status.DisplayError($1) ;

so, if you get something like:

USER>set st=##class(bla.bla).methodbla()

you can get the error as:

USER>:err st
d $System.Status.DisplayError(st) ;

ERROR #879: Target role NewRole does not exist.

Hope you like it!

All aliases

Discussion (15)2
Log in or sign up to continue

You could also use the language extension feature: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls.... The advantage is that everyone using the instance can use it. Someone sent this to me a very long time ago as an example:

ZZE(Status) public {
if $get(Status)="?" {w !,"ZZE(Status): Decode and display the parsed error" q}
Set $ZT="zzderr"
i '$D(Status) {
i $d(%objlasterror) {
d $SYSTEM.Status.DisplayError(%objlasterror)
} else {
;Don't display anything if no error
}
} else {
d $SYSTEM.Status.DisplayError(Status)
d $SYSTEM.Status.DecomposeStatus(Status,.err)
zw err
}
Quit
zzderr Set $ZT="" Ztrap $ZUTIL(96,3,$ZUTIL(96,0)) ; Throws error to caller
}

I wrote my own class that does some fun, magical stuff. Here's the class:

/// This class can be used to make a status code do some basic handling on its own when it becomes an error status.
/// For example:<br /><br />
/// set status = ##class(DH.WatchedStatus).New("RW")<br />
/// set status.sc = (some method that returns a status here)<br /><br />
/// If the method returns an error status, it will immediately be written, and the status will change back to $$$OK.<br />
/// Note that if ..sc is not set back to an $$$OK status, either automatically or manually, error handling will not trigger again on the next error.
Class DH.WatchedStatus Extends %RegisteredObject
{

/// This is the status code to be watched.
Property sc As %Status [ InitialExpression = 1, SqlComputeCode = {set {*} = ##class(DH.WatchedStatus).Reset({sc},{resetSelf})}, SqlComputed, SqlComputeOnChange = isErr ];

/// Used to track if the status code is an error. This is necessary for some shenanigans with SQLComputeCode between this flag and the status code.
Property isErr As %Boolean [ InitialExpression = 0, SqlComputeCode = {set {*} = ##class(DH.WatchedStatus).ErrCheck({sc},{writeSelf},{logSelf},{throwSelf})}, SqlComputed, SqlComputeOnChange = sc ];

/// If true, this will will throw ..sc as soon as it becomes an error.
Property throwSelf As %Boolean [ InitialExpression = 0 ];

/// If true, this will log ..sc as an Exception as soon as it becomes an error.
Property logSelf As %Boolean [ InitialExpression = 0 ];

/// If true, this will write the error text of ..sc as soon as it becomes an error.
Property writeSelf As %Boolean [ InitialExpression = 0 ];

/// If true, after other error handling, ..sc will be reset to $$$OK.
/// Note that if this is false, you will need to reset the status yourself for the automatic handling to trigger again.
Property resetSelf As %Boolean [ InitialExpression = 0 ];

/// Handles status according to flags set, then sets isErr.
ClassMethod ErrCheck(sc, writeSelf, logSelf, throwSelf) As %Boolean [ Internal ]
{
	if $$$ISERR(sc){
		if writeSelf{
			write $SYSTEM.Status.GetErrorText(sc)
		}
		if logSelf = 1{
			do ##class(%Exception.StatusException).CreateFromStatus(sc).Log()
		}
		if throwSelf = 1{
			$$$ThrowStatus(sc)
		}
		quit 1
	}
	else{
		quit 0
	}
}

/// If resetSelf is true, resets the status code after error handling occurs.
ClassMethod Reset(sc, resetSelf) As %Status [ Internal ]
{
	return:resetSelf $$$OK
	return sc
}

/// flags is a string which determines status behavior when an error occurs
/// T = throw the status
/// L = log the status as an exception
/// W = write the status error text
/// R = reset status after error handling; if set, isErr goes back to 0 and sc goes back to 1
ClassMethod New(flags As %String) As DH.WatchedStatus
{
	set status = ##class(DH.WatchedStatus).%New()
	set flags = $ZCVT(flags,"U")
	set:(flags [ "T") status.throwSelf = 1
	set:(flags [ "L") status.logSelf = 1
	set:(flags [ "W") status.writeSelf = 1
	set:(flags [ "R") status.resetSelf = 1
	return status
}

}

Here's how I can use it:

set mystatus = ##class(WoodWare.Status).New("WR")
set mystatus.sc = (something that returns a status)

Having done that, since I used the write and reset flags in the constructor, it will write out the error text and then reset the status object back to $$$OK. I can use this to save myself from constantly having to check whether the status is an error and log or throw it in other code outside the terminal too. It essentially watches itself.

Update: this is now available on IPM.

install watchedstatus

Well done, @David Hockenbroch !

USER>zpm "install watchedstatus"

[USER|watchedstatus] Reload START (/usr/irissys/mgr/.modules/USER/watchedstatus/1.0.3/)

[USER|watchedstatus] Reload SUCCESS

[watchedstatus] Module object refreshed.

[USER|watchedstatus] Validate START

[USER|watchedstatus] Validate SUCCESS

[USER|watchedstatus] Compile START

[USER|watchedstatus] Compile SUCCESS

[USER|watchedstatus] Activate START

[USER|watchedstatus] Configure START

[USER|watchedstatus] Configure SUCCESS

[USER|watchedstatus] Activate SUCCESS

Installed perfectly!

Not sure about functionality though. I was wondering of a class that gives me an error, and remember another ipm module - objectcript-errors.

Installed:

USER>zpm "install objectscript-errors"

[USER|objectscript-errors] Reload START (/usr/irissys/mgr/.modules/USER/objectscript-errors/1.0.2/)

[USER|objectscript-errors] Reload SUCCESS

[objectscript-errors] Module object refreshed.

[USER|objectscript-errors] Validate START

[USER|objectscript-errors] Validate SUCCESS

[USER|objectscript-errors] Compile START

[USER|objectscript-errors] Compile SUCCESS

[USER|objectscript-errors] Activate START

[USER|objectscript-errors] Configure START

[USER|objectscript-errors] Configure SUCCESS

[USER|objectscript-errors] Activate SUCCESS

it can generate errors in simple objectscript calls, e.g. null division etc:

USER>s sc=##class(shvarov.objectscript.errors).DIVIDE()         

ERROR #5002: ObjectScript error: <DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1

USER>zw sc

sc="0 "_$lb($lb(5002,"<DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1",,,,,,,,$lb(,"USER",$lb("^DIVIDE+3^shvarov.objectscript.errors.1^1","e^^^0"))))/* ERROR #5002: ObjectScript error: <DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1 */

So this is what I did:

USER>set status=##class(DH.WatchedStatus).New("RW")

USER>set status.sc=##class(shvarov.objectscript.errors).DIVIDE()

ERROR #5002: ObjectScript error: <DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1ERROR #5002: ObjectScript error: <DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1

if I do w status.sc:

USER>w status.sc

1

If I do everything right?

Ok, I upgraded errors, so they verbose on demand. now:

USER>s sc=##class(shvarov.objectscript.errors).DIVIDE()

USER>zw sc

sc="0 "_$lb($lb(5002,"<DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1",,,,,,,,$lb(,"USER",$lb("^DIVIDE+3^shvarov.objectscript.errors.1^1","e^^^0"))))/* ERROR #5002: ObjectScript error: <DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1 */

USER>s status.sc=##class(shvarov.objectscript.errors).DIVIDE()

ERROR #5002: ObjectScript error: <DIVIDE>DIVIDE+3^shvarov.objectscript.errors.1

USER>

So, every time I need to printout an error I do:

USER>set status.sc=##class(bla.bla).method()

and it prints out an error if it is an error. and if not it is silent?

I like it!