如何在SQL Server中捕获指定数据库未被捕获的错误至error_table
如何在SQL Server中捕获未处理错误并保存到error_table
嘿,我来帮你搞定这个需求!要在指定SQL Server数据库里自动捕获所有没被人工TRY/CATCH处理的错误,并存到error_table中,咱们分步骤来:
第一步:创建错误日志表
首先得先建个用来存错误信息的表,我给你列了几个实用的字段,能把关键的错误细节都记录下来:
CREATE TABLE error_table ( error_id INT IDENTITY(1,1) PRIMARY KEY, error_number INT, error_message NVARCHAR(4000), error_severity INT, error_state INT, error_procedure NVARCHAR(128), error_line INT, database_name NVARCHAR(128), username NVARCHAR(128), error_datetime DATETIME DEFAULT GETDATE(), sql_statement NVARCHAR(MAX) );
第二步:用扩展事件实现全局错误捕获
如果想不用修改现有代码,全局自动捕获所有未处理的错误,**扩展事件(Extended Events)**是最佳选择——这是SQL Server官方主推的轻量级监控方案,性能开销远低于老的SQL Trace,不会给数据库添额外负担。
创建扩展事件会话
执行下面的SQL,创建一个专门捕获未处理错误的会话:
CREATE EVENT SESSION [Capture_Unhandled_Errors] ON DATABASE ADD EVENT sqlserver.error_reported( ACTION( sqlserver.database_name, sqlserver.sql_text, sqlserver.username ) WHERE ( -- 过滤掉系统内部的错误,只抓用户会话产生的错误 sqlserver.is_system = 0 -- 只捕获没被TRY/CATCH处理的错误(is_handled=0就是未处理) AND is_handled = 0 ) ) ADD TARGET package0.event_file(SET filename=N'Capture_Unhandled_Errors.xel'), ADD TARGET package0.ring_buffer WITH (STARTUP_STATE=ON); -- 确保SQL Server重启后这个会话自动启动,不用手动开
把扩展事件数据导入error_table
扩展事件会把错误数据写到.xel格式的文件里,你可以建个SQL Server代理定时作业,定期读取这些文件里的错误数据,插入到error_table中。给你个示例脚本:
DECLARE @file_path NVARCHAR(260) = N'Capture_Unhandled_Errors_*.xel'; INSERT INTO error_table ( error_number, error_message, error_severity, error_state, error_procedure, error_line, database_name, username, sql_statement ) SELECT XEventData.value('(event/data[@name="error_number"]/value)[1]', 'INT') AS error_number, XEventData.value('(event/data[@name="message"]/value)[1]', 'NVARCHAR(4000)') AS error_message, XEventData.value('(event/data[@name="severity"]/value)[1]', 'INT') AS error_severity, XEventData.value('(event/data[@name="state"]/value)[1]', 'INT') AS error_state, XEventData.value('(event/data[@name="procedure"]/value)[1]', 'NVARCHAR(128)') AS error_procedure, XEventData.value('(event/data[@name="line_number"]/value)[1]', 'INT') AS error_line, XEventData.value('(event/action[@name="database_name"]/value)[1]', 'NVARCHAR(128)') AS database_name, XEventData.value('(event/action[@name="username"]/value)[1]', 'NVARCHAR(128)') AS username, XEventData.value('(event/action[@name="sql_text"]/value)[1]', 'NVARCHAR(MAX)') AS sql_statement FROM ( SELECT CAST(event_data AS XML) AS XEventData FROM sys.fn_xe_file_target_read_file(@file_path, NULL, NULL, NULL) ) AS XEvents WHERE -- 避免重复插入已经处理过的错误 XEventData.value('(event/@timestamp)[1]', 'DATETIME') > ISNULL((SELECT MAX(error_datetime) FROM error_table), '1900-01-01');
启动扩展事件会话
最后,启动这个会话就开始捕获错误啦:
ALTER EVENT SESSION [Capture_Unhandled_Errors] ON DATABASE STATE = START;
备选方案:用TRY/CATCH包装存储过程
如果你的场景只需要覆盖存储过程里的错误,也可以用TRY/CATCH块配合统一的日志存储过程来实现——不过这个方法需要修改所有现有存储过程,适合小规模系统:
首先创建一个统一的错误日志存储过程:
CREATE PROCEDURE dbo.Log_Error AS BEGIN SET NOCOUNT ON; INSERT INTO error_table ( error_number, error_message, error_severity, error_state, error_procedure, error_line, database_name, username, sql_statement ) VALUES ( ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_SEVERITY(), ERROR_STATE(), ERROR_PROCEDURE(), ERROR_LINE(), DB_NAME(), SUSER_SNAME(), (SELECT text FROM sys.dm_exec_sql_text(ERROR_PROCEDURE())) ); END;
然后在每个存储过程里加上TRY/CATCH:
CREATE PROCEDURE dbo.Your_Business_Procedure AS BEGIN BEGIN TRY -- 这里写你的业务逻辑代码 SELECT 1/0; -- 模拟一个未处理的错误 END TRY BEGIN CATCH -- 调用日志存储过程记录错误 EXEC dbo.Log_Error; -- 可选:重新抛出错误,让上层调用方感知到错误 THROW; END CATCH END;
内容的提问来源于stack exchange,提问作者Purma Zalame
相关产品推荐
相关产品推荐

