我正在尝试实现一个系统范围的日志记录,它将在我们的dabatase中记录所有失败的存储过程执行,我正在查看扩展事件.
我做了一些研究,使用以下代码捕获失败的语句似乎很容易:
--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;
但是,我想立即将失败的语句存储到表中,让我们称之为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将记录直接插入到某个表中,如果可能的话.
有任何问题 – 请告诉我.
最佳答案 我发现XEvents非常适合在发生事件时监控事件.但是,它们没有提供“处理”观察事件的机制.为了填补这个空白,我使用了
Event Notifications.我经常将它们描述为异步DDL触发器.我会让你决定这个tl; dr定义是否准确.
如果你想尝试一下事件通知,这是一个你可以开始的脚本(对不起,它真的很长).如果您有任何问题/疑问,请与我们联系.我会尽力回复.
--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