错误日志必须显式insert到专用表中,字段至少包括errornumber、errorprocedure、errorline、errormessage、errortime、loginname,并在try/catch中脱离主事务独立写入,确保持久化可追溯。

错误日志必须写进表,不能只靠 PRINT 或 RAISERROR
PRINT 输出只到客户端,不落盘;RAISERROR 默认是临时消息,不持久化。审计日志要可查、可追溯、带上下文,就必须显式 INSERT INTO 到专用表里。哪怕只是调试用的错误记录,也得走这张表——否则重启后、会话断开后,啥都找不到。
错误日志表至少要包含哪些字段
别堆字段,生产环境最常漏掉的是关键上下文和事务隔离能力:
-
ErrorNumber(INT):用ERROR_NUMBER()取,比@@ERROR更准,能跨语句捕获 -
ErrorProcedure(NVARCHAR(128)):用ERROR_PROCEDURE(),不是硬编码存储过程名 -
ErrorLine(INT):配合ERROR_LINE()定位具体哪行出错 -
ErrorMessage(NVARCHAR(4000)):直接取ERROR_MESSAGE(),别截断 -
ErrorTime(DATETIME2(3)):用SYSDATETIME(),不是GETDATE(),避免时区偏差 -
LoginName(NVARCHAR(128)):用ORIGINAL_LOGIN(),不是SUSER_SNAME(),后者在连接池下不可信
TRY/CATCH 里 INSERT 日志时,千万别卷入主事务
这是最大陷阱:把 INSERT INTO ErrorLog 写在主事务块里,一旦业务回滚,错误日志也跟着消失,等于没记。
正确做法是让日志插入脱离主事务边界:
- 用
sp_getapplock加锁防并发冲突,比如@Resource = 'ErrorLogInsert' - 日志
INSERT单独执行,TRY块里不COMMIT,也不ROLLBACK - 最后调用
sp_releaseapplock释放锁 - 确保日志表在独立 schema(如
audit.ErrorLog),无触发器、无外键、索引精简
错误日志里必须拼上原始参数和影响行数
光有错误号和消息,很难复现问题。尤其在参数驱动的通用过程里,缺了输入就等于没日志:
- 在
CATCH块里,用FOR JSON PATH把输入参数序列化为params_json字段 - 记录
@@ROWCOUNT,判断是否“静默失败”(比如UPDATE没命中任何行却没报错) - 如果过程里用了游标或循环,加个
IF @i % 50 = 0记进度,防卡死无感知 - 敏感字段(如手机号)要脱敏再记录:
LEFT(@phone, 3) + '****' + RIGHT(@phone, 4)
INSERT,而是保证它总能写进去——哪怕主逻辑崩了、事务回滚了、连接中断了,这条错误记录还得在。这需要锁机制兜底、表结构克制、字段来源可靠,而不是靠运气。











