Skip to content

Commit cb09255

Browse files
authored
Update the default list of auto-instrumentations to be disabled for Genesis (#425)
*Description of changes:* - Remove botocore/boto3 from default disabled instrumentations list - Add system_metrics and google-genai to disabled instrumentations - Add suppress_instrumentation() for disable botocore spans from ADOT SDK By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent 7527d84 commit cb09255

File tree

4 files changed

+198
-66
lines changed

4 files changed

+198
-66
lines changed

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/aws_opentelemetry_distro.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def _configure(self, **kwargs):
118118
os.environ.setdefault(
119119
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
120120
"http,sqlalchemy,psycopg2,pymysql,sqlite3,aiopg,asyncpg,mysql_connector,"
121-
"botocore,boto3,urllib3,requests,starlette",
121+
"urllib3,requests,starlette,system_metrics,google-genai",
122122
)
123123

124124
# Set logging auto instrumentation default

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/exporter/aws/metrics/_cloudwatch_log_client.py

Lines changed: 67 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from botocore.exceptions import ClientError
1212
from botocore.session import Session
1313

14+
from opentelemetry.instrumentation.utils import suppress_instrumentation
15+
1416
logger = logging.getLogger(__name__)
1517

1618

@@ -118,27 +120,29 @@ def _generate_log_stream_name(self) -> str:
118120

119121
def _create_log_group_if_needed(self):
120122
"""Create log group if it doesn't exist."""
121-
try:
122-
self.logs_client.create_log_group(logGroupName=self.log_group_name)
123-
logger.info("Created log group: %s", self.log_group_name)
124-
except ClientError as error:
125-
if error.response.get("Error", {}).get("Code") == "ResourceAlreadyExistsException":
126-
logger.debug("Log group %s already exists", self.log_group_name)
127-
else:
128-
logger.error("Failed to create log group %s : %s", self.log_group_name, error)
129-
raise
123+
with suppress_instrumentation():
124+
try:
125+
self.logs_client.create_log_group(logGroupName=self.log_group_name)
126+
logger.info("Created log group: %s", self.log_group_name)
127+
except ClientError as error:
128+
if error.response.get("Error", {}).get("Code") == "ResourceAlreadyExistsException":
129+
logger.debug("Log group %s already exists", self.log_group_name)
130+
else:
131+
logger.error("Failed to create log group %s : %s", self.log_group_name, error)
132+
raise
130133

131134
def _create_log_stream_if_needed(self):
132135
"""Create log stream if it doesn't exist."""
133-
try:
134-
self.logs_client.create_log_stream(logGroupName=self.log_group_name, logStreamName=self.log_stream_name)
135-
logger.info("Created log stream: %s", self.log_stream_name)
136-
except ClientError as error:
137-
if error.response.get("Error", {}).get("Code") == "ResourceAlreadyExistsException":
138-
logger.debug("Log stream %s already exists", self.log_stream_name)
139-
else:
140-
logger.error("Failed to create log stream %s : %s", self.log_stream_name, error)
141-
raise
136+
with suppress_instrumentation():
137+
try:
138+
self.logs_client.create_log_stream(logGroupName=self.log_group_name, logStreamName=self.log_stream_name)
139+
logger.info("Created log stream: %s", self.log_stream_name)
140+
except ClientError as error:
141+
if error.response.get("Error", {}).get("Code") == "ResourceAlreadyExistsException":
142+
logger.debug("Log stream %s already exists", self.log_stream_name)
143+
else:
144+
logger.error("Failed to create log stream %s : %s", self.log_stream_name, error)
145+
raise
142146

143147
def _validate_log_event(self, log_event: Dict) -> bool:
144148
"""
@@ -277,52 +281,52 @@ def _send_log_batch(self, batch: LogEventBatch) -> None:
277281
}
278282

279283
start_time = time.time()
280-
281-
try:
282-
# Make the PutLogEvents call
283-
response = self.logs_client.put_log_events(**put_log_events_input)
284-
285-
elapsed_ms = int((time.time() - start_time) * 1000)
286-
logger.debug(
287-
"Successfully sent %s log events (%s KB) in %s ms",
288-
batch.size(),
289-
batch.byte_total / 1024,
290-
elapsed_ms,
291-
)
292-
293-
return response
294-
295-
except ClientError as error:
296-
# Handle resource not found errors by creating log group/stream
297-
error_code = error.response.get("Error", {}).get("Code")
298-
if error_code == "ResourceNotFoundException":
299-
logger.info("Log group or stream not found, creating resources and retrying")
300-
301-
try:
302-
# Create log group first
303-
self._create_log_group_if_needed()
304-
# Then create log stream
305-
self._create_log_stream_if_needed()
306-
307-
# Retry the PutLogEvents call
308-
response = self.logs_client.put_log_events(**put_log_events_input)
309-
310-
elapsed_ms = int((time.time() - start_time) * 1000)
311-
logger.debug(
312-
"Successfully sent %s log events (%s KB) in %s ms after creating resources",
313-
batch.size(),
314-
batch.byte_total / 1024,
315-
elapsed_ms,
316-
)
317-
318-
return response
319-
320-
except ClientError as retry_error:
321-
logger.error("Failed to send log events after creating resources: %s", retry_error)
284+
with suppress_instrumentation():
285+
try:
286+
# Make the PutLogEvents call
287+
response = self.logs_client.put_log_events(**put_log_events_input)
288+
289+
elapsed_ms = int((time.time() - start_time) * 1000)
290+
logger.debug(
291+
"Successfully sent %s log events (%s KB) in %s ms",
292+
batch.size(),
293+
batch.byte_total / 1024,
294+
elapsed_ms,
295+
)
296+
297+
return response
298+
299+
except ClientError as error:
300+
# Handle resource not found errors by creating log group/stream
301+
error_code = error.response.get("Error", {}).get("Code")
302+
if error_code == "ResourceNotFoundException":
303+
logger.info("Log group or stream not found, creating resources and retrying")
304+
with suppress_instrumentation():
305+
try:
306+
# Create log group first
307+
self._create_log_group_if_needed()
308+
# Then create log stream
309+
self._create_log_stream_if_needed()
310+
311+
# Retry the PutLogEvents call
312+
response = self.logs_client.put_log_events(**put_log_events_input)
313+
314+
elapsed_ms = int((time.time() - start_time) * 1000)
315+
logger.debug(
316+
"Successfully sent %s log events (%s KB) in %s ms after creating resources",
317+
batch.size(),
318+
batch.byte_total / 1024,
319+
elapsed_ms,
320+
)
321+
322+
return response
323+
324+
except ClientError as retry_error:
325+
logger.error("Failed to send log events after creating resources: %s", retry_error)
326+
raise
327+
else:
328+
logger.error("Failed to send log events: %s", error)
322329
raise
323-
else:
324-
logger.error("Failed to send log events: %s", error)
325-
raise
326330

327331
def send_log_event(self, log_event: Dict[str, Any]):
328332
"""

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/exporter/aws/metrics/test_cloudwatch_log_client.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import time
77
import unittest
8-
from unittest.mock import Mock, patch
8+
from unittest.mock import MagicMock, Mock, patch
99

1010
from botocore.exceptions import ClientError
1111

@@ -579,6 +579,134 @@ def test_is_batch_active_edge_cases(self):
579579
result = self.log_client._is_batch_active(batch2, current_time)
580580
self.assertFalse(result)
581581

582+
@patch("amazon.opentelemetry.distro.exporter.aws.metrics._cloudwatch_log_client.suppress_instrumentation")
583+
def test_create_log_group_uses_suppress_instrumentation(self, mock_suppress):
584+
"""Test that _create_log_group_if_needed uses suppress_instrumentation."""
585+
# Configure the mock context manager
586+
mock_context = MagicMock()
587+
mock_suppress.return_value = mock_context
588+
mock_context.__enter__.return_value = mock_context
589+
mock_context.__exit__.return_value = None
590+
591+
# Call the method
592+
self.log_client._create_log_group_if_needed()
593+
594+
# Verify suppress_instrumentation was called
595+
mock_suppress.assert_called_once()
596+
mock_context.__enter__.assert_called_once()
597+
mock_context.__exit__.assert_called_once()
598+
599+
# Verify the AWS call happened within the context
600+
self.log_client.logs_client.create_log_group.assert_called_once_with(logGroupName="test-log-group")
601+
602+
@patch("amazon.opentelemetry.distro.exporter.aws.metrics._cloudwatch_log_client.suppress_instrumentation")
603+
def test_create_log_stream_uses_suppress_instrumentation(self, mock_suppress):
604+
"""Test that _create_log_stream_if_needed uses suppress_instrumentation."""
605+
# Configure the mock context manager
606+
mock_context = MagicMock()
607+
mock_suppress.return_value = mock_context
608+
mock_context.__enter__.return_value = mock_context
609+
mock_context.__exit__.return_value = None
610+
611+
# Call the method
612+
self.log_client._create_log_stream_if_needed()
613+
614+
# Verify suppress_instrumentation was called
615+
mock_suppress.assert_called_once()
616+
mock_context.__enter__.assert_called_once()
617+
mock_context.__exit__.assert_called_once()
618+
619+
# Verify the AWS call happened within the context
620+
self.log_client.logs_client.create_log_stream.assert_called_once()
621+
622+
@patch("amazon.opentelemetry.distro.exporter.aws.metrics._cloudwatch_log_client.suppress_instrumentation")
623+
def test_send_log_batch_uses_suppress_instrumentation(self, mock_suppress):
624+
"""Test that _send_log_batch uses suppress_instrumentation."""
625+
# Configure the mock context manager
626+
mock_context = MagicMock()
627+
mock_suppress.return_value = mock_context
628+
mock_context.__enter__.return_value = mock_context
629+
mock_context.__exit__.return_value = None
630+
631+
# Create a batch with events
632+
batch = self.log_client._create_event_batch()
633+
batch.add_event({"message": "test", "timestamp": int(time.time() * 1000)}, 10)
634+
635+
# Mock successful put_log_events
636+
self.log_client.logs_client.put_log_events.return_value = {"nextSequenceToken": "12345"}
637+
638+
# Call the method
639+
self.log_client._send_log_batch(batch)
640+
641+
# Verify suppress_instrumentation was called
642+
mock_suppress.assert_called_once()
643+
mock_context.__enter__.assert_called_once()
644+
mock_context.__exit__.assert_called_once()
645+
646+
# Verify the AWS call happened within the context
647+
self.log_client.logs_client.put_log_events.assert_called_once()
648+
649+
@patch("amazon.opentelemetry.distro.exporter.aws.metrics._cloudwatch_log_client.suppress_instrumentation")
650+
def test_send_log_batch_retry_uses_suppress_instrumentation(self, mock_suppress):
651+
"""Test that _send_log_batch retry logic also uses suppress_instrumentation."""
652+
# Configure the mock context manager
653+
mock_context = MagicMock()
654+
mock_suppress.return_value = mock_context
655+
mock_context.__enter__.return_value = mock_context
656+
mock_context.__exit__.return_value = None
657+
658+
# Create a batch with events
659+
batch = self.log_client._create_event_batch()
660+
batch.add_event({"message": "test", "timestamp": int(time.time() * 1000)}, 10)
661+
662+
# Mock put_log_events to fail first with ResourceNotFoundException, then succeed
663+
self.log_client.logs_client.put_log_events.side_effect = [
664+
ClientError({"Error": {"Code": "ResourceNotFoundException"}}, "PutLogEvents"),
665+
{"nextSequenceToken": "12345"},
666+
]
667+
668+
# Call the method
669+
self.log_client._send_log_batch(batch)
670+
671+
# Verify suppress_instrumentation was called:
672+
# 1. Initial _send_log_batch context
673+
# 2. Nested context in the retry block
674+
# 3. _create_log_group_if_needed context
675+
# 4. _create_log_stream_if_needed context
676+
self.assertEqual(mock_suppress.call_count, 4)
677+
# Each context should have been properly entered and exited
678+
self.assertEqual(mock_context.__enter__.call_count, 4)
679+
self.assertEqual(mock_context.__exit__.call_count, 4)
680+
681+
# Verify AWS calls happened
682+
self.assertEqual(self.log_client.logs_client.put_log_events.call_count, 2)
683+
self.log_client.logs_client.create_log_group.assert_called_once()
684+
self.log_client.logs_client.create_log_stream.assert_called_once()
685+
686+
@patch("amazon.opentelemetry.distro.exporter.aws.metrics._cloudwatch_log_client.suppress_instrumentation")
687+
def test_create_log_group_exception_still_uses_suppress_instrumentation(self, mock_suppress):
688+
"""Test that suppress_instrumentation is properly used even when exceptions occur."""
689+
# Configure the mock context manager
690+
mock_context = MagicMock()
691+
mock_suppress.return_value = mock_context
692+
mock_context.__enter__.return_value = mock_context
693+
mock_context.__exit__.return_value = None
694+
695+
# Make create_log_group raise an exception
696+
self.log_client.logs_client.create_log_group.side_effect = ClientError(
697+
{"Error": {"Code": "AccessDenied"}}, "CreateLogGroup"
698+
)
699+
700+
# Call should raise the exception
701+
with self.assertRaises(ClientError):
702+
self.log_client._create_log_group_if_needed()
703+
704+
# Verify suppress_instrumentation was still properly used
705+
mock_suppress.assert_called_once()
706+
mock_context.__enter__.assert_called_once()
707+
# __exit__ should be called even though an exception was raised
708+
mock_context.__exit__.assert_called_once()
709+
582710

583711
if __name__ == "__main__":
584712
unittest.main()

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_opentelemetry_distro.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def test_configure_with_agent_observability_enabled(
127127
self.assertEqual(
128128
os.environ.get("OTEL_PYTHON_DISABLED_INSTRUMENTATIONS"),
129129
"http,sqlalchemy,psycopg2,pymysql,sqlite3,aiopg,asyncpg,mysql_connector,"
130-
"botocore,boto3,urllib3,requests,starlette",
130+
"urllib3,requests,starlette,system_metrics,google-genai",
131131
)
132132
self.assertEqual(os.environ.get("OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED"), "true")
133133
self.assertEqual(os.environ.get("OTEL_AWS_APPLICATION_SIGNALS_ENABLED"), "false")

0 commit comments

Comments
 (0)