Question Anna Golitsyna · Jun 2

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

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$NamespaceSet$Namespace = "%SYS"Return##class(SYS.ApplicationError).DeleteByNamespace(Namespace)
}

Delete by date

ClassMethod DeleteAppErrorLogByDT(pNamespace As%String = {$Namespace},pDate ={$ZD(+$H)}) As%Status
{
    New$NamespaceSet$Namespace = "%SYS"Return##class(SYS.ApplicationError).DeleteByDate(pNamespace,pDate)
}
0
Anna Golitsyna  Jun 3 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 to Anna Golitsyna

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

0

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 changestest 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 $HDate="" 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 nowNSPACE=$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 markersign=$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 to Stephen Canzano

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

0