diff --git a/CHANGELOG.md b/CHANGELOG.md index 6101817317..1a307e90da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366)) - `opentelemetry-instrumentation`: add support for `OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH` to inform opentelemetry-instrument about gevent monkeypatching ([#3699](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3699)) +- `opentelemetry-instrumentation-botocore`: Add support for SNS semantic convention attribute aws.sns.topic.arn + ([#3734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3734)) ## Version 1.36.0/0.57b0 (2025-07-29) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py index 3ff42a3fed..fdc0227286 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py @@ -24,6 +24,10 @@ _AwsSdkCallContext, _AwsSdkExtension, _BotocoreInstrumentorContext, + _BotoResultT, +) +from opentelemetry.semconv._incubating.attributes.aws_attributes import ( + AWS_SNS_TOPIC_ARN, ) from opentelemetry.semconv.trace import ( MessagingDestinationKindValues, @@ -161,6 +165,9 @@ def __init__(self, call_context: _AwsSdkCallContext): def extract_attributes(self, attributes: _AttributeMapT): attributes[SpanAttributes.MESSAGING_SYSTEM] = "aws.sns" + topic_arn = self._call_context.params.get("TopicArn") + if topic_arn: + attributes[AWS_SNS_TOPIC_ARN] = topic_arn if self._op: self._op.extract_attributes(self._call_context, attributes) @@ -170,3 +177,16 @@ def before_service_call( ): if self._op: self._op.before_service_call(self._call_context, span) + + def on_success( + self, + span: Span, + result: _BotoResultT, + instrumentor_context: _BotocoreInstrumentorContext, + ): + if not span.is_recording(): + return + + topic_arn = result.get("TopicArn") + if topic_arn: + span.set_attribute(AWS_SNS_TOPIC_ARN, topic_arn) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_sns.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_sns.py index 38bdfc28a3..4cf504bfce 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_sns.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_sns.py @@ -21,6 +21,9 @@ from moto import mock_aws from opentelemetry.instrumentation.botocore import BotocoreInstrumentor +from opentelemetry.semconv._incubating.attributes.aws_attributes import ( + AWS_SNS_TOPIC_ARN, +) from opentelemetry.semconv.trace import ( MessagingDestinationKindValues, SpanAttributes, @@ -151,6 +154,10 @@ def test_publish_injects_span(self): ) span = self.assert_span(f"{self.topic_name} send") + self.assertEqual( + topic_arn, + span.attributes[AWS_SNS_TOPIC_ARN], + ) self.assert_injected_span(message_attrs, span) def test_publish_batch_to_topic(self): @@ -188,6 +195,10 @@ def test_publish_batch_to_topic(self): MessagingDestinationKindValues.TOPIC.value, span.attributes[SpanAttributes.MESSAGING_DESTINATION_KIND], ) + self.assertEqual( + topic_arn, + span.attributes[AWS_SNS_TOPIC_ARN], + ) self.assertEqual( topic_arn, span.attributes[SpanAttributes.MESSAGING_DESTINATION], @@ -199,3 +210,16 @@ def test_publish_batch_to_topic(self): self.assert_injected_span(message1_attrs, span) self.assert_injected_span(message2_attrs, span) + + @mock_aws + def test_create_topic_span(self): + _ = self.client.create_topic(Name=self.topic_name) + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(1, len(spans)) + span = spans[0] + self.assertEqual(SpanKind.CLIENT, span.kind) + self.assertEqual("SNS.CreateTopic", span.name) + self.assertEqual( + self.topic_arn, + span.attributes[AWS_SNS_TOPIC_ARN], + )