2016-11-28 6 views
3

J'essaie d'implémenter une journalisation à l'échelle du système, qui consignerait toutes les exécutions de procédure stockée ayant échoué dans notre dabatase et j'examinerais les événements étendus.La requête de journalisation SQL Server a échoué requêtes

Je l'ai fait quelques recherches et il semble assez facile à capturer des déclarations a échoué en utilisant le code suivant:

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

Cependant, je voudrais enregistrer immédiatement instruction a échoué dans une table, nous allons l'appeler mais je Logs.Errors ne pouvait pas trouver un moyen de le faire et la méthode supérieure devrait fonctionner comme un travail planifié.

En ce moment, nos procédures ressemblent à ce que:

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 

Lorsque la procédure Logs.LogError fait usage de DBCC INPUTBUFFER(); mais il ne tient pas compte des paramètres, juste la procédure exacte qui a été exécuté. C'est tout ce que je peux obtenir de lui:

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

Je suis à la recherche d'un moyen de rendre le travail soit DBCC INPUTBUFFER() en le forçant à capturer tout ou déclaration XE pour insérer des enregistrements directement dans une table, si cela est possible.

Toutes les questions - faites le moi savoir.

+0

Pouvez-vous s'il vous plaît donner des détails sur 'il la méthode supérieure devrait travailler comme prévu job.' – TheGameiswar

+0

@TheGameiswar Bien sûr. J'avais à l'esprit que les événements étendus pourraient s'exécuter en arrière-plan et stocker des informations de requêtes échouées dans un fichier donné. Ensuite, basé sur un horaire (disons toutes les heures), je pourrais lire ce fichier et insérer des enregistrements dans la table 'Logs.Errors'. Cela a-t-il plus de sens maintenant? –

+0

Vous n'avez pas besoin d'exécuter des événements étendus démarrer et arrêter des événements en tant que travail, une fois que vous l'avez démarré, il s'exécute en arrière-plan – TheGameiswar

Répondre

2

J'ai trouvé que les XEvents sont parfaits pour surveiller les événements lorsqu'ils se produisent. Cependant, ils ne fournissent pas un mécanisme pour "gérer" les événements observés. Pour combler ce vide, j'ai utilisé Event Notifications. Je les décris souvent comme des déclencheurs DDL asynchrones. Je vais vous laisser décider si cette définition tl;dr est exacte ou non.

Si vous souhaitez essayer les notifications d'événements, voici un script avec lequel vous pouvez commencer (désolé, c'est vraiment long). Faites-moi savoir si vous avez des questions/problèmes. Je vais essayer de répondre du mieux que je peux.

--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 mon garçon. Cela ressemble exactement à ce que je cherche. Je vais essayer cela sur ma machine locale et la rétroaction juste quand je peux. Merci! –

+0

Quelque chose que je viens de penser ... le USER_ERROR_MESSAGE peut générer beaucoup de "bruit". Il comprend des erreurs pour tous les niveaux de gravité (je pense). Par exemple, vous pourriez trouver un grand nombre d'événements pour des «erreurs» avec un niveau de gravité inférieur à 10, ce qui ne vous intéressera probablement pas. Cela peut inonder la file d'attente plus rapidement que SQL peut la traiter. – DMason

+0

Est-il possible de filtrer uniquement les procédures échouées au-dessus de la gravité spécifique? –