Skip to content

Commit 1437a6a

Browse files
SQLServer XE add missing event fields (#20293)
* fill in extra fields * move primary field to sqlserver struct for RQT events * changelog
1 parent 3e9da90 commit 1437a6a

File tree

4 files changed

+84
-6
lines changed

4 files changed

+84
-6
lines changed

sqlserver/changelog.d/20293.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fill in missing fields for XE events

sqlserver/datadog_checks/sqlserver/xe_collection/base.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,17 @@ def _create_event_payload(self, raw_event):
430430
if 'query_signature' in raw_event:
431431
normalized_event['query_signature'] = raw_event['query_signature']
432432

433+
# Add primary_sql_field if available
434+
if 'primary_sql_field' in raw_event:
435+
normalized_event['primary_sql_field'] = raw_event['primary_sql_field']
436+
437+
# Add metadata if available
438+
normalized_event['metadata'] = {
439+
'tables': raw_event.get('dd_tables'),
440+
'commands': raw_event.get('dd_commands'),
441+
'comments': raw_event.get('dd_comments'),
442+
}
443+
433444
return {
434445
"host": self._check.resolved_hostname,
435446
"database_instance": self._check.database_identifier,
@@ -518,7 +529,11 @@ def run_job(self):
518529
for event in events:
519530
try:
520531
# Obfuscate SQL fields and get the raw statement
521-
obfuscated_event, raw_sql_fields = self._obfuscate_sql_fields(event)
532+
obfuscated_event, raw_sql_fields, primary_sql_field = self._obfuscate_sql_fields(event)
533+
534+
# Add primary SQL field to the event if available
535+
if primary_sql_field:
536+
obfuscated_event['primary_sql_field'] = primary_sql_field
522537

523538
# Create a properly structured payload for the individual event
524539
payload = self._create_event_payload(obfuscated_event)
@@ -607,6 +622,7 @@ def _obfuscate_sql_fields(self, event):
607622
"""SQL field obfuscation and signature creation"""
608623
obfuscated_event = event.copy()
609624
raw_sql_fields = {}
625+
primary_sql_field = None
610626

611627
# Get SQL fields for this event type
612628
sql_fields = self.get_sql_fields(event.get('event_name', ''))
@@ -636,8 +652,9 @@ def _obfuscate_sql_fields(self, event):
636652
obfuscated_event['dd_comments'].extend(result['metadata']['comments'])
637653

638654
# Compute query_signature and raw_query_signature from the primary field
639-
primary_field = self._get_primary_sql_field(event)
640-
if field == primary_field or 'query_signature' not in obfuscated_event:
655+
current_primary_field = self._get_primary_sql_field(event)
656+
if field == current_primary_field or 'query_signature' not in obfuscated_event:
657+
primary_sql_field = field # Store the field used for signature
641658
obfuscated_event['query_signature'] = compute_sql_signature(result['query'])
642659
raw_signature = compute_sql_signature(event[field])
643660
raw_sql_fields['raw_query_signature'] = raw_signature
@@ -652,7 +669,7 @@ def _obfuscate_sql_fields(self, event):
652669
if 'dd_comments' in obfuscated_event:
653670
obfuscated_event['dd_comments'] = list(set(obfuscated_event['dd_comments']))
654671

655-
return obfuscated_event, raw_sql_fields if raw_sql_fields else None
672+
return obfuscated_event, raw_sql_fields if raw_sql_fields else None, primary_sql_field
656673

657674
def _get_primary_sql_field(self, event):
658675
"""
@@ -698,7 +715,7 @@ def _create_rqt_event(self, event, raw_sql_fields, query_details):
698715
return None
699716

700717
# Get the primary SQL field for this event type
701-
primary_field = self._get_primary_sql_field(event)
718+
primary_field = event.get('primary_sql_field') or self._get_primary_sql_field(event)
702719
if not primary_field or primary_field not in raw_sql_fields:
703720
self._log.debug(
704721
f"Skipping RQT event creation: Primary SQL field {primary_field} not found in raw_sql_fields"
@@ -729,6 +746,7 @@ def _create_rqt_event(self, event, raw_sql_fields, query_details):
729746
"session_id": event.get("session_id"),
730747
"xe_type": event.get("event_name"),
731748
"event_fire_timestamp": query_details.get("event_fire_timestamp"),
749+
"primary_sql_field": primary_field,
732750
}
733751

734752
# Only include duration and query_start for non-error events

sqlserver/tests/test_integration.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,22 @@ def test_xe_collection_integration(aggregator, dd_run_check, bob_conn, instance_
10391039
assert 'raw_query_signature' in query_details, "raw_query_signature not found in query details"
10401040
assert query_details.get('raw_query_signature'), "raw_query_signature is empty"
10411041

1042+
# Verify primary_sql_field is present
1043+
assert 'primary_sql_field' in query_details, "primary_sql_field not found in query details"
1044+
assert query_details.get('primary_sql_field') in [
1045+
'batch_text',
1046+
'sql_text',
1047+
'statement',
1048+
], f"Unexpected primary_sql_field value: {query_details.get('primary_sql_field')}"
1049+
1050+
# Verify metadata is present
1051+
assert 'metadata' in query_details, "metadata not found in query details"
1052+
metadata = query_details.get('metadata', {})
1053+
assert isinstance(metadata, dict), "metadata is not a dictionary"
1054+
assert 'tables' in metadata, "tables not found in metadata"
1055+
assert 'commands' in metadata, "commands not found in metadata"
1056+
assert 'comments' in metadata, "comments not found in metadata"
1057+
10421058
assert found_test_query, "Could not find our specific test query in the completion events"
10431059

10441060
# Verify specific error event details
@@ -1058,4 +1074,20 @@ def test_xe_collection_integration(aggregator, dd_run_check, bob_conn, instance_
10581074
assert 'raw_query_signature' in query_details, "raw_query_signature not found in error query details"
10591075
assert query_details.get('raw_query_signature'), "raw_query_signature is empty"
10601076

1077+
# Verify primary_sql_field is present
1078+
assert 'primary_sql_field' in query_details, "primary_sql_field not found in error query details"
1079+
assert query_details.get('primary_sql_field') in [
1080+
'batch_text',
1081+
'sql_text',
1082+
'statement',
1083+
], f"Unexpected primary_sql_field value: {query_details.get('primary_sql_field')}"
1084+
1085+
# Verify metadata is present
1086+
assert 'metadata' in query_details, "metadata not found in error query details"
1087+
metadata = query_details.get('metadata', {})
1088+
assert isinstance(metadata, dict), "metadata is not a dictionary"
1089+
assert 'tables' in metadata, "tables not found in metadata"
1090+
assert 'commands' in metadata, "commands not found in metadata"
1091+
assert 'comments' in metadata, "comments not found in metadata"
1092+
10611093
assert found_error_query, "Could not find our specific error query in the error events"

sqlserver/tests/test_xe_collection.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ def test_obfuscate_sql_fields(self, mock_compute_signature, mock_obfuscate, quer
733733
'sql_text': 'SELECT * FROM Customers WHERE CustomerId = 123',
734734
}
735735

736-
obfuscated_event, raw_sql_fields = query_completion_handler._obfuscate_sql_fields(event)
736+
obfuscated_event, raw_sql_fields, primary_sql_field = query_completion_handler._obfuscate_sql_fields(event)
737737

738738
# Verify obfuscated fields
739739
assert obfuscated_event['batch_text'] == 'SELECT * FROM Customers WHERE CustomerId = ?'
@@ -747,6 +747,9 @@ def test_obfuscate_sql_fields(self, mock_compute_signature, mock_obfuscate, quer
747747
assert raw_sql_fields['sql_text'] == 'SELECT * FROM Customers WHERE CustomerId = 123'
748748
assert raw_sql_fields['raw_query_signature'] == 'abc123'
749749

750+
# Verify primary SQL field
751+
assert primary_sql_field == 'batch_text'
752+
750753
# Verify raw_query_signature is added to the obfuscated event when collect_raw_query is enabled
751754
assert 'raw_query_signature' in obfuscated_event
752755
assert obfuscated_event['raw_query_signature'] == 'abc123'
@@ -836,6 +839,10 @@ def test_create_event_payload(self, mock_agent, query_completion_handler):
836839
'database_name': 'TestDB',
837840
'batch_text': 'SELECT * FROM Customers WHERE CustomerId = 123',
838841
'query_signature': 'abc123',
842+
'primary_sql_field': 'batch_text',
843+
'dd_tables': ['Customers'],
844+
'dd_commands': ['SELECT'],
845+
'dd_comments': [],
839846
}
840847

841848
# Create payload
@@ -854,6 +861,14 @@ def test_create_event_payload(self, mock_agent, query_completion_handler):
854861
assert query_details['request_id'] == 456
855862
assert query_details['database_name'] == 'TestDB'
856863
assert query_details['query_signature'] == 'abc123'
864+
assert query_details['primary_sql_field'] == 'batch_text'
865+
866+
# Verify metadata structure
867+
assert 'metadata' in query_details
868+
metadata = query_details['metadata']
869+
assert metadata['tables'] == ['Customers']
870+
assert metadata['commands'] == ['SELECT']
871+
assert metadata['comments'] == []
857872

858873
@patch('datadog_checks.sqlserver.xe_collection.base.datadog_agent')
859874
def test_create_rqt_event(self, mock_agent, query_completion_handler):
@@ -869,6 +884,10 @@ def test_create_rqt_event(self, mock_agent, query_completion_handler):
869884
'database_name': 'TestDB',
870885
'batch_text': 'SELECT * FROM Customers WHERE CustomerId = ?',
871886
'query_signature': 'abc123',
887+
'primary_sql_field': 'batch_text',
888+
'dd_tables': ['Customers'],
889+
'dd_commands': ['SELECT'],
890+
'dd_comments': [],
872891
}
873892

874893
# Create raw SQL fields
@@ -892,12 +911,20 @@ def test_create_rqt_event(self, mock_agent, query_completion_handler):
892911
assert rqt_event['db']['raw_query_signature'] == 'def456'
893912
assert rqt_event['db']['statement'] == 'SELECT * FROM Customers WHERE CustomerId = 123'
894913

914+
# Verify metadata is present in the RQT event (RQT events already have this structure)
915+
assert 'metadata' in rqt_event['db']
916+
metadata = rqt_event['db']['metadata']
917+
assert metadata['tables'] == ['Customers']
918+
assert metadata['commands'] == ['SELECT']
919+
assert metadata['comments'] == []
920+
895921
# Verify sqlserver fields
896922
assert rqt_event['sqlserver']['session_id'] == 123
897923
assert rqt_event['sqlserver']['xe_type'] == 'sql_batch_completed'
898924
assert rqt_event['sqlserver']['event_fire_timestamp'] == '2023-01-01T12:00:00.123Z'
899925
assert rqt_event['sqlserver']['duration_ms'] == 10.0
900926
assert rqt_event['sqlserver']['query_start'] == '2023-01-01T11:59:50.123Z'
927+
assert rqt_event['sqlserver']['primary_sql_field'] == 'batch_text'
901928

902929
def test_create_rqt_event_disabled(self, mock_check, mock_config):
903930
"""Test RQT event creation when disabled"""

0 commit comments

Comments
 (0)