文章目录

第十五章 疯狂Caché 错误处理(三)

使用$ETRAP处理错误

当出现错误陷阱并且设置了$ETRAP时,Caché将执行以下步骤:

  1. 设置$ECODE$ZERROR的值。
  2. 处理值为$ETRAP的命令。

默认情况下,每个DOXECUTE或用户定义的函数上下文都继承调用它的帧的$ETRAP错误处理程序。这意味着在任何上下文级别指定的$ETRAP错误处理程序都是最后定义的$ETRAP,即使该定义是从当前级别向下几个堆栈级别进行的。

$ETRAP错误处理程序

$ETRAP特殊变量可以包含一个或多个在发生错误时执行的ObjectScript命令。使用SET命令将$ETRAP设置为一个字符串,该字符串包含一个或多个将控制转移到错误处理例程的Caché命令。此示例将控制转移到LogError代码标签(它是例程ErrRoutine的一部分):

 SET $ETRAP="DO LogError^ErrRoutine"

$ETRAP特殊变量中的命令后面总是隐式的QUIT命令。在需要带参数退出的用户定义函数上下文中调用$ETRAP错误处理程序时,隐式QUIT命令使用空字符串参数退出。

$ETRAP具有全局作用域。这意味着设置$ETRAP之前通常应该有 New $ETRAP。否则,如果在当前上下文中设置了$ETRAP的值,则在超出该上下文的范围后,存储在$ETRAP中的值仍然存在,而控制位于更高级别的上下文中。因此,如果不指定新的$ETRAP,则$ETRAP可能会在设置它的上下文不再存在的意外时间执行。

上下文特定的$ETRAP错误处理程序

任何上下文都可以通过执行以下步骤来建立其自己的$ETRAP错误处理程序:

  1. 使用new命令创建$ETRAP的新副本。
  2. $ETRAP设置为新值。

如果例程在没有首先创建$ETRAP的新副本的情况下设置$ETRAP,则会为当前上下文、调用它的上下文以及可能已保存在调用堆栈上的其他上下文建立新的$ETRAP错误处理程序。因此,建议在设置$ETRAP之前创建一个新副本。

请记住,创建$ETRAP的新副本不会清除$ETRAP。新命令保持$ETRAP的值不变。

下图显示了创建$ETRAP错误处理程序堆栈的$ETRAP赋值序列。如图所示:

  • 例程A创建$ETRAP的新副本,将其设置为“GOTO^ERR”,并包含调用例程Bdo命令。
  • 例程B不对$ETRAP执行任何操作(从而继承例程A$ETRAP错误处理程序),并包含调用例程Cdo命令。
  • 例程C创建$ETRAP的新副本,将其设置为“GOTO^CERR”,并包含调用例程Ddo命令。
  • 例程D创建$ETRAP的新副本,然后将其清除,不为其上下文留下$ETRAP错误处理程序。

如果例程D(其中未定义$ETRAP错误处理程序的上下文)中出现错误,则Caché从调用堆栈中移除例程DDO帧,并将控制转移到例程C$ETRAP错误处理程序。例程C$ETRAP错误处理程序依次分派给^CERR来处理错误。如果例程C中出现错误,则Caché会将控制转移到例程C$ETRAP错误处理程序,但不会展开堆栈,因为错误发生在定义了$ETRAP错误处理程序的上下文中。

$ETRAP错误处理程序

第十五章 疯狂Caché 错误处理(三)_编程语言

$ETRAP控制选项流

当调用$ETRAP错误处理程序来处理错误并执行任何清理或错误日志记录操作时,它具有以下控制流选项:

处理错误并继续应用程序

当调用$ETRAP错误处理程序来处理错误时,Caché会将错误条件视为活动的,直到错误条件被清除。可以通过将$ECODE特殊变量设置为空字符串来消除错误条件:

 SET $ECODE=""

清除$ECODE还会清除进程的错误堆栈。

通常,在消除错误条件后,可以使用GOTO命令将控制转移到应用程序中的预定重新启动或继续点。在某些情况下,可能会发现在消除错误条件后退回到以前的上下文级别会更方便。

将控制传递给另一个错误处理程序

如果错误条件没有解除,当Quit命令终止调用$ETRAP错误处理程序的上下文时,Caché会将控制传递给调用堆栈上的另一个错误处理程序。因此,通过在不清除$ECODE的情况下从$ETRAP上下文执行退出,可以将控制传递给上一级错误处理程序。

如果从例程C调用的例程D包含将控制转移到^CERR的错误,则^CERR中前面没有将$ECODE设置为“”(空字符串)的QUIT命令将控制权转移到前一个上下文级的$ETRAP错误处理程序。相反,如果通过清除$ECODE来消除错误条件,则从^CERR退出会将控制转移到例程Bdo^C命令之后的语句。

终止应用程序

如果调用堆栈上不存在上一级错误处理程序,并且$ETRAP错误处理程序在不消除错误条件的情况下执行退出,则应用程序将终止。在应用程序模式下,然后运行Caché并将控制权传递给操作系统。然后出现终端提示符。

请记住,无论是否解除错误条件,都可以使用QUIT命令终止$ETRAP错误处理程序上下文。由于可以在需要无参数退出的上下文级别和需要带参数退出的上下文级别(用户定义的函数上下文)调用相同的$ETRAP错误处理程序,因此提供$QUIT特殊变量来指示特定上下文级别所需的退出命令形式。

对于需要带参数退出的上下文,$QUIT特殊变量返回1(1);对于需要无参数退出的上下文,$QUIT特殊变量返回0(零)。

$ETRAP错误处理程序可以使用$QUIT为任一情况提供支持,如下所示:

  Quit:$QUIT "" Quit

如果合适,$ETRAP错误处理程序可以使用HALT命令终止应用程序。

在错误处理程序中处理错误

当错误处理程序中发生错误时,执行流取决于当前正在执行的错误处理程序的类型。

$ETRAP错误处理程序中的错误

如果新错误出现在$ETRAP错误处理程序中,则Caché会展开调用堆栈,直到调用$ETRAP错误处理程序的上下文级别被删除。然后,Caché将控制传递给调用堆栈上的下一个错误处理程序(如果有的话)。

$ZTRAP错误处理程序中的错误

如果新错误出现在$ZTRAP错误处理程序中,则Caché会将控制权传递给它遇到的第一个错误处理程序,仅在必要时才展开调用堆栈。因此,如果$ZTRAP错误没有在当前堆栈级清除$ZTRAP,并且错误处理程序中随后出现另一个错误,则会在相同的上下文级别再次调用$ZTRAP处理程序,从而导致无限循环。要避免这种情况,请将$ZTRAP设置为错误处理程序开头的另一个值。

$ZERROR$ECODE特殊变量中的错误信息

如果在处理原始错误期间出现另一个错误,则有关第二个错误的信息将替换$ZERROR特殊变量中有关原始错误的信息。但是,Caché会将新信息附加到$ECODE特殊变量。根据第二个错误的上下文级别,Caché也可以将新信息附加到进程错误堆栈。如果$ECODE特殊变量的现有值非空,则Caché会将新错误的代码作为新逗号附加到当前$ECODE值。在发生以下任一情况之前,$ECODE特殊变量中会累积错误代码:

  • 可以显式清除$ECODE,例如:
 SET $ECODE = ""
  • $ECODE的长度超过了最大字符串长度。
    然后,下一个新的错误代码将替换$ECODE中的当前错误代码列表。

当发生错误并且错误堆栈已经存在时,除非错误堆栈上的上下文级别已经存在有关另一个错误的信息,否则Caché会在发生错误的上下文级别记录有关新错误的信息。在这种情况下,信息被放在错误堆栈的下一级(不管那里可能已经记录了哪些信息)。

因此,根据新错误的上下文级别,错误堆栈可以扩展(添加一个或多个上下文级别),或者可以重写现有错误堆栈上下文级别的信息以容纳关于新错误的信息。

请记住,可以通过清除$ECODE特殊变量来清除进程错误堆栈。

强制出错

可以设置$ECODE特殊变量或使用ZTRAP命令在受控环境下导致错误发生。

设置$ECODE

可以将$ECODE特殊变量设置为任何非空字符串以导致错误发生。当例程将$ECODE设置为非空字符串时,Caché会将$ECODE设置为指定的字符串,然后生成错误条件。此情况下的$ZERROR特殊变量设置为以下错误文本:

<ECODETRAP>

然后,控制传递给错误处理程序,就像它对正常的应用程序级错误所做的那样。

可以向错误处理程序添加逻辑,以检查设置$ECODE导致的错误。错误处理程序可以检查$ZERROR中是否有<ECODETRAP>错误(例如,“$ze[”ECODETRAP“”),或者错误处理程序可以检查$ECODE中选择的特定字符串值。

创建特定于应用程序的错误

请记住,$ECODE的ANSI标准格式是一个或多个错误代码的逗号括起来的列表:

  • 前缀为“Z”的错误是特定于实现的错误
  • 前缀为“U”的错误是特定于应用程序的错误

可以按照ANSI标准创建自己的错误代码,方法是让错误处理程序将$ECODE设置为以“U”为前缀的相应错误消息。

  SET $ECODE=",Upassword expired,"

处理来自Terminal的错误

如果在未设置错误处理程序的情况下在终端提示符下登录到Caché后生成错误,则当输入的代码行出现错误时,Caché将执行以下步骤:

  1. Caché在进程的主设备上显示一条错误消息。
  2. Caché在发生错误的调用堆栈级别中断。
  3. 该过程返回终端提示。

了解错误消息格式

作为错误消息,Caché显示三行:

  1. 发生错误的整行源代码。
  2. 在源代码行下方,插入符号(^)指向导致错误的命令。
  3. 包含$ZERROR内容的行。

在以下终端提示示例中,第二个SET命令具有未定义的局部变量错误:

DHC-APP>WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
hello
 
WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
                              ^
<UNDEFINED> *zzz
DHC-APP>

在下面的示例中,同一行代码位于从终端提示符处执行的名为mytest的程序中:

USER>DO ^mytest
hello

  WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
                                ^
<UNDEFINED>WriteOut+2^mytest *zzz
USER 2d0>

在本例中,$ZERROR表示mytest中的错误发生在距名为WriteOut的标签偏移2行的位置。请注意,提示符已更改,表示已启动新的程序堆栈级别。

了解终端提示符

默认情况下,终端提示指定当前命名空间。如果一个或多个事务处于打开状态,则它还包括$TLEVEL事务级别计数。此默认提示符可以配置不同的内容,如ZNSPACE命令文档中所述。以下示例显示了默认值:

USER>
TL1:USER>

如果在例程执行期间发生错误,系统将保存当前程序堆栈并启动新的堆栈帧。将显示扩展提示,例如:

USER 2d0>

这个扩展提示表示程序堆栈上有两个条目,最后一个是调用do(由“d”表示)。
请注意,此错误在程序堆栈上放置了两个条目。下一个DO执行错误将导致终端提示:

USER 4d0>

从错误中恢复

然后,可以执行以下任一步骤:

  • 从终端提示符发出命令
  • 查看和修改变量和全局数据
  • 编辑包含错误的例程或任何其他例程
  • 执行其他例程

这些步骤中的任何一个甚至都可能导致额外的错误。

执行完这些步骤后,最有可能的做法是恢复执行或删除全部或部分程序堆栈。

在下一个顺序命令处恢复执行

通过在终端提示符下输入无参数GOTO,可以在导致错误的命令之后的下一个命令恢复执行:

USER>DO ^mytest
hello

  WRITE "hello",! SET x="world" SET y=zzz WRITE x,!
                                ^
<UNDEFINED>WriteOut+2^mytest *zzz
USER 2d0>GOTO
world

USER>

在另一行恢复执行

可以通过在终端提示符下发出带有LABEL参数的GOTO命令,在另一行恢复执行:

USER 2d0>GOTO ErrSect

删除程序堆栈

USER 4d0>QUIT
USER>

删除部分程序堆栈

可以在终端提示符下发出带有整数参数的Quit n命令,以删除最后一个(或最后几个)程序堆栈条目:

USER 8d0>QUIT 1
 
USER 7E0>QUIT 3
 
USER 4d0>QUIT 1
 
USER 3E0>QUIT 1
 
USER 2d0>QUIT 1
 
USER 1S0>QUIT 1
 
USER>

请注意,在本例中,因为程序错误创建了两个程序堆栈条目,所以必须位于“d”堆栈条目上,才能通过发出goto来恢复执行。根据发生的其他情况,“d”堆栈条目可以是偶数(用户2d0>)或奇数(用户3d0>)。

通过使用NEW $ESTACK,可以退出到指定的程序堆栈级别:

USER 4d0>NEW $ESTACK

USER 5E1>
/* more errors create more stack frames */

USER 11d7>QUIT $ESTACK
 
USER 4d0>

请注意,新的$ESTACK命令将向程序堆栈添加一个条目。

使用%ETN记录应用程序错误

%ETN实用程序将异常记录到应用程序错误日志中,然后退出。可以将%ETN(或其入口点之一)作为实用程序调用:

  DO ^%ETN

或者,可以将$ZTRAP特殊变量设置为%ETN(或其入口点之一):

  SET $ZTRAP="^%ETN"

可以指定%ETN或其入口点之一:

  • FORE^%ETN(前台)在标准应用程序错误日志中记录异常,然后暂停退出。这将调用回滚操作。这与%ETN的操作相同。

  • BACK^%ETN(后台)将异常记录到标准应用程序错误日志,然后退出。这不会调用回滚操作。

  • LOG^%ETN将异常记录到标准应用程序错误日志,然后退出。这不会调用回滚操作。异常可以是标准%Exception.SystemException,也可以是用户定义的异常。

要定义异常,请在调用LOG^%ETN之前将$ZERROR设置为有意义的值;该值将用作日志条目中的错误消息字段。还可以在LOG^%ETN:do LOG^%ETN("This is my custom exception")中直接指定用户定义的异常;该值将用作日志条目中的错误消息字段。如果将$ZERROR设置为空字符串(SET $ZERROR=“”),LOG^%ETN会记录<LOG ENTRY>错误。如果将$ZERROR设置为<INTERRUPT>(SET$ZERROR=“<INTERRUPT>”)LOG^%ETN会记录一个<interrupt log>错误。

LOG^%ETN返回包含两个元素的%列表结构:$HOROLOG日期和错误号。

下面的示例使用推荐的编码实践,即立即将$ZERROR复制到变量中。LOG^%ETN返回%列表值:

  SET err=$ZERROR
  SET rtn = $$LOG^%ETN(err)
  WRITE "logged error date: ",$LIST(rtn,1),!
  WRITE "logged error number: ",$LIST(rtn,2)

调用LOG^%ETNBACK^%ETN会自动增加可用的进程内存,完成工作,然后恢复原始的$ZSTORAGE值。但是,如果在<store>错误之后调用log^%ETNback^%ETN,恢复原始$ZSTORAGE值可能会触发另一个<store>错误。因此,当因<store>错误调用这些%etn入口点时,系统会保留增加的可用内存。

使用管理门户查看应用程序错误日志

从管理门户中,依次选择系统操作、系统日志和应用程序错误日志。这将显示具有应用程序错误日志的命名空间的命名空间列表。可以使用标题对列表进行排序。

第十五章 疯狂Caché 错误处理(三)_错误处理_02

选择命名空间的日期以显示存在应用程序错误日志的日期,以及该日期记录的错误数。可以使用标题对列表进行排序。可以使用筛选器将字符串与日期和数量值匹配。

选择日期的错误以显示该日期的错误。Error#按时间顺序将整数分配给错误。Error#*com是应用于该日期所有错误的用户注释。您可以使用标题对列表进行排序。您可以使用筛选器来匹配字符串。

选择“错误的详细信息”可打开“错误详细信息”窗口,该窗口显示错误发生时的状态信息,包括特殊变量、值和堆栈详细信息。您可以为单个错误指定用户注释。

命名空间、日期和错误列表包括允许删除相应一个或多个错误的错误日志的复选框。选中要删除的内容,然后选择删除按钮。

使用%ERN查看应用程序错误日志

%ETN实用程序检查%ETN错误捕获实用程序记录的应用程序错误。%ETN返回为当前命名空间记录的所有错误。

  1. DO ^%ERN实用程序,请执行以下步骤:该实用程序的名称区分大小写;对该实用程序中的提示的响应不区分大小写。在提示下,你可以输入吗?列出提示符的语法选项,或?L列出所有已定义的值。可以使用Enter键退出到上一级。
  2. For Date:在此提示下,输入错误发生的日期。可以使用%date实用程序接受的任何日期格式;如果省略年份,则假定为当前年份。它返回日期和该日期记录的错误数。或者,可以使用以下语法从此提示检索错误列表:
  • ?L列出所有发生错误的日期,最近的第一个日期,以及记录的错误数量。
    (T)列表示多少天前,其中(T)=今天,(T-7)=七天前。如果为当天的所有错误定义了用户注释,则它会显示在方括号中。列出后,它将重新显示for date:提示符。您可以输入日期或T-n。

  • [TEXT列出包含子字符串TEXT的所有错误。text列出在错误名称组件中包含子字符串文本的所有错误。^TEXT列出错误位置组件中包含子字符串文本的所有错误。列出后,它将重新显示for date:提示符。输入日期。

  1. Error:在此提示符下提供要检查的错误的整数:1表示当天的第一个错误,2表示第二个错误,依此类推。或输入问号(?)。有关可用响应的列表,请执行以下操作。该实用程序显示有关错误的以下信息:错误名称、错误位置、时间、系统变量值以及在错误发生时执行的代码行。

可以在ERROR:PROMPT FOR COMMENTS时指定*。*显示应用于当天所有错误的当前用户指定备注。然后,它会提示提供新注释,以替换所有这些错误的现有注释。

  • Variable:在此提示下,您可以为有关变量的信息指定多个选项。如果指定局部变量的名称(未订阅或已下标),%ERN将返回该变量(如果已定义)及其所有后代节点的堆栈级别和值。不能指定全局变量、进程私有变量或特殊系统变量。

You may enter ?要列出变量的其他语法选项:PROMPT。

  • *A:在Variable:Prompt中指定时,显示DEVICE:PROMPT;按Return键在当前终端设备上显示结果。

  • *V:在Variable:提示下指定时,显示变量:提示。在此提示下,指定无下标局部变量或无下标局部变量的逗号分隔列表;有下标的变量将被拒绝。%ERN然后显示Device:Prompt;按Return在当前终端设备上显示结果。%ern返回每个指定变量(如果已定义)及其所有子节点的值。

翻译:

注意:%ER是%ERN的旧名称。它们的功能是相同的,尽管%ern的效率略高一些。

第十五章 疯狂Caché 错误处理(三)_数据库_03
第十五章 疯狂Caché 错误处理(三)_M_04
第十五章 疯狂Caché 错误处理(三)_M_05