Question Anna Golitsyna · Jun 2, 2025

How to access and clean Application Error Log programmatically?

  1. I like the Application Error Log functionality a lot. However, it becomes time consuming to inspect it date by date and directory by directory on a multidirectory server. Ideally, I would use an existing error class to write a custom error report by date, selected namespaces, etc. Does such a system class actually exist? Not that I found. The detail level on the screenshot below is enough.  
  2. Some Application Error Logs go back a couple of years and load for a long while. Is there any programmatic way to clean them? Deleting errors in GUI date by date and directory by directory is rather tedious as well.  
  3. The ^ERRORS global has an unclear structure to me and I doubt it is related to the Application Error Log anyway.

Thanks in advance,
Anna

Product version: Caché 2017.1

Comments

Ashok Kumar T · Jun 3, 2025

You can delete the application error logs for all days by executing the below code for specific namespace 

ClassMethod DeleteAppErrorLog(Namespace As %String = {$Namespace}) As %Status
{
    New $Namespace
    Set $Namespace = "%SYS"
    Return ##class(SYS.ApplicationError).DeleteByNamespace(Namespace)
}

Delete by date

ClassMethod DeleteAppErrorLogByDT(pNamespace As %String = {$Namespace},pDate ={$ZD(+$H)}) As %Status
{
    New $Namespace
    Set $Namespace = "%SYS"
    Return ##class(SYS.ApplicationError).DeleteByDate(pNamespace,pDate)
}
0
Anna Golitsyna  Jun 3, 2025 to Ashok Kumar T

Thanks a lot! I successfully tested both and one correction: the date is not in MUMPS format. It works with the 06/03/2025 format though.

0
Ashok Kumar T  Jun 3, 2025 to Anna Golitsyna

Yeah, that's true. the date should be in MM/DD/YYYY format. I updated the code.

0
Stuart Strickland · Jun 3, 2025

Hi, I wrote some code to access the ^ERROR global from a character based screen some years ago because the GUI was too slow and cumbersome. There's too much code to share on here but I can give you some direction on the global layout:

The location of ^ERRORS varies between current namespace and %SYS. I haven't looked in to why, it might be due to Caché version. This full program works from Caché 5 through to 2018.

 ClassMethod START(test = 0) As %String
{
NSP=$ZNSPACE,%ENSP=NSP ; Error global namespace could be "%SYS" or local namespace, haven't worked out how it changes
test NSP="%SYS",%ENSP=NSP
 ;
 ; work out where the ^ERRORS global is
$D(^ERRORS) {
  ; it's in the current namespace
%ENSP=NSP ; location of the error global
 }
 ElseIf $D(^["%SYS"]ERRORS) {
%ENSP="%SYS"
 }

 ; count the number of errors for this namespace by date. Date is $H
Date=""
 F  {
Date=$O(^[%ENSP]ERRORS(Date),-1) q:Date=""

Errors with a date are given a sequence number starting at 1 for the first error

 {
ERN=""
 F  {
ERN=$O(^[%ENSP]ERRORS(Date,ERN)) Q:ERN=""
  ; STACK 0 has got most of what we need to see right now
NSPACE=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$ZU( 5)"))
NSP=NSPACE,$I(COUNT) { 
   ; sort by time, latest to earliest
   ; stack 0 contains environment variables like $I, $J, etc. 
Time=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$H"))
Username=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$USERNAME"))
ZE=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$ZE"))

Further stack levels contain variables (including arrays and objects) and other stack information

 changePoint=$G(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"L"))
 var=$o(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var),1,value) q:var=""
 $D(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var))#2 {
ZR=$ZR
$p(value,"0 ",2)'="",..ListValid($p(value,"0 ",2)) {
  ; looks like a status code
   //" "_var_" = (status code - fail...)")
 }
 else {
  // (" "_var_" = "_value)
 }
value["<OBJECT REFERENCE>[" {
..ShowObject(ScreenObj3,ZR,.i,0)
  }
 }
 // Variables that are arrays
$D(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var,"N"))>1{
subs=""
j=1:1 {
subs=$O(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var,"N",subs),1,value)
   q:subs=""
   //(" "_var_"("_subs_") = "_value))

Objects are stored thus

 ClassMethod ShowObject(ScreenObj3 As ZSS.Screen, ZRef, ByRef i, expand = 1)
{
Date=$QS(ZRef,1)
ERN=$QS(ZRef,2)
Stack=$QS(ZRef,4)
var=$QS(ZRef,6)
value=@ZRef
 ; should be immediately followed by "OREF",1)=no of lines
 ; and "OREF",1,0)=something I don't know - could be a character count
 ; then loads of rows where each line is terminated by $C(13,10)
 ; re-add with expand/collapse marker
sign=$s(expand:" - ",1:" + ")
 //$LB(sign_var_" = "_value),$LB("OBJECT",ZRef))
expand {
row=""
line=1:1:$G(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var,"OREF",1)) {
row=row_$G(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var,"OREF",1,line))
$E(row,$l(row)-1,9999)=$c(13,10) {
   //$LB(" "_$e(row,0,$l(row)-2)))
row=""
   }
  }
 }

Once you've understood the global layout it wouldn't take a lot to kill off dates that you are not interested in any longer. I haven't needed to.

0
Anna Golitsyna  Jun 3, 2025 to Stuart Strickland

Very instructive, thanks!

0
Anna Golitsyna  Jun 3, 2025 to Stephen Canzano

Yes, I use that too occasionally. I think Portal functionality is faster though.

0