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