SQL 服务器记录查询失败
SQL Server logging failed queries
我正在尝试实现系统范围的日志记录,它将在我们的数据库中记录所有失败的存储过程执行,我正在查看扩展事件。
我做了一些研究,使用以下代码似乎很容易捕获失败的语句:
--Create an extended event session
CREATE EVENT SESSION what_queries_are_failing ON SERVER
ADD EVENT sqlserver.error_reported (
ACTION (sqlserver.sql_text
, sqlserver.tsql_stack
, sqlserver.database_id
, sqlserver.username
)
WHERE ([severity] > 10)
)
ADD TARGET package0.asynchronous_file_target (
SET filename = 'C:\XEventSessions\what_queries_are_failing.xel'
, metadatafile = 'C:\XEventSessions\what_queries_are_failing.xem'
, max_file_size = 5
, max_rollover_files = 5
)
WITH (MAX_DISPATCH_LATENCY = 5 SECONDS)
GO
-- Start the session
ALTER EVENT SESSION what_queries_are_failing ON SERVER STATE = START
GO
;WITH events_cte
AS (
SELECT DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), CURRENT_TIMESTAMP), xevents.event_data.value('(event/@timestamp)[1]', 'datetime2')) AS [err_timestamp]
, xevents.event_data.value('(event/data[@name="severity"]/value)[1]', 'bigint') AS [err_severity]
, xevents.event_data.value('(event/data[@name="error_number"]/value)[1]', 'bigint') AS [err_number]
, xevents.event_data.value('(event/data[@name="message"]/value)[1]', 'nvarchar(512)') AS [err_message]
, xevents.event_data.value('(event/action[@name="sql_text"]/value)[1]', 'nvarchar(max)') AS [sql_text]
, xevents.event_data
FROM sys.fn_xe_file_target_read_file('S:\XEventSessions\what_queries_are_failing*.xel', 'S:\XEventSessions\what_queries_are_failing*.xem', NULL, NULL)
CROSS APPLY (
SELECT CAST(event_data AS XML) AS event_data
) AS xevents
)
SELECT *
FROM events_cte
ORDER BY err_timestamp;
但是我想立即将失败的语句存储到 table 中,我们称它为 Logs.Errors
但我找不到方法来做,上面的方法必须工作作为计划作业。
现在,我们的程序是这样的:
CREATE PROCEDURE [dbo].[MyProcedure]
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
SELECT 1;
END TRY
BEGIN CATCH
EXECUTE Logs.PrintError;
EXECUTE Logs.LogError;
END CATCH
END
其中 Logs.LogError
过程正在使用 DBCC INPUTBUFFER();
但它不捕获参数,只是执行的确切过程。这就是我能从中得到的全部:
+----------------------------+-----------+-----------+------------------------------+
| ErrorMessage | EventType | Parameter | Statement |
+----------------------------+-----------+-----------+------------------------------+
| Incorrect syntax near '.'. | RPC Event | 0 | DbName.dbo.FailedProcedure;1 |
+----------------------------+-----------+-----------+------------------------------+
我正在寻找一种方法来使 DBCC INPUTBUFFER()
工作,方法是强制它捕获整个语句或 XE 将记录直接插入某些 table,如果可能的话。
有任何问题 - 让我知道。
我发现 XEvents 非常适合在事件发生时对其进行监控。但是,它们不提供 "handle" 观察到的事件的机制。为了填补这个空白,我使用了 Event Notifications。我经常将它们描述为异步 DDL 触发器。我会让你决定 tl;dr
定义是否准确。
如果您想尝试事件通知,可以从这里开始(抱歉,它真的很长)。如果你有 questions/issues,请告诉我。我会尽量回复的。
--Create these objects within a database that has service broker enabled.
USE DbaData
GO
--Drop objects first before trying to create them (in specific sequence).
IF EXISTS (
SELECT *
FROM sys.services
WHERE name = 'svcUserErrorReportedNotification'
)
DROP SERVICE svcUserErrorReportedNotification;
GO
IF EXISTS (
SELECT *
FROM sys.service_queues
WHERE name = 'queUserErrorReportedNotification'
)
DROP QUEUE queUserErrorReportedNotification;
GO
IF EXISTS (
SELECT *
FROM sys.server_event_notifications
WHERE name = 'enUserErrorReportedEvents'
)
DROP EVENT NOTIFICATION enUserErrorReportedEvents
ON SERVER
GO
--Create a queue just for user error events.
CREATE QUEUE queUserErrorReportedNotification
GO
--Create a service just for user error events.
CREATE SERVICE svcUserErrorReportedNotification
ON QUEUE queUserErrorReportedNotification ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])
GO
-- Create the event notification for user error events on the service.
CREATE EVENT NOTIFICATION enUserErrorReportedEvents
ON SERVER
WITH FAN_IN
FOR USER_ERROR_MESSAGE
TO SERVICE 'svcUserErrorReportedNotification', 'current database';
GO
IF EXISTS (
SELECT *
FROM INFORMATION_SCHEMA.ROUTINES r
WHERE r.ROUTINE_SCHEMA = 'dbo' AND r.ROUTINE_NAME = 'ReceiveUserErrorReportedEvent'
)
DROP PROCEDURE dbo.ReceiveUserErrorReportedEvent
GO
CREATE PROCEDURE dbo.ReceiveUserErrorReportedEvent
/*****************************************************************************
* Name : dbo.ReceiveUserErrorReportedEvent
* Purpose : Runs when there is a USER_ERROR_MESSAGE event.
* Inputs : None
* Outputs : None
* Returns : Nothing
******************************************************************************
* Change History
* 11/28/2016 DMason Created
******************************************************************************/
AS
BEGIN
SET NOCOUNT ON
DECLARE @MsgBody XML
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION
-- Receive the next available message FROM the queue
WAITFOR (
RECEIVE TOP(1) -- just handle one message at a time
@MsgBody = CAST(message_body AS XML)
FROM queUserErrorReportedNotification
), TIMEOUT 1000 -- if the queue is empty for one second, give UPDATE and go away
-- If we didn't get anything, bail out
IF (@@ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION
BREAK
END
ELSE
BEGIN
--Grab some relevant items from the message body XML (it is EVENTDATA(), btw)
DECLARE @Login SYSNAME;
DECLARE @ErrMsgText VARCHAR(MAX);
DECLARE @ApplicationName VARCHAR(MAX);
DECLARE @Severity INT;
DECLARE @ErrorNumber INT;
DECLARE @DBName SYSNAME;
SET @Login = @MsgBody.value('(/EVENT_INSTANCE/LoginName)[1]', 'VARCHAR(128)' );
SET @ErrMsgText = @MsgBody.value('(/EVENT_INSTANCE/TextData)[1]', 'VARCHAR(MAX)' );
SET @ApplicationName = @MsgBody.value('(/EVENT_INSTANCE/ApplicationName)[1]', 'VARCHAR(MAX)' );
SET @Severity = @MsgBody.value('(/EVENT_INSTANCE/Severity)[1]', 'INT' );
SET @ErrorNumber = @MsgBody.value('(/EVENT_INSTANCE/Error)[1]', 'INT' );
SET @DBName = @MsgBody.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'VARCHAR(128)' );
--Do stuff here.
--Log to a table, etc.
/*
Commit the transaction. At any point before this, we
could roll back -- the received message would be back
on the queue AND the response wouldn't be sent.
*/
COMMIT TRANSACTION
END
END
END
GO
ALTER QUEUE dbo.queUserErrorReportedNotification
WITH
STATUS = ON,
ACTIVATION (
PROCEDURE_NAME = dbo.ReceiveUserErrorReportedEvent,
STATUS = ON,
MAX_QUEUE_READERS = 1,
EXECUTE AS OWNER)
GO
我正在尝试实现系统范围的日志记录,它将在我们的数据库中记录所有失败的存储过程执行,我正在查看扩展事件。
我做了一些研究,使用以下代码似乎很容易捕获失败的语句:
--Create an extended event session
CREATE EVENT SESSION what_queries_are_failing ON SERVER
ADD EVENT sqlserver.error_reported (
ACTION (sqlserver.sql_text
, sqlserver.tsql_stack
, sqlserver.database_id
, sqlserver.username
)
WHERE ([severity] > 10)
)
ADD TARGET package0.asynchronous_file_target (
SET filename = 'C:\XEventSessions\what_queries_are_failing.xel'
, metadatafile = 'C:\XEventSessions\what_queries_are_failing.xem'
, max_file_size = 5
, max_rollover_files = 5
)
WITH (MAX_DISPATCH_LATENCY = 5 SECONDS)
GO
-- Start the session
ALTER EVENT SESSION what_queries_are_failing ON SERVER STATE = START
GO
;WITH events_cte
AS (
SELECT DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), CURRENT_TIMESTAMP), xevents.event_data.value('(event/@timestamp)[1]', 'datetime2')) AS [err_timestamp]
, xevents.event_data.value('(event/data[@name="severity"]/value)[1]', 'bigint') AS [err_severity]
, xevents.event_data.value('(event/data[@name="error_number"]/value)[1]', 'bigint') AS [err_number]
, xevents.event_data.value('(event/data[@name="message"]/value)[1]', 'nvarchar(512)') AS [err_message]
, xevents.event_data.value('(event/action[@name="sql_text"]/value)[1]', 'nvarchar(max)') AS [sql_text]
, xevents.event_data
FROM sys.fn_xe_file_target_read_file('S:\XEventSessions\what_queries_are_failing*.xel', 'S:\XEventSessions\what_queries_are_failing*.xem', NULL, NULL)
CROSS APPLY (
SELECT CAST(event_data AS XML) AS event_data
) AS xevents
)
SELECT *
FROM events_cte
ORDER BY err_timestamp;
但是我想立即将失败的语句存储到 table 中,我们称它为 Logs.Errors
但我找不到方法来做,上面的方法必须工作作为计划作业。
现在,我们的程序是这样的:
CREATE PROCEDURE [dbo].[MyProcedure]
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
SELECT 1;
END TRY
BEGIN CATCH
EXECUTE Logs.PrintError;
EXECUTE Logs.LogError;
END CATCH
END
其中 Logs.LogError
过程正在使用 DBCC INPUTBUFFER();
但它不捕获参数,只是执行的确切过程。这就是我能从中得到的全部:
+----------------------------+-----------+-----------+------------------------------+
| ErrorMessage | EventType | Parameter | Statement |
+----------------------------+-----------+-----------+------------------------------+
| Incorrect syntax near '.'. | RPC Event | 0 | DbName.dbo.FailedProcedure;1 |
+----------------------------+-----------+-----------+------------------------------+
我正在寻找一种方法来使 DBCC INPUTBUFFER()
工作,方法是强制它捕获整个语句或 XE 将记录直接插入某些 table,如果可能的话。
有任何问题 - 让我知道。
我发现 XEvents 非常适合在事件发生时对其进行监控。但是,它们不提供 "handle" 观察到的事件的机制。为了填补这个空白,我使用了 Event Notifications。我经常将它们描述为异步 DDL 触发器。我会让你决定 tl;dr
定义是否准确。
如果您想尝试事件通知,可以从这里开始(抱歉,它真的很长)。如果你有 questions/issues,请告诉我。我会尽量回复的。
--Create these objects within a database that has service broker enabled.
USE DbaData
GO
--Drop objects first before trying to create them (in specific sequence).
IF EXISTS (
SELECT *
FROM sys.services
WHERE name = 'svcUserErrorReportedNotification'
)
DROP SERVICE svcUserErrorReportedNotification;
GO
IF EXISTS (
SELECT *
FROM sys.service_queues
WHERE name = 'queUserErrorReportedNotification'
)
DROP QUEUE queUserErrorReportedNotification;
GO
IF EXISTS (
SELECT *
FROM sys.server_event_notifications
WHERE name = 'enUserErrorReportedEvents'
)
DROP EVENT NOTIFICATION enUserErrorReportedEvents
ON SERVER
GO
--Create a queue just for user error events.
CREATE QUEUE queUserErrorReportedNotification
GO
--Create a service just for user error events.
CREATE SERVICE svcUserErrorReportedNotification
ON QUEUE queUserErrorReportedNotification ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])
GO
-- Create the event notification for user error events on the service.
CREATE EVENT NOTIFICATION enUserErrorReportedEvents
ON SERVER
WITH FAN_IN
FOR USER_ERROR_MESSAGE
TO SERVICE 'svcUserErrorReportedNotification', 'current database';
GO
IF EXISTS (
SELECT *
FROM INFORMATION_SCHEMA.ROUTINES r
WHERE r.ROUTINE_SCHEMA = 'dbo' AND r.ROUTINE_NAME = 'ReceiveUserErrorReportedEvent'
)
DROP PROCEDURE dbo.ReceiveUserErrorReportedEvent
GO
CREATE PROCEDURE dbo.ReceiveUserErrorReportedEvent
/*****************************************************************************
* Name : dbo.ReceiveUserErrorReportedEvent
* Purpose : Runs when there is a USER_ERROR_MESSAGE event.
* Inputs : None
* Outputs : None
* Returns : Nothing
******************************************************************************
* Change History
* 11/28/2016 DMason Created
******************************************************************************/
AS
BEGIN
SET NOCOUNT ON
DECLARE @MsgBody XML
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION
-- Receive the next available message FROM the queue
WAITFOR (
RECEIVE TOP(1) -- just handle one message at a time
@MsgBody = CAST(message_body AS XML)
FROM queUserErrorReportedNotification
), TIMEOUT 1000 -- if the queue is empty for one second, give UPDATE and go away
-- If we didn't get anything, bail out
IF (@@ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION
BREAK
END
ELSE
BEGIN
--Grab some relevant items from the message body XML (it is EVENTDATA(), btw)
DECLARE @Login SYSNAME;
DECLARE @ErrMsgText VARCHAR(MAX);
DECLARE @ApplicationName VARCHAR(MAX);
DECLARE @Severity INT;
DECLARE @ErrorNumber INT;
DECLARE @DBName SYSNAME;
SET @Login = @MsgBody.value('(/EVENT_INSTANCE/LoginName)[1]', 'VARCHAR(128)' );
SET @ErrMsgText = @MsgBody.value('(/EVENT_INSTANCE/TextData)[1]', 'VARCHAR(MAX)' );
SET @ApplicationName = @MsgBody.value('(/EVENT_INSTANCE/ApplicationName)[1]', 'VARCHAR(MAX)' );
SET @Severity = @MsgBody.value('(/EVENT_INSTANCE/Severity)[1]', 'INT' );
SET @ErrorNumber = @MsgBody.value('(/EVENT_INSTANCE/Error)[1]', 'INT' );
SET @DBName = @MsgBody.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'VARCHAR(128)' );
--Do stuff here.
--Log to a table, etc.
/*
Commit the transaction. At any point before this, we
could roll back -- the received message would be back
on the queue AND the response wouldn't be sent.
*/
COMMIT TRANSACTION
END
END
END
GO
ALTER QUEUE dbo.queUserErrorReportedNotification
WITH
STATUS = ON,
ACTIVATION (
PROCEDURE_NAME = dbo.ReceiveUserErrorReportedEvent,
STATUS = ON,
MAX_QUEUE_READERS = 1,
EXECUTE AS OWNER)
GO