From f491545afd00fed75abb7d9324bbe52452cdf71f Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Mon, 23 Jun 2025 17:57:26 +0000 Subject: [PATCH 1/8] Initial commit to replace deprecated events API/ SDK with logs. --- .../instrumentation/botocore/__init__.py | 28 +++++++-------- .../botocore/extensions/bedrock.py | 36 +++++++++---------- .../botocore/extensions/bedrock_utils.py | 16 ++++----- .../botocore/extensions/types.py | 6 ++-- .../tests/bedrock_utils.py | 2 +- .../tests/conftest.py | 16 ++++----- .../tests/test_botocore_bedrock.py | 1 + .../tests/test_botocore_dynamodb.py | 4 +-- 8 files changed, 54 insertions(+), 55 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py index 0649d62893..eb047a45c7 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py @@ -88,7 +88,7 @@ def response_hook(span, service_name, operation_name, result): from botocore.exceptions import ClientError from wrapt import wrap_function_wrapper -from opentelemetry._events import get_event_logger +from opentelemetry._logs import get_logger from opentelemetry.instrumentation.botocore.extensions import ( _find_extension, _has_extension, @@ -139,8 +139,8 @@ def _instrument(self, **kwargs): # tracers are lazy initialized per-extension in _get_tracer self._tracers = {} - # event_loggers are lazy initialized per-extension in _get_event_logger - self._event_loggers = {} + # loggers are lazy initialized per-extension in _get_logger + self._loggers = {} # meters are lazy initialized per-extension in _get_meter self._meters = {} # metrics are lazy initialized per-extension in _get_metrics @@ -154,7 +154,7 @@ def _instrument(self, **kwargs): self.propagator = propagator self.tracer_provider = kwargs.get("tracer_provider") - self.event_logger_provider = kwargs.get("event_logger_provider") + self.logger_provider = kwargs.get("logger_provider") self.meter_provider = kwargs.get("meter_provider") wrap_function_wrapper( @@ -195,23 +195,23 @@ def _get_tracer(self, extension: _AwsSdkExtension): ) return self._tracers[instrumentation_name] - def _get_event_logger(self, extension: _AwsSdkExtension): - """This is a multiplexer in order to have an event logger per extension""" + def _get_logger(self, extension: _AwsSdkExtension): + """This is a multiplexer in order to have a logger per extension""" instrumentation_name = self._get_instrumentation_name(extension) - event_logger = self._event_loggers.get(instrumentation_name) - if event_logger: - return event_logger + logger = self._loggers.get(instrumentation_name) + if logger: + return logger schema_version = extension.event_logger_schema_version() - self._event_loggers[instrumentation_name] = get_event_logger( + self._loggers[instrumentation_name] = get_logger( instrumentation_name, "", schema_url=f"https://opentelemetry.io/schemas/{schema_version}", - event_logger_provider=self.event_logger_provider, + logger_provider=self.logger_provider, ) - return self._event_loggers[instrumentation_name] + return self._loggers[instrumentation_name] def _get_meter(self, extension: _AwsSdkExtension): """This is a multiplexer in order to have a meter per extension""" @@ -287,11 +287,11 @@ def _patched_api_call(self, original_func, instance, args, kwargs): end_span_on_exit = extension.should_end_span_on_exit() tracer = self._get_tracer(extension) - event_logger = self._get_event_logger(extension) + logger = self._get_logger(extension) meter = self._get_meter(extension) metrics = self._get_metrics(extension, meter) instrumentor_ctx = _BotocoreInstrumentorContext( - event_logger=event_logger, + logger=logger, metrics=metrics, ) with tracer.start_as_current_span( diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py index d4c06fed16..c626728abb 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock.py @@ -457,9 +457,9 @@ def before_service_call( messages = self._get_request_messages() for message in messages: - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger for event in message_to_event(message, capture_content): - event_logger.emit(event) + logger.emit(event) if span.is_recording(): operation_name = span.attributes.get(GEN_AI_OPERATION_NAME, "") @@ -501,12 +501,12 @@ def _converse_on_success( # In case of an early stream closure, the result may not contain outputs if self._stream_has_output_content(result): - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_converse(result, capture_content) # this path is used by streaming apis, in that case we are already out of the span # context so need to add the span context manually span_ctx = span.get_span_context() - event_logger.emit( + logger.emit( choice.to_choice_event( trace_id=span_ctx.trace_id, span_id=span_ctx.span_id, @@ -729,11 +729,11 @@ def _handle_amazon_titan_response( [result["completionReason"]], ) - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_invoke_amazon_titan( response_body, capture_content ) - event_logger.emit(choice.to_choice_event()) + logger.emit(choice.to_choice_event()) metrics = instrumentor_context.metrics metrics_attributes = self._extract_metrics_attributes() @@ -791,9 +791,9 @@ def _handle_amazon_nova_response( # In case of an early stream closure, the result may not contain outputs if self._stream_has_output_content(response_body): - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_converse(response_body, capture_content) - event_logger.emit(choice.to_choice_event()) + logger.emit(choice.to_choice_event()) metrics = instrumentor_context.metrics metrics_attributes = self._extract_metrics_attributes() @@ -848,11 +848,11 @@ def _handle_anthropic_claude_response( GEN_AI_RESPONSE_FINISH_REASONS, [response_body["stop_reason"]] ) - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_invoke_anthropic_claude( response_body, capture_content ) - event_logger.emit(choice.to_choice_event()) + logger.emit(choice.to_choice_event()) metrics = instrumentor_context.metrics metrics_attributes = self._extract_metrics_attributes() @@ -903,11 +903,11 @@ def _handle_cohere_command_r_response( [response_body["finish_reason"]], ) - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_invoke_cohere_command_r( response_body, capture_content ) - event_logger.emit(choice.to_choice_event()) + logger.emit(choice.to_choice_event()) def _handle_cohere_command_response( self, @@ -929,11 +929,11 @@ def _handle_cohere_command_response( [generations["finish_reason"]], ) - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_invoke_cohere_command( response_body, capture_content ) - event_logger.emit(choice.to_choice_event()) + logger.emit(choice.to_choice_event()) def _handle_meta_llama_response( self, @@ -956,9 +956,9 @@ def _handle_meta_llama_response( GEN_AI_RESPONSE_FINISH_REASONS, [response_body["stop_reason"]] ) - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_invoke_meta_llama(response_body, capture_content) - event_logger.emit(choice.to_choice_event()) + logger.emit(choice.to_choice_event()) def _handle_mistral_ai_response( self, @@ -979,11 +979,11 @@ def _handle_mistral_ai_response( GEN_AI_RESPONSE_FINISH_REASONS, [outputs["stop_reason"]] ) - event_logger = instrumentor_context.event_logger + logger = instrumentor_context.logger choice = _Choice.from_invoke_mistral_mistral( response_body, capture_content ) - event_logger.emit(choice.to_choice_event()) + logger.emit(choice.to_choice_event()) def on_error( self, diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py index 743827910e..cdda0c0761 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py @@ -22,7 +22,7 @@ from botocore.eventstream import EventStream, EventStreamError from wrapt import ObjectProxy -from opentelemetry._events import Event +from opentelemetry._logs import LogRecord from opentelemetry.instrumentation.botocore.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, ) @@ -492,7 +492,7 @@ def extract_tool_results( def message_to_event( message: dict[str, Any], capture_content: bool -) -> Iterator[Event]: +) -> Iterator[LogRecord]: attributes = {GEN_AI_SYSTEM: GenAiSystemValues.AWS_BEDROCK.value} role = message.get("role") content = message.get("content") @@ -507,14 +507,14 @@ def message_to_event( elif role == "user": # in case of tool calls we send one tool event for tool call and one for the user event for tool_body in extract_tool_results(message, capture_content): - yield Event( - name="gen_ai.tool.message", + yield LogRecord( + event_name="gen_ai.tool.message", attributes=attributes, body=tool_body, ) - yield Event( - name=f"gen_ai.{role}.message", + yield LogRecord( + event_name=f"gen_ai.{role}.message", attributes=attributes, body=body if body else None, ) @@ -617,9 +617,9 @@ def _to_body_dict(self) -> dict[str, Any]: "message": self.message, } - def to_choice_event(self, **event_kwargs) -> Event: + def to_choice_event(self, **event_kwargs) -> LogRecord: attributes = {GEN_AI_SYSTEM: GenAiSystemValues.AWS_BEDROCK.value} - return Event( + return LogRecord( name="gen_ai.choice", attributes=attributes, body=self._to_body_dict(), diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/types.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/types.py index 9017e79612..acc33bcf0a 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/types.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/types.py @@ -17,7 +17,7 @@ import logging from typing import Any, Dict, Optional, Tuple -from opentelemetry._events import EventLogger +from opentelemetry._logs import Logger from opentelemetry.metrics import Instrument, Meter from opentelemetry.trace import SpanKind from opentelemetry.trace.span import Span @@ -96,10 +96,10 @@ def _get_attr(obj, name: str, default=None): class _BotocoreInstrumentorContext: def __init__( self, - event_logger: EventLogger, + logger: Logger, metrics: Dict[str, Instrument] | None = None, ): - self.event_logger = event_logger + self.logger = logger self.metrics = metrics or {} diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py index f07ea13877..7f92308456 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py @@ -292,7 +292,7 @@ def assert_log_parent(log, span): def assert_message_in_logs(log, event_name, expected_content, parent_span): assert ( log.log_record.attributes[EventAttributes.EVENT_NAME] == event_name - ), log.log_record.attributes[EventAttributes.EVENT_NAME] + ) assert ( log.log_record.attributes[GenAIAttributes.GEN_AI_SYSTEM] == GenAIAttributes.GenAiSystemValues.AWS_BEDROCK.value diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py index 0c3f98fe2f..c71a106a7a 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py @@ -11,7 +11,6 @@ from opentelemetry.instrumentation.botocore.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, ) -from opentelemetry.sdk._events import EventLoggerProvider from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk._logs.export import ( InMemoryLogExporter, @@ -55,13 +54,12 @@ def fixture_tracer_provider(span_exporter): return provider -@pytest.fixture(scope="function", name="event_logger_provider") -def fixture_event_logger_provider(log_exporter): +@pytest.fixture(scope="function", name="logger_provider") +def fixture_logger_provider(log_exporter): provider = LoggerProvider() provider.add_log_record_processor(SimpleLogRecordProcessor(log_exporter)) - event_logger_provider = EventLoggerProvider(provider) - return event_logger_provider + return provider @pytest.fixture(scope="function", name="meter_provider") @@ -103,7 +101,7 @@ def vcr_config(): @pytest.fixture(scope="function") def instrument_no_content( - tracer_provider, event_logger_provider, meter_provider + tracer_provider, logger_provider, meter_provider ): os.environ.update( {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "False"} @@ -112,7 +110,7 @@ def instrument_no_content( instrumentor = BotocoreInstrumentor() instrumentor.instrument( tracer_provider=tracer_provider, - event_logger_provider=event_logger_provider, + logger_provider=logger_provider, meter_provider=meter_provider, ) @@ -123,7 +121,7 @@ def instrument_no_content( @pytest.fixture(scope="function") def instrument_with_content( - tracer_provider, event_logger_provider, meter_provider + tracer_provider, logger_provider, meter_provider ): os.environ.update( {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "True"} @@ -131,7 +129,7 @@ def instrument_with_content( instrumentor = BotocoreInstrumentor() instrumentor.instrument( tracer_provider=tracer_provider, - event_logger_provider=event_logger_provider, + logger_provider=logger_provider, meter_provider=meter_provider, ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py index 4f7208eabb..7a26d27513 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py @@ -2943,6 +2943,7 @@ def test_invoke_model_with_response_stream_handles_stream_error( logs = log_exporter.get_finished_logs() assert len(logs) == 1 user_content = {"content": [{"text": "Say this is a test"}]} + print(logs[0].log_record) assert_message_in_logs(logs[0], "gen_ai.user.message", user_content, span) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py index de4fc72153..bfdb4f5d9c 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_dynamodb.py @@ -183,7 +183,7 @@ def assert_extension_item_col_metrics(self, operation: str): extension.on_success( span, {"ItemCollectionMetrics": {"ItemCollectionKey": {"id": "1"}}}, - _BotocoreInstrumentorContext(event_logger=mock.Mock()), + _BotocoreInstrumentorContext(logger=mock.Mock()), ) self.assert_item_col_metrics(span) @@ -295,7 +295,7 @@ def test_delete_item_consumed_capacity(self): extension.on_success( span, {"ConsumedCapacity": {"TableName": "table"}}, - _BotocoreInstrumentorContext(event_logger=mock.Mock()), + _BotocoreInstrumentorContext(logger=mock.Mock()), ) self.assert_consumed_capacity(span, "table") From 00086186c72b9be90db585cad432cf0f6c91f701 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Wed, 9 Jul 2025 14:51:56 +0000 Subject: [PATCH 2/8] Use LogRecord in API instead of SDK --- .../instrumentation/botocore/extensions/bedrock_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py index cdda0c0761..3b560e424b 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py @@ -22,7 +22,7 @@ from botocore.eventstream import EventStream, EventStreamError from wrapt import ObjectProxy -from opentelemetry._logs import LogRecord +from opentelemetry.sdk._logs import LogRecord from opentelemetry.instrumentation.botocore.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, ) From 948053d4cfb75cb8de6256727ca5f93c6092c48d Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Wed, 9 Jul 2025 14:52:02 +0000 Subject: [PATCH 3/8] Use LogRecord from APi instead of Events --- .../instrumentation/botocore/extensions/bedrock_utils.py | 8 ++++++-- .../tests/bedrock_utils.py | 6 ++---- .../tests/conftest.py | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py index 3b560e424b..95252693dc 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py @@ -21,8 +21,9 @@ from botocore.eventstream import EventStream, EventStreamError from wrapt import ObjectProxy +from opentelemetry.context import get_current +from opentelemetry._logs import LogRecord -from opentelemetry.sdk._logs import LogRecord from opentelemetry.instrumentation.botocore.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, ) @@ -511,12 +512,14 @@ def message_to_event( event_name="gen_ai.tool.message", attributes=attributes, body=tool_body, + context = get_current(), ) yield LogRecord( event_name=f"gen_ai.{role}.message", attributes=attributes, body=body if body else None, + context = get_current(), ) @@ -620,8 +623,9 @@ def _to_body_dict(self) -> dict[str, Any]: def to_choice_event(self, **event_kwargs) -> LogRecord: attributes = {GEN_AI_SYSTEM: GenAiSystemValues.AWS_BEDROCK.value} return LogRecord( - name="gen_ai.choice", + event_name="gen_ai.choice", attributes=attributes, body=self._to_body_dict(), **event_kwargs, + context = get_current(), ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py index 7f92308456..7b279dffb0 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py @@ -282,7 +282,7 @@ def remove_none_values(body): def assert_log_parent(log, span): if span: - assert log.log_record.trace_id == span.get_span_context().trace_id + assert log.log_record.trace_id == span.get_span_context().trace_id, "{} does not equal {}".format(span.get_span_context().trace_id, log.log_record.trace_id) assert log.log_record.span_id == span.get_span_context().span_id assert ( log.log_record.trace_flags == span.get_span_context().trace_flags @@ -290,9 +290,7 @@ def assert_log_parent(log, span): def assert_message_in_logs(log, event_name, expected_content, parent_span): - assert ( - log.log_record.attributes[EventAttributes.EVENT_NAME] == event_name - ) + assert log.log_record.event_name == event_name, log.log_record.event_name assert ( log.log_record.attributes[GenAIAttributes.GEN_AI_SYSTEM] == GenAIAttributes.GenAiSystemValues.AWS_BEDROCK.value diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py index c71a106a7a..29c06496f3 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py @@ -58,7 +58,6 @@ def fixture_tracer_provider(span_exporter): def fixture_logger_provider(log_exporter): provider = LoggerProvider() provider.add_log_record_processor(SimpleLogRecordProcessor(log_exporter)) - return provider From 47270d15aeadab6ef9cfaab1036b38482e2109dd Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Wed, 9 Jul 2025 18:36:33 +0000 Subject: [PATCH 4/8] Add changelog, fix lint, fix ruff --- CHANGELOG.md | 2 ++ .../opentelemetry/instrumentation/botocore/__init__.py | 8 +++----- .../botocore/extensions/bedrock_utils.py | 10 +++++----- .../tests/bedrock_utils.py | 7 +++---- .../tests/conftest.py | 8 ++------ 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41bfbd0a4f..ea8acd3619 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3567](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3567)) - `opentelemetry-resource-detector-containerid`: make it more quiet on platforms without cgroups ([#3579](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3579)) +- `opentelemetry-instrumentation-botocore`: migrate off the deprecated events API to use the logs API + ([#3624](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3624)) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py index eb047a45c7..b31f531972 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py @@ -199,9 +199,8 @@ def _get_logger(self, extension: _AwsSdkExtension): """This is a multiplexer in order to have a logger per extension""" instrumentation_name = self._get_instrumentation_name(extension) - logger = self._loggers.get(instrumentation_name) - if logger: - return logger + if self._loggers.get(instrumentation_name): + return self._loggers.get(instrumentation_name) schema_version = extension.event_logger_schema_version() self._loggers[instrumentation_name] = get_logger( @@ -287,11 +286,10 @@ def _patched_api_call(self, original_func, instance, args, kwargs): end_span_on_exit = extension.should_end_span_on_exit() tracer = self._get_tracer(extension) - logger = self._get_logger(extension) meter = self._get_meter(extension) metrics = self._get_metrics(extension, meter) instrumentor_ctx = _BotocoreInstrumentorContext( - logger=logger, + logger=self._get_logger(extension), metrics=metrics, ) with tracer.start_as_current_span( diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py index 95252693dc..8abd889fb5 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py @@ -21,9 +21,9 @@ from botocore.eventstream import EventStream, EventStreamError from wrapt import ObjectProxy -from opentelemetry.context import get_current -from opentelemetry._logs import LogRecord +from opentelemetry._logs import LogRecord +from opentelemetry.context import get_current from opentelemetry.instrumentation.botocore.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, ) @@ -512,14 +512,14 @@ def message_to_event( event_name="gen_ai.tool.message", attributes=attributes, body=tool_body, - context = get_current(), + context=get_current(), ) yield LogRecord( event_name=f"gen_ai.{role}.message", attributes=attributes, body=body if body else None, - context = get_current(), + context=get_current(), ) @@ -627,5 +627,5 @@ def to_choice_event(self, **event_kwargs) -> LogRecord: attributes=attributes, body=self._to_body_dict(), **event_kwargs, - context = get_current(), + context=get_current(), ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py index 7b279dffb0..4f46137074 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py @@ -26,9 +26,6 @@ ) from opentelemetry.sdk.metrics._internal.point import ResourceMetrics from opentelemetry.sdk.trace import ReadableSpan -from opentelemetry.semconv._incubating.attributes import ( - event_attributes as EventAttributes, -) from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAIAttributes, ) @@ -282,7 +279,9 @@ def remove_none_values(body): def assert_log_parent(log, span): if span: - assert log.log_record.trace_id == span.get_span_context().trace_id, "{} does not equal {}".format(span.get_span_context().trace_id, log.log_record.trace_id) + assert ( + log.log_record.trace_id == span.get_span_context().trace_id + ), f"{span.get_span_context().trace_id} does not equal {log.log_record.trace_id}" assert log.log_record.span_id == span.get_span_context().span_id assert ( log.log_record.trace_flags == span.get_span_context().trace_flags diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py index 29c06496f3..36ed79baab 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py @@ -99,9 +99,7 @@ def vcr_config(): @pytest.fixture(scope="function") -def instrument_no_content( - tracer_provider, logger_provider, meter_provider -): +def instrument_no_content(tracer_provider, logger_provider, meter_provider): os.environ.update( {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "False"} ) @@ -119,9 +117,7 @@ def instrument_no_content( @pytest.fixture(scope="function") -def instrument_with_content( - tracer_provider, logger_provider, meter_provider -): +def instrument_with_content(tracer_provider, logger_provider, meter_provider): os.environ.update( {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "True"} ) From 1b83355996a498db042789da26d6d585aa821df3 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Wed, 9 Jul 2025 20:41:06 +0000 Subject: [PATCH 5/8] Respond to review comments --- .../src/opentelemetry/instrumentation/botocore/__init__.py | 5 +++-- .../tests/test_botocore_bedrock.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py index b31f531972..bf672f7841 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py @@ -199,8 +199,9 @@ def _get_logger(self, extension: _AwsSdkExtension): """This is a multiplexer in order to have a logger per extension""" instrumentation_name = self._get_instrumentation_name(extension) - if self._loggers.get(instrumentation_name): - return self._loggers.get(instrumentation_name) + instrumentation_logger = self._loggers.get(instrumentation_name) + if instrumentation_logger: + return instrumentation_logger schema_version = extension.event_logger_schema_version() self._loggers[instrumentation_name] = get_logger( diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py index 7a26d27513..4f7208eabb 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py @@ -2943,7 +2943,6 @@ def test_invoke_model_with_response_stream_handles_stream_error( logs = log_exporter.get_finished_logs() assert len(logs) == 1 user_content = {"content": [{"text": "Say this is a test"}]} - print(logs[0].log_record) assert_message_in_logs(logs[0], "gen_ai.user.message", user_content, span) From a3da8b22573f3d2ba8e72f3e320b9b022778df7e Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Fri, 11 Jul 2025 13:57:05 +0000 Subject: [PATCH 6/8] Require v1.35 of otel python --- .../opentelemetry-instrumentation-botocore/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml b/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml index 783686dbe6..a65f060511 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] dependencies = [ - "opentelemetry-api ~= 1.30", + "opentelemetry-api >= 1.35", "opentelemetry-instrumentation == 0.56b0.dev", "opentelemetry-semantic-conventions == 0.56b0.dev", "opentelemetry-propagator-aws-xray ~= 1.0", From a64b30896a778601b429ba416934cfb0db0b7f85 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Fri, 11 Jul 2025 14:14:52 +0000 Subject: [PATCH 7/8] Specify the .0 --- .../opentelemetry-instrumentation-botocore/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml b/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml index a092302f87..26a50634f8 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] dependencies = [ - "opentelemetry-api >= 1.35", + "opentelemetry-api >= 1.35.0", "opentelemetry-instrumentation == 0.57b0.dev", "opentelemetry-semantic-conventions == 0.57b0.dev", "opentelemetry-propagator-aws-xray ~= 1.0", From afb7a7e54d323955c7ed939809069ecde2ae880e Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Fri, 11 Jul 2025 15:13:11 +0000 Subject: [PATCH 8/8] Fix changelog --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2589f82b52..c327cfa3ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +### Fixed +- `opentelemetry-instrumentation-botocore`: migrate off the deprecated events API to use the logs API + ([#3624](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3624)) + ## Version 1.35.0/0.56b0 (2025-07-11) ### Added @@ -30,8 +36,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3567](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3567)) - `opentelemetry-resource-detector-containerid`: make it more quiet on platforms without cgroups ([#3579](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3579)) -- `opentelemetry-instrumentation-botocore`: migrate off the deprecated events API to use the logs API - ([#3624](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3624)) ## Version 1.34.0/0.55b0 (2025-06-04)