必须用pragma autonomous_transaction,因为日志insert属主事务,commit/rollback会破坏调用方事务完整性;自治事务使日志操作独立提交或回滚,确保错误记录不丢失且不影响业务逻辑。
oracle 存储过程记录错误日志必须用自治事务,否则 insert + commit 会提前结束调用方的事务,导致业务数据丢失或逻辑错乱。
为什么必须用 PRAGMA AUTONOMOUS_TRANSACTION
存储过程中直接写日志表(比如 INSERT INTO TBL_PROC_ERRMSG)时,如果不加自治事务声明,该 INSERT 就属于当前主事务的一部分。一旦你随后 COMMIT 或 ROLLBACK,整个调用链的事务状态就被破坏了。
常见错误现象:
- 主过程刚插入一条业务记录,还没提交,日志写入后一
COMMIT,业务数据就提前落库了 - 主过程遇到异常
ROLLBACK,日志记录也被一起回滚,查不到任何痕迹 - 调用方是另一个存储过程或应用事务,结果被日志操作意外提交/回滚
自治事务让日志操作完全独立:它有自己的 COMMIT/ROLLBACK,不影响外部事务边界。
PROC_SAVE_ERRMSG 的参数设计要点
一个实用的日志保存过程至少要捕获四类关键信息,且类型需匹配 Oracle 异常上下文:
-
BIZ_CODE:业务标识,建议传入调用方传来的业务单号、批次号等,便于事后关联定位 -
ERR_LINE:推荐用DBMS_UTILITY.format_error_backtrace,不是$$PLSQL_LINE——后者只返回当前行号,而前者能定位到真正出错的嵌套调用位置 -
ERR_CODE:直接用SQLCODE,注意它是负数(如-1表示主键冲突),别做ABS()处理 -
MSG:用SQLERRM,它已含错误码前缀(如ORA-00001: unique constraint violated),无需再拼接
示例调用片段:
EXCEPTION
WHEN OTHERS THEN
PROC_SAVE_ERRMSG(
biz_code => 'ORDER_20260502_12345',
errorline => DBMS_UTILITY.format_error_backtrace,
errorcode => SQLCODE,
msg => SQLERRM
);
RAISE; -- 不建议静默吞掉异常,除非明确要兜底处理
END;
建表与字段长度的实际约束
日志表字段不能拍脑袋定,尤其要注意 Oracle 对 VARCHAR2 和错误消息长度的限制:
-
MSG字段至少设为VARCHAR2(4000),因为SQLERRM最长可达 4000 字节(不是字符),VARCHAR2(200)会截断关键堆栈 -
ERR_LINE推荐VARCHAR2(4000),format_error_backtrace返回多行文本,含行号、包名、调用栈,远超 10 字符 - 避免用
CLOB存日志主体——自治事务中写CLOB性能差,且部分 Oracle 版本对自治事务 +CLOB有隐式限制 - 加索引:在
BIZ_CODE和CRT_TM上建组合索引,查某批次或某时间段错误时才不卡
和 DBMS_ERRLOG 的根本区别
DBMS_ERRLOG.CREATE_ERROR_LOG 是给 DML 语句(INSERT/UPDATE/DELETE)用的,它不适用于存储过程逻辑异常(比如除零、空指针、自定义校验失败)。两者不能混用:
-
DBMS_ERRLOG只捕获 DML 执行层错误(如约束冲突、数据类型转换失败),且必须配合LOG ERRORS INTO ... REJECT LIMIT语法 - 存储过程内部异常(
WHEN OTHERS)只能靠自治事务手动捕获,DBMS_ERRLOG完全不感知 PL/SQL 块内的RAISE_APPLICATION_ERROR或未处理异常 - 不要试图在
EXCEPTION块里调用INSERT ... LOG ERRORS——语法不合法,会报PLS-00103
真正容易被忽略的一点:自治事务过程本身也可能失败(比如日志表空间满、触发器抛错),所以 PROC_SAVE_ERRMSG 内部最好再套一层 WHEN OTHERS THEN NULL,确保日志失败不会影响主逻辑——但必须记录这个“日志失败”本身,形成递归保护。











