How to access and clean Application Error Log programmatically?
- 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.
- 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.
- The ^ERRORS global has an unclear structure to me and I doubt it is related to the Application Error Log anyway.
.png)
Thanks in advance,
Anna
Comments
Hi, not sure about Cache2017 but in later versions there is a TaskMgr task available to purge application error logs, afaik this is automatically configured to purge any application errors older than 30days.
%SYS.Task.PurgeErrorsAndLogs - InterSystems IRIS Data Platform 2025.1 - including private class members
Yes, it is available!
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)
}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.
Yeah, that's true. the date should be in MM/DD/YYYY format. I updated the code.
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{ S NSP=$ZNSPACE,%ENSP=NSP ; Error global namespace could be "%SYS" or local namespace, haven't worked out how it changes I test S NSP="%SYS",%ENSP=NSP ; ; work out where the ^ERRORS global is i $D(^ERRORS) { ; it's in the current namespace S %ENSP=NSP ; location of the error global } ElseIf $D(^["%SYS"]ERRORS) { S %ENSP="%SYS" } ; count the number of errors for this namespace by date. Date is $H S Date="" F { S Date=$O(^[%ENSP]ERRORS(Date),-1) q:Date=""Errors with a date are given a sequence number starting at 1 for the first error
{ S ERN="" F { S ERN=$O(^[%ENSP]ERRORS(Date,ERN)) Q:ERN="" ; STACK 0 has got most of what we need to see right now S NSPACE=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$ZU( 5)")) I NSP=NSPACE,$I(COUNT) {; sort by time, latest to earliest
; stack 0 contains environment variables like $I, $J, etc. S Time=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$H")) S Username=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$USERNAME")) S ZE=$g(^[%ENSP]ERRORS(Date,ERN,"*STACK",0,"V","$ZE"))Further stack levels contain variables (including arrays and objects) and other stack information
S changePoint=$G(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"L"))s var=$o(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var),1,value) q:var=""i $D(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var))#2 { s ZR=$ZR i $p(value,"0 ",2)'="",..ListValid($p(value,"0 ",2)) { ; looks like a status code //" "_var_" = (status code - fail...)")} else { // (" "_var_" = "_value)} i value["<OBJECT REFERENCE>[" { d ..ShowObject(ScreenObj3,ZR,.i,0) } } // Variables that are arrays i $D(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var,"N"))>1{ s subs="" f j=1:1 { s 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){ S Date=$QS(ZRef,1) S ERN=$QS(ZRef,2) S Stack=$QS(ZRef,4) S var=$QS(ZRef,6) S 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 s sign=$s(expand:" - ",1:" + ") //$LB(sign_var_" = "_value),$LB("OBJECT",ZRef)) i expand { s row="" f line=1:1:$G(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var,"OREF",1)) { s row=row_$G(^[%ENSP]ERRORS(Date,ERN,"*STACK",Stack,"V",var,"OREF",1,line)) i $E(row,$l(row)-1,9999)=$c(13,10) { //$LB(" "_$e(row,0,$l(row)-2))) s 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.
Very instructive, thanks!
You can also reference https://docs.intersystems.com/iris20251/csp/docbook/Doc.View.cls?KEY=GC…. While this documentation is for IRIS all of the information should be applicable for Cache as well.
Yes, I use that too occasionally. I think Portal functionality is faster though.
💡 This question is considered a Key Question. More details here.