Question
· 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
Discussion (9)2
Log in or sign up to continue

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

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.