必须显式INSERT到日志表,PRINT或RAISERROR不落盘;SQL Server中CATCH内日志需用sp_getapplock加独立锁防回滚丢失;MySQL用DECLARE HANDLER CONTINUE捕获SQLEXCEPTION;关键字段须用ERROR_NUMBER()、ERROR_PROCEDURE()、ORIGINAL_LOGIN()等稳定函数获取,禁用@@ERROR等易失效变量。

必须显式 INSERT 到日志表,PRINT 或 RAISERROR 都不落盘,错误日志会随事务回滚一起消失。
SQL Server 中用 TRY/CATCH 捕获错误但日志没写进去?
常见现象是 CATCH 块里写了 INSERT INTO ErrorLog,但主事务一回滚,这条日志也跟着没了——因为默认它被卷进了同一事务作用域。
- 务必在 CATCH 块中调用
sp_getapplock获取独立锁,例如@Resource = 'ErrorLogInsert',防止并发写冲突 -
INSERT语句本身不能放在COMMIT或ROLLBACK之后,也不能依赖外部事务状态;执行完就让它自己提交 - 日志表建议放在独立 schema(如
audit.ErrorLog),禁用触发器、外键和复杂索引,避免 INSERT 失败连锁反应 - 用
SYSDATETIME()而不是GETDATE(),避免时区或精度偏差
MySQL 存储过程中怎么捕获异常并写日志?
MySQL 不支持 TRY/CATCH,得靠 DECLARE ... HANDLER,且类型必须选对:用 CONTINUE,不是 EXIT,否则 handler 执行完存储过程就直接退出了,后续清理逻辑跑不了。
- handler 的
condition_value推荐写成SQLEXCEPTION,别用具体 SQLSTATE 码,除非你明确要捕获某类警告 -
ERROR_CODE()和ERROR_MESSAGE()在 MySQL 8.0+ 才可用;低版本只能靠自定义变量 +GET DIAGNOSTICS提取 - 日志表字段至少含
error_procedure_name、error_code、error_message、error_timestamp,时间字段设为CURRENT_TIMESTAMP自动填充 - handler 内的
INSERT必须是单条语句,或用BEGIN...END包裹,否则语法报错
日志字段哪些不能省,哪些容易填错?
光记 ERROR_MESSAGE() 没用,定位不到上下文。真正关键的是来源可靠、不随会话/连接池漂移的字段。
-
ErrorNumber必须用ERROR_NUMBER(),不用@@ERROR——后者只捕获上一条语句,跨语句就失效 -
ErrorProcedure用ERROR_PROCEDURE(),不是硬编码过程名,否则重命名后日志全错位 -
LoginName用ORIGINAL_LOGIN(),不用SUSER_SNAME(),连接池场景下后者返回的是代理账号 - 敏感参数要脱敏再记录,比如手机号存成
LEFT(@phone, 3) + '****' + RIGHT(@phone, 4),别裸存
为什么加了 TRY/CATCH 还漏记某些错误?
不是所有错误都能被捕获。严重性 20+ 的错误(如资源耗尽)可能断连,而严重性 ≤10 的警告(如“无行影响”)根本进不了 CATCH。
- 编译期错误(如表不存在、语法错)在执行前就失败,TRY/CATCH 完全不生效
- 事务中调用
SET XACT_ABORT ON后,某些错误会直接终止批处理,跳过 CATCH - 如果过程里嵌套了远程查询(OPENQUERY、链接服务器),部分错误可能被截断或转义,
ERROR_MESSAGE()返回空或截断内容 - 真正难的是“确保总能写进去”——这要求锁机制兜底、表结构极简、字段来源稳定,而不是靠 TRY/CATCH 包一层就以为万事大吉


















