IBMS错误处理改善
设计说明书V1.0
修订历史
版本 | 日期 | 修改内容 | 作者 | 审核人 | 批准 |
1.0 | 2011-03-30 | 发布 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. 概述
1.1 目的
目前IBMS系统后台捕捉错误的信息只有代理或脚本库名称和出错行数,并且是通过描述的方式输出在控制台上,这种方式很不规范,造成了查询、跟踪错误比较困难。为了更好的跟踪IBMS系统发生的错误信息,需要改善目前捕捉错误日志信息的处理方式。
1.2 适用范围
IBMS错误捕捉与处理
1.3 术语表
表1-1 术语与缩写表
缩写、术语 | 解 释 |
|
|
|
|
2. 架构设计
2.1 基本逻辑图
系统关系说明:
n 应用数据库:IBMS系统的一个应用库,当用户操作或系统自动处理出现异常错误时,程序的错误捕捉机制获取到错误信息,抛出错误到控制台,并在错误日志库中建立相应文档。
n 错误日志库:IBMS架构下的数据库,为了方便设计,路径固定为“lks/sys/lks_ErrorLog.nsf”。按照一定规则保存所有抛出的异常错误,同时通过代理定时将错误日志导入到关系型数据库中(SQL)。
n 关系型数据库:用于长期保存所有的异常错误。
3. 错误日志库
3.1 表单设计说明
3.1.1 错误日志
n 表单画面
暂略
n 表单字段说明
字段名 | Notes字段名 | SQL 字段名 | SQL字段类型 | 是否必需 | 描述 |
错误编号 | F_ErrorNo | Error_No | NVARCHAR(50) | 是 | 控制台显示错误消息时,可以根据错误编号到错误日志库中查询对应文档查看详细错误日志 编号规则: Err_yymmdd_0001(四位流水号) |
错误类型 | F_ErrorType | Error_Type | INT | 是 | 0 -系统捕捉到错误,并且已针对错误进行处理 1 -系统捕捉的未知错误,未对错误进行处理 默认:1 |
严重程度 | F_ErrorLevel | Error_Level | INT | 是 | 0 - 一般(不会对用户造成影响) 1 - 严重(可能对用户造成影响,但仅局限于某个用户或单独的应用模块) 2 - 非常严重(可能对整个系统造成影响) 默认:1 |
错误时间 | F_ErrorTime | Error_Time | DateTime | 是 | 发生错误时间 |
代理名称 | F_ErrorAgent | Error_Name | NVARCHAR(255) | 是 | 发生错误的代理名称 |
脚本库名称 | F_ErrorLibary | Error_Libary | NVARCHAR(50) | 是 | 发生错误的脚本库名称 |
函数名称 | F_ErrorFunction | Error_Function | NVARCHAR(50) | 是 | 发生错误的函数名称 |
错误行数 | F_ErrorLine | Error_Line | INT | 是 |
|
错误代码 | F_ErrorCode | Error_Code | INT | 是 |
|
错误描述 | F_ErrorDesc | Error_Desc | NVARCHAR(255) | 是 |
|
服务器名称 | F_ErrorServer | Error_Server | NVARCHAR(50) | 是 | 发生错误的服务器名称 |
数据库ID | F_ErrorDBID | Error_DBID | CHAR(32) | 是 | 发生错误的代理所在数据库ID |
数据库路径 | F_ErrorDBPath | Error_DBPath | NVARCHAR(255) | 是 | 发生错误的代理所在数据库路径 |
处理文档 | F_ErrorDocID | Error_DocID | NVARCHAR(255) | 否 | 发生错误时正在处理的文档ID |
处理字段 | F_ErrorField | Error_Field | NVARCHAR(50) | 否 | 发生错误时正在处理的字段名称 |
当前用户 | F_ErrorUser | Error_User | NVARCHAR(50) | 否 | 发生错误时当前用户名称 |
当前用户IP | F_ErrorIP | Error_IP | NVARCHAR(50) | 否 | 发生错误时用户的访问IP地址 |
当前流程名称 | F_ErrorFlow | ErrorFlow | NVARCHAR(100) | 否 | 发生错误时当前流程名称 |
当前流程节点 | F_ErrorFlowNode | ErrorFlowNode | NVARCHAR(100) | 否 | 发生错误时当前流程环节 |
3.1.2 系统设定
n 表单画面
暂略
n 表单字段说明
字段名 | Notes 字段名 | Notes字段类型 | 是否必填 | 描述 |
服务器 | F_Server | 文本 | 是 | SQL Server 服务器名称或ip地址 |
数据库 | F_Database | 文本 | 是 | 存放错误日志的数据库名称 |
表单 | F_Form | 文本 | 是 | 存放错误日志的表 |
用户名 | F_Login | 文本 | 是 | 链接Sql的用户名,需要有写的权限 |
密码 | F_Password | 密码 | 是 |
|
3.2 代理
代理用于将日志文档定时导入到关系型数据库中。
运行方式:定时运行
运行频率:每周一次
处理方式:从日志库中获取Flag标志不等于“1”的错误日志文档,成功导入到关系型数据库后将文档Flag标志设为“1”。
4. 应用库
4.1 错误处理范围
错误处理的涉及范围:所有应用库的代理、脚本库。
4.2 错误处理方案
因原先的错误处理方式多样,没有统一的接口来处理,且经过多次开发后,代码格式也不统一,没法通过程序统一来部署,所以本方案只能采用人工一一部署的方式。
(一) 旧错误捕捉方式处理
因原先已经存在的错误处理方式多样,且存在量很多。如果一一分析是否需要删除并用新的方案替换,此种方式工作量太大。所以采用原来的错误保持不变,在原先的基础上增加新的处理机制,当出现问题时,只要关注新的错误提示信息即可。
(二) 新的错误捕捉方式处理
n 代码添加的两种方式:
A. 错误冒泡模式,即错误会反映到上层调用者(即引起上层调用者触发错误),如:
序号 | 部署前代码 | 部署后代码 | 描述 |
1 | 一般的函数 Sub Sample() On Error Goto errhandle 代码块 errhandle: 原错误处理块 End Sub | Use "LS_CError" Sub Sample() On Error Goto errhandle 代码块 errhandle: 原错误处理块 Dim cError As New CError(Erl,Err,Error) Error Err,cError.GetError End Sub | n "LS_CError"脚本中为错误处理类 n 黄色标记的为新的错误处理方式: 像这种部署前的代码可以看出报错后,错误会反映到上层调用者,那么在此sub内就不需要建立错误日志文档和将错误信息输入到服务器控制台,只需要将错误信息手动抛出到上层调用者那即可。 注:部署只要将黄色标记代码添加到合理位置即可 Function Sample()也是如此 |
2 | 一般类 Public Class temp Sub Sample() On Error Goto errhandle 代码块 errhandle: 原错误处理块 End Sub End Class | Use "LS_CError" Public Class temp Sub Sample() On Error Goto errhandle 代码块 errhandle: 原错误处理块 Dim cError As New CError(Erl,Err,Error) Error Err,cError.GetError End Sub End Class | n "LS_CError"脚本中为错误处理类 n 黄色标记的为新的错误处理方式: 像这种部署前的代码可以看出报错后,错误会反映到上层调用者,那么在此sub内就不需要建立错误日志文档和将错误信息输入到服务器控制台,只需要将错误信息手动抛出到上层调用者那即可。 注:部署只要将黄色标记代码添加到合理位置即可 Function Sample()也是如此 |
错误描述说明 |
保存到错误日志库和输出到控制台的错误信息格式为: 【ErrorNo】_【Server】_【Database】_【Agent】_【LotusScript】_【Function】_【Error】_ 示例: 【Server】CN=homedev/O=STP【Database】suntech/sto/lks_MaterialIntroduce.nsf【Agent】测试错误日志代理|test【LotusScript】TESTSCRIPT【Function】TEST【Error】Type mismatch(10,13) Type mismatch(10,13)括号中的第一个表示错误行,第二个表示错误代码 |
B. 错误不冒泡模式,即错误不会反映到上层调用者,如:
序号 | 部署前代码 | 部署后代码 | 描述 |
1 | 脚本库 Sub Initialize On Error Goto errhandle 代码块 errhandle: 原错误处理块 End sub | Use "LS_CError" Sub Initialize On Error Goto errhandle 代码块 errhandle: 原错误处理块 Dim cError As New CError(Erl,Err,Error) cError.ThrowError End sub | n 黄色标记的为新的错误处理方式: 像这种部署前的代码可以看出报错后,在Initialize错误不会反映到外层,那么在此sub内就需要建立错误日志文档和将错误信息输入到服务器控制台 注:部署只要将黄色标记代码添加到合理位置即可 |
2 | 代理 Sub Initialize On Error Goto errhandle 代码块 errhandle: 原错误处理块 End sub | Use "LS_CError" Sub Initialize On Error Goto errhandle 代码块 errhandle: 原错误处理块 Dim cError As New CError(Erl,Err,Error) cError.ThrowError End sub | n 黄色标记的为新的错误处理方式: 像这种部署前的代码可以看出报错后,在Initialize错误不会反映到外层,那么在此sub内就需要建立错误日志文档和将错误信息输入到服务器控制台 注:部署只要将黄色标记代码添加到合理位置即可 |
3 | 代理和脚本的一般函数 Sub Sample() On Error Goto errhandle 代码块 Exitfunc: Exit sub errhandle: 原错误处理块 Resume exitfunc End sub | Use "LS_CError" Sub Sample() On Error Goto errhandle 代码块 Exitfunc: Exit sub errhandle: 原错误处理块 Dim cError As New CError(Erl,Err,Error) cError.ThrowError resume exitfunc End sub | n 黄色标记的为新的错误处理方式: 像这种部署前的代码可以看出报错后,错误不会反映到上层调用者,那么在此sub内就需要建立错误日志文档和将错误信息输入到服务器控制台。 注:部署只要将黄色标记代码添加到合理位置即可 Function Sample()也是如此
说明:如果通过exit function/sub的方式结束的话一般不会将错误反映到上层调用者 |
4 | 一般函数 Sub Sample() On Error Goto errhandle 代码块 Exit sub errhandle: 原错误处理块 Resume next End sub | n 黄色标记的为新的错误处理方式: 像这种部署前的代码可以看出报错后,错误不会反映到上层调用者,那么在此sub内就需要建立错误日志文档和将错误信息输入到服务器控制台。 注:部署只要将黄色标记代码添加到合理位置即可 Function Sample()也是如此 说明:如果通过<FONT style="LINE-HEIGHT: 125%" color="red" face=""">exit function/sub的方式结束的话一般不会将错误反映到上层调用者 |
错误描述说明 |
保存到错误日志库和输出到控制台的错误信息格式为: 【ErrorNo】_【Server】_【Database】_【Agent】_【LotusScript】_【Function】_【Error】_【Execute Relation】_ 示例: 【Server】CN=homedev/O=STP【Database】suntech/sto/lks_MaterialIntroduce.nsf【Agent】测试错误日志代理|test【LotusScript】TESTSCRIPT【Function】TEST【Error】Type mismatch(10,13)【Execute Relation】[LS]TESTSCRIPT[Func]TEST1(5)<<[LS]TESTSCRIPT[Func]TEST2(3)<<[LS]DADDC54[Func]INITIALIZE(3)
n 【Execute Relation】表示错误程序的一个调用关系,关系为代理“测试错误日志代理|test”的INITIALIZE方法调用了方法test2,test2调用了test1,test1调用了test,test方法报错,将错误信息冒泡到调用层,生成了此错误信息。 n [LS]TESTSCRIPT[Func] TEST1 (5)表示脚本库TESTSCRIPT的方法TEST1的第5行报错,它调用了test方法 n [LS]DADDC54[Func]INITIALIZE(3)中LS为DADDC54时不像一个脚本的名称则表示此LS其实是Agent代号(无法获得代理名,代理名称前面已经通过其他方式获取了,所以不影响) |
n 可选参数的设置
错误类型、严重程度和处理文档ID、自定义参数(描述看类的方法),这4个参数默认情况下可以不需要处理。但有些方法体内报错时有此4个参数中的部分参数需要保存到错误日志时,可以通过CError类的SetOptional成员方法来完成。
如:
Use "LS_CError" Sub Sample() On Error Goto errhandle 代码块 Exitfunc: Exit sub errhandle: 原错误处理块 Dim cError As New CError(Erl,Err,Error) Call cError.SetOptional(1,1,cdoc.DocumentUniqueID,"F_ErrorField:1;F_ErrorUser:1;F_ErrorIP:1; F_ErrorFlow:1;F_ErrorFlowNode:1") cError.ThrowError resume exitfunc End sub |
n 特殊部署说明
在部署过程中,可能有些方法并没有加On Error goto 语句来捕捉错误,所以这里可能就不会部署新的错误捕捉机制。当一个方法报错并触发新的错误捕捉机制(错误冒泡模式)时,它的调用链(方法的调用者,一直到代理的Initialize方法)中没有一个方法部署了新的错误捕捉机制,出现的问题为抛出的错误信息未使用类CError的ThrowError方法来处理,那么将不会保存错误信息到错误日志库中,只会抛出如下的错误信息到控制台:
2011-06-29 13:06:41 HTTP Server: Agent '测试错误日志代理(部分无错误处理) test3' error: F_ErrorType:1;F_ErrorLevel:1;F_ErrorDocID:;F_ErrorField:;F_ErrorUser:CN=guangbiao Pan/OU=WuXi/O=STP;F_ErrorIP:1;F_ErrorFlow:1;F_ErrorFlowNode:1*&$#【Ser ver】CN=homedev/O=STP【Database】suntech/sto/ErrorTemp.nsf【Agent】测试错误日志代理(部分无错误处理) test3【LotusScript】TESTSCRIPT3【Function】TEST【Error】Type mismatch(7,13) 注:错误信息由 类CError的方法GetError产生 |
A. 解决方法一:在所有代理的Initialize方法中使用新的错误捕捉机制,但可能会无法获得完整的调用链(建议)
B. 解决方法二:不执行解决方法一,让错误只在控制台显示(不建议)
5. 原代码
错误日志类的原代码 |
'**************************************************** '类名:CError '描述:记录脚本运行错误,并保存到错误日志库中(lks/sys/lks_ErrorLog.nsf) '作者:潘广彪 '日期:2011.06.13-2013.11.12 '**************************************************** Public Class CError '◆定义属性---------------------------------------------------------------------------------- Public session As NotesSession Public cdb As NotesDatabase Public cAgent As NotesAgent Public errorDb As NotesDatabase '错误日志库 '==============属性1======================== Public ErrorLine As Long '错误行数 Public ErrorCode As Integer '错误代码 Public ErrorDesc As String '错误描述 '==============属性2======================== Public ErrorNo As String '错误编号:Err_yymmdd_0001(四位流水号) Public ErrorTime As NotesDateTime '错误时间 Public ErrorServer As String '服务器名称 Public ErrorDBID As String '数据库ID Public ErrorDBPath As String '数据库路径 Public ErrorAgent As String '代理名称 Public ErrorLibary As String '脚本库名称 Public ErrorFunction As String '函数名称 '==============属性3======================== Public ErrorType As Integer '错误类型: '0-系统捕捉到错误,并且已针对错误进行处理 '1 -系统捕捉的未知错误,未对错误进行处理 Public ErrorLevel As Integer '严重程度: '0- 一般(不会对用户造成影响) '1 - 严重(可能对用户造成影响,但仅局限于某个用户或单独的应用模块) '2 - 非常严重(可能对整个系统造成影响) Public ErrorDocID As String '处理文档ID Public SelfDefineParam As String '自定义参数,在方法SetOptional中详细描述 '●构造方法----------------------------------------------------------------------------------- Public Sub New(errorLine As Long,errorCode As Integer,errorDesc As String) Me.ErrorLine=errorLine Me.ErrorCode=errorCode Me.ErrorDesc=errorDesc ErrorType=1 ErrorLevel=1 Set session=New NotesSession Set cdb=session.CurrentDatabase Set cAgent=session.CurrentAgent Set errorDb=session.GetDatabase(cdb.Server,"lks/sys/lks_ErrorLog.nsf",False) End Sub '●定义方法----------------------------------------------------------------------------------- '******************************************* '方法:抛出错误信息 '描述:抛出所有错误信息并保存到错误日志库中 '******************************************* Public Sub ThrowError() ErrorLibary=Getthreadinfo(11) ErrorFunction=Getthreadinfo(10) '获取错误信息 Me.GetError '保存错误日志 Me.save '输出错误到控制台 Me.PrintToConsole End Sub '******************************************* '方法:获取错误信息 '描述:获取所有错误信息并保存到错误日志库中 '******************************************* Public Function GetError() ErrorServer=cdb.Server ErrorDBID=cdb.Replicaid ErrorDBPath=cdb.FilePath Set ErrorTime=New NotesDateTime(Now) ErrorAgent=cAgent.Name If ErrorFunction=""Then ErrorLibary=Getthreadinfo(11) ErrorFunction=Getthreadinfo(10) End If GetError=Me.GetErrorDesc End Function '******************************************* '方法:保存错误日志 '描述:保存错误信息到错误日志库中 '******************************************* Private Sub Save() If errorDb Is Nothing Then Msgbox "错误日志库打开失败..." Else '保存必要属性 Dim errDoc As NotesDocument '错误日志文档 Set errDoc=errorDb.CreateDocument errDoc.Form="FM_ErrorLog" '获取详细错误描述 Dim fullErrorDesc As String Dim errorDesc As String fullErrorDesc=Me.GetErrorDesc errorDesc=Strright(fullErrorDesc,"*&$#") errDoc.F_ErrorDesc=errorDesc '从详细错误描述中提取错误行、错误代码、脚本名、函数名 Dim errorInfo As String errorInfo=StrMiddle(errorDesc,"【Error】","【") errorInfo=Strright(errorInfo,"(") errDoc.F_ErrorLine=Strleft(errorInfo,",") errDoc.F_ErrorCode=StrMiddle(errorInfo,",",")") errDoc.F_ErrorLibary=StrMiddle(errorDesc,"【LotusScript】","【") errDoc.F_ErrorFunction=StrMiddle(errorDesc,"【Function】","【") '-------------------------------------------------------- ErrorNo=GetErrorNo() errDoc.F_ErrorNo=ErrorNo Dim item As New NotesItem(errDoc,"F_ErrorTime",ErrorTime) item.IsSummary=True errDoc.F_ErrorServer=ErrorServer errDoc.F_ErrorDBID=ErrorDBID errDoc.F_ErrorDBPath=ErrorDBPath errDoc.F_ErrorAgent=ErrorAgent '获取非必要属性 Dim arrFullErrorDesc As Variant arrFullErrorDesc=Split(Strleft(fullErrorDesc,"*&$#"),";") arrFullErrorDesc=fulltrim(arrFullErrorDesc) Dim i As Integer For i=0 To Ubound(arrFullErrorDesc) Call errDoc.AppendItemValue(Strleft(arrFullErrorDesc(i),":"),Strright(arrFullErrorDesc(i),":")) Next Call errDoc.Save(True,False) End If End Sub '******************************************* '方法:获取详细错误描述 '描述:将错误信息分类组合显示 '格式:非必要属性*&$#必要属性+触发错误机制的方法调用链 '******************************************* Private Function GetErrorDesc() As String If Instr(ErrorDesc,"【Server】") <> 0 Then '表示GetErrorDesc的方法已经被执行过至少一次 '增加触发错误机制所在方法的调用链 If Instr(ErrorDesc,"【Execute Relation】") <> 0 Then GetErrorDesc=ErrorDesc & |<<[LS]|& ErrorLibary & |[Func]| & ErrorFunction & |(| & ErrorLine & |)| Else GetErrorDesc=ErrorDesc & |【Execute Relation】| & |[LS]|& ErrorLibary & |[Func]| & ErrorFunction & |(| & ErrorLine & |)| End If Else '获取非必要属性描述 Dim optionnalDesc As String GetErrorDesc=|F_ErrorType:| & ErrorType & |;F_ErrorLevel:| & ErrorLevel & |;F_ErrorDocID:| & ErrorDocID & |;| & SelfDefineParam '获取必要属性描述 GetErrorDesc=GetErrorDesc & |*&$#| & |【Server】| & ErrorServer &|【Database】| & ErrorDBPath & |【Agent】| & ErrorAgent & |【LotusScript】| & ErrorLibary &_ |【Function】| & ErrorFunction & |【Error】| & ErrorDesc & |(| & ErrorLine & |,| & ErrorCode & |)| End If End Function '******************************************* '方法:获取错误编号 '描述: '******************************************* Private Function GetErrorNo() As String Dim profileDoc As NotesDocument Dim prefix As String Dim no As String Set profileDoc=errorDb.GetProfileDocument("SysErrorNo") prefix="Err_" & Right(Year(Today),2) & Format(Month(Today),"00") & Format(Day(Today),"00") If prefix<>profileDoc.F_Prefix(0)Then GetErrorNo= prefix & "0001" profileDoc.F_Prefix=prefix profileDoc.F_No="0001" Else no=Format(Int(profileDoc.F_No(0))+1,"0000") profileDoc.F_No=no GetErrorNo= prefix & no End If Call profileDoc.Save(True,False) End Function '******************************************* '方法:设置多个属性值 '描述:对部分非必要的属性赋值 '参数: 'selfDefineParam:自定义参数 '格式:Notes字段名:值;Notes字段名:值;...;Notes字段名:值 '为了字段统一,提供几个自定义参数的字段名 : 'F_ErrorField(处理字段),F_ErrorUser(当前用户), F_ErrorIP (当前用户IP),F_ErrorFlow(当前流程名称),F_ErrorFlowNode(当前流程节点) '******************************************* Public Sub SetOptional(errorType As Integer,errorLevel As Integer,errorDocID As String,selfDefineParam As String) Me.ErrorType=errorType Me.ErrorLevel=errorLevel Me.ErrorDocID=errorDocID Me.SelfDefineParam=selfDefineParam End Sub '******************************************* '方法:输出错误信息到控制台 '描述: '******************************************* Public Sub PrintToConsole() Msgbox |【ErrorNo】| & ErrorNo & Strright(Me.GetErrorDesc,"*&$#") End Sub '******************************************* '方法:截取字符串 '描述: '******************************************* Private Function StrMiddle(sourceStr As String,startStr As String,endStr As String) As String Dim returnInfo As Variant returnInfo=Evaluate(|@Middle("| & sourceStr & |";"| & startStr & |";"| & endStr & |")|) StrMiddle=returnInfo(0) End Function End Class |