Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

SQL Server 2008 : Récupérer la requête et la pile d’appel suite à une erreur (Utilisation des XEvent)

Voilà une tâche est quasi-impossible avec les versions précédentes de SQL Server ou nécessiterait une habileté hors du commun avec le Profiler de SQL Server. Personnellement je n'ai jamais essayé même. Sachant que dans le Profiler il est possible de récupérer le message d'erreur ou le numéro de l'erreur ou la requête exécuté, mais pas tout, le seul moyen est de jeter à un coup d'œil aux événements start(SP/Stmt/etc.) et end d'une session et de voir quel erreur ont été levée entre temps.

Autant chercher une aiguille dans une botte de foin sur un serveur en production, si vous suspectez des erreurs. Il existe en fait une commande DBCC que je ne relaterais pas ici qui permet de réaliser un DUMP (empreinte mémoire de la pile d'appel) en cas de réalisation d'une erreur, qui présente plus d'inconvénient que d'intérêt réel.

SQL Server 2008 arrive avec les eXtended Events (XEvents) qui sont un système de trace bien plus efficace que celui utilisé par le SQL Profiler (et qui le remplacera à termes d'ailleurs). Celui-ci permet d'indiquer les événements à suivre, rien de neuf me direz vous, sauf que l'on descend à un niveau bien plus bas que les événements actuels. Il y a possibilité d'ajouter des actions à des événements (récupérer plus d'infos, attendre un autre évènement, etc.) et des cibles pour consigner le résultat de tout cela.

Voyons l'exemple concret qui nous intéresse ici. Je souhaite tracer des erreurs ayant lieux sur mon serveur. Pour la liste des événements disponibles sous votre version /édition :

SELECT * FROM sys.dm_xe_objects
WHERE object_type = 'event'

Un événement s'appelle justement : « error_reported ». Quelles informations peut ont y trouver ?

SELECT * FROM sys.dm_xe_object_columns
WHERE object_name = 'error_reported'

Rien de neuf ici, le message, la sévérité et le numéro de l'erreur, mais comment trouver la requête et même mieux, la pile d'appel ? La réponse se situe dans les actions à exécuter quand cet évenement sera rencontré.

SELECT * FROM sys.dm_xe_objects
WHERE object_type = 'action'

Deux ici m'interpelle :

  • sql_text : Collect SQL text
  • tsql_stack : Collect Transact-SQL stack

Ces 2 actions semblent bien fournir les informations dont j'ai besoin, passons à la création du XEvent :

CREATE EVENT SESSION [error_trapping] ON SERVER
ADD
EVENT

sqlserver.error_reported

(

ACTION (sqlserver.sql_text,sqlserver.tsql_stack)

WHERE ( error_reported.severity > 10 )

  )

ADD TARGET package0.ring_buffer

(
SET max_memory=4096
)

WITH (

MAX_MEMORY = 4096KB,
EVENT_RETENTION_MODE = ALLOW_MULTIPLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 1 SECONDS,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = OFF

);

Dans mon exemple, je veux tracer l'événement « error_reported », mais uniquement quand la sévérité est supérieure à 10, histoire de ne pas traiter tous les avertissements divers et variés que le serveur peut me renvoyer (ex : La base de données actuelle a changée, etc.). La cible est une zone mémoire limitée en taille, ici 4 Mo alimentée de manière à ne pas gêner le serveur (le ALLOW_MULTIPLE_EVENT_LOSS vous le garantie, on préfère perdre du trace que gêner le comportement du serveur). Le XEvent est crée non démarré.

Pour le démarrer justement (pour stopper, remplacer START par STOP) :

ALTER EVENT SESSION [error_trapping] ON SERVER STATE=START;

Et maintenant on teste en simulant une erreur toute bête :

select 1 / 0

Maintenant comment accède t'on au Trace ? C'est là que l'on espère une intégration prochaine au SQL Profiler pour plus de facilité. C'est dépendant de la cible, ici c'est une zone mémoire contenant de l'XML que je vais requêter comme cela :

DECLARE @xml xml

SELECT @xml = CONVERT(XML, st.target_data)
FROM sys.dm_xe_sessions s
    JOIN sys.dm_xe_session_targets st ON s.address = st.event_session_address
WHERE s.name = 'error_trapping'

SELECT @xml

Le résultat n'étant pas très lisible ont va le retraiter dans la foulée.

< RingBufferTarget eventsPerSec ="0" processingTime="0" totalEventsProcessed="7" eventCount="7" droppedCount="0" memoryUsed="3983">
<
event name="error_reported" package="sqlserver" id="100" version="1" timestamp="2009-03-26T16:36:48.027Z">
<
data name="error">
<
type name="int32" package="package0" />
<
value>28005</value>

Avec des jolies requêtes XQuery.

DECLARE @xml xml

SELECT @xml = CONVERT(XML, st.target_data)
FROM sys.dm_xe_sessions s
    JOIN sys.dm_xe_session_targets st ON s.address = st.event_session_address
WHERE s.name = 'error_trapping'

SELECT x.value('@timestamp', 'datetime') as error_datetime,
     x.value('(data[@name="error"]/value)[1]', 'int') as [error_number],
     x.value('(data[@name="severity"]/value)[1]', 'int') as [error_severity],
     x.value('(data[@name="message"]/value)[1]', 'nvarchar(max)') as [error_message]
FROM @xml.nodes('RingBufferTarget/event') as T(x)

Ici j'ai uniquement récupérer les informations de base, il est a peine plus difficile d'ajouter la requête est la pile d'appel :

DECLARE @xml xml

SELECT @xml = CONVERT(XML, st.target_data)
FROM sys.dm_xe_sessions s
    JOIN sys.dm_xe_session_targets st ON s.address = st.event_session_address
WHERE s.name = 'error_trapping'

SELECT x.value('@timestamp', 'datetime') as error_datetime,
     x.value('(data[@name="error"]/value)[1]', 'int') as [error_number],
     x.value('(data[@name="severity"]/value)[1]', 'int') as [error_severity],
     x.value('(data[@name="message"]/value)[1]', 'nvarchar(max)') as [error_message],
     x.value('(action[@name="sql_text"]/value)[1]', 'nvarchar(max)') as [error_statement],
     x.value('(action[@name="tsql_stack"]/value)[1]', 'nvarchar(max)') as [error_stack]
FROM @xml.nodes('RingBufferTarget/event') as T(x)

Ce qui vous donne à l'exécution, quelque chose ressemblant à ceci :

2009-03-26 16:46:21.070

8134

16

Divide by zero error encountered.

select 1 / 0

<frame level='1' handle='0x01000100EB55693380EE5B86000000000000000000000000' line='1' offsetStart='0' offsetEnd='-1'/>

A utiliser sans modérations ou presque, l'impact des XEvents est bien plus faible qu'un Trace classique sur un serveur. Le réglage de mon exemple privilégie de toute façon le fonctionnement normal du serveur plutôt que le Trace.

Je travaille sur d'autres exemples utiles...

Bon profiling...

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Publié jeudi 26 mars 2009 17:50 par christian
Classé sous : ,

Commentaires

Pas de commentaires
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01