错误严重性 16 针对 is_event_logged = 0 的事件发出警报

SEa*_*986 1 errors alerts raiserror sql-server-2016

我的错误级别 16 警报设置如下:

USE [msdb]
GO

EXEC msdb.dbo.sp_add_alert @name=N'Error - Severity 16', 
        @message_id=0, 
        @severity=16, 
        @enabled=1, 
        @delay_between_responses=0, 
        @include_event_description_in=1, 
        @category_name=N'[Uncategorized]', 
        @job_id=N'00000000-0000-0000-0000-000000000000'
Run Code Online (Sandbox Code Playgroud)

我很好奇为什么

SELECT 1/0;
Run Code Online (Sandbox Code Playgroud)

消息 8134,级别 16,状态 1,第 17 行 遇到除零错误。

没有引发错误,但是

BACKUP DATABASE MyDatabase TO DISK = 'C:\FolderThatDoesntexist'
Run Code Online (Sandbox Code Playgroud)

消息 3201,级别 16,状态 1,第 8 行 无法打开备份设备“C:\FolderThatDoesntexist”。操作系统错误 5(访问被拒绝。)。

消息 3013,级别 16,状态 1,第 8 行备份数据库异常终止。

曾是

这篇文章它引用的博客文章表明 SQL Server 只会针对记录的错误引发事件,这可以在sys.messages

所以我查询sys.messages了这些错误代码:

SELECT  severity,
        message_id,
        is_event_logged
FROM    sys.messages
WHERE   language_id = 1033 AND
        message_id IN (8134,3201,3013)
Run Code Online (Sandbox Code Playgroud)

但发现这三个都设置为0:

在此输入图像描述

为什么 3201 和 3013 会触发我的警报,而 8134 却不会?

Pre*_*hts 5

只有发送到 Windows 应用程序事件日志的事件才能用于警报。

这可以通过指定的RAISERROR时间来完成,请参阅文档WITH LOG

以下代码将生成 2 个错误,第二个错误将发送到事件日志,第一个不会。第二个将触发您的警报,第一个则不会。

RAISERROR('Test for dba.stackexchange 1', 16, 1);
RAISERROR('Test for dba.stackexchange 2', 16, 1) WITH LOG;
Run Code Online (Sandbox Code Playgroud)

事件日志

操作系统错误(如访问被拒绝、磁盘空间不足等)会记录到 Windows 的应用程序事件日志中。

除以零不会记录到 Windows 的应用程序事件日志中。

这是触发备份错误警报的消息。

SELECT *
FROM sys.messages
WHERE 1=1
AND is_event_logged = 1
AND Severity = 16
AND message_id IN (3041,18204)
AND language_id = 1033
Run Code Online (Sandbox Code Playgroud)
消息ID 语言_id 严重程度 已记录事件 文本
3041 1033 16 1 BACKUP 无法完成命令 %.*ls。检查备份应用程序日志以获取详细消息。
18204 1033 16 1 %s:备份设备“%s”未能%s。操作系统错误 %s。

SSMS 和您的查询中显示的消息不同。您可以通过提供过滤器来测试警报。message_id 在事件日志中可见: 在此输入图像描述

如何触发警报

您可以像这样包装您的代码并重新抛出异常WITH LOG,然后您的警报应该可以工作。

DECLARE 
    @ErrorMessage  NVARCHAR(4000), 
    @ErrorSeverity INT, 
    @ErrorState    INT;

BEGIN TRY
    SELECT 1/0;
END TRY
BEGIN CATCH
    SELECT 
        @ErrorMessage = ERROR_MESSAGE(), 
        @ErrorSeverity = ERROR_SEVERITY(), 
        @ErrorState = ERROR_STATE();

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState) WITH LOG;
END CATCH;
Run Code Online (Sandbox Code Playgroud)

重新抛出日志

另请阅读SQL Server 警报的备注。