2016-11-28 4 views
3

Ich versuche, eine systemweite Protokollierung zu implementieren, die alle fehlgeschlagenen Stored Procedure Ausführungen in unserer Dabatase protokollieren würde, und ich schaue auf Erweiterte Ereignisse.SQL Server Protokollierung fehlgeschlagener Abfragen

Ich habe einige der Forschung getan und es scheint recht einfach zu Capture-Anweisungen folgenden Code fehlgeschlagen:

--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; 

Allerdings Ich mag würde sofort in eine Tabelle fehlgeschlagen Anweisung speichern, nennen wir es Logs.Errors aber ich konnte keinen Weg finden, dies zu tun, und die obere Methode müsste als geplanter Job funktionieren.

Gerade jetzt, schauen unsere Verfahren wie folgt aus:

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 

Wo Logs.LogError Verfahren ist die Verwendung von DBCC INPUTBUFFER(); macht aber keine Parameter erfassen, nur die genaue Prozedur, die ausgeführt wurde. Das ist alles, was ich von ihm bekommen können:

+----------------------------+-----------+-----------+------------------------------+ 
|  ErrorMessage  | EventType | Parameter |   Statement   | 
+----------------------------+-----------+-----------+------------------------------+ 
| Incorrect syntax near '.'. | RPC Event |   0 | DbName.dbo.FailedProcedure;1 | 
+----------------------------+-----------+-----------+------------------------------+ 

ich nach einer Möglichkeit, entweder zu DBCC INPUTBUFFER() Arbeit machen, indem sie zwingen ganze Aussage oder XE zu erfassen Aufzeichnungen direkt in eine Tabelle einfügen, wenn das möglich ist.

Haben Sie Fragen - lassen Sie es mich wissen.

+0

Können Sie bitte näher ausführen 'er obere Methode als geplant job.' – TheGameiswar

+0

@TheGameiswar Sicher arbeiten würde. Ich hatte im Hinterkopf, dass Extended Events im Hintergrund ablaufen und Informationen über fehlgeschlagene Abfragen in einer bestimmten Datei speichern könnten. Dann könnte ich basierend auf einem Zeitplan (sagen wir stündlich) diese Datei lesen und Datensätze in die Tabelle "Logs.Errors" einfügen. Macht es jetzt mehr Sinn? –

+0

Youou muss nicht ausgeführt werden erweiterte Ereignisse starten und stoppen Ereignisse als Job, sobald Sie es starten, läuft es im Hintergrund – TheGameiswar

Antwort

2

Ich habe festgestellt, dass XEvents großartig für die Überwachung von Ereignissen sind, wie sie passieren. Sie bieten jedoch keinen Mechanismus, um beobachtete Ereignisse zu "handhaben". Um diese Lücke zu füllen, habe ich Event Notifications verwendet. Ich beschreibe sie oft als asynchrone DDL-Trigger. Ich lasse Sie entscheiden, ob diese tl;dr Definition korrekt ist oder nicht.

Wenn Sie Ereignisbenachrichtigungen versuchen möchten, hier ist ein Skript, mit dem Sie beginnen können (Entschuldigung, es ist wirklich lang). Lassen Sie mich wissen, wenn Sie irgendwelche Fragen/Probleme haben. Ich werde versuchen, so gut wie möglich zu antworten.

--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 
+0

Oh Junge. Das sieht genau nach was ich suche. Ich werde das auf meinem lokalen Computer und Feedback versuchen, wenn ich kann. Vielen Dank! –

+0

Etwas, an das ich gerade gedacht habe ... die USER_ERROR_MESSAGE kann eine Menge "Rauschen" erzeugen. Es enthält Fehler für alle Schweregrade (glaube ich). Beispielsweise finden Sie möglicherweise eine große Anzahl von Ereignissen für "Fehler" mit einem Schweregrad unter 10, die Ihnen wahrscheinlich egal sind. Dies könnte die Warteschlange schneller überfluten als SQL sie verarbeiten kann. – DMason

+0

Ist es möglich, nur fehlgeschlagene Prozeduren über bestimmten Schweregrad zu filtern? –

Verwandte Themen