From 08b3c555bf7f8940f416559727287c6e828bf8e4 Mon Sep 17 00:00:00 2001 From: Luke Zhang Date: Wed, 17 Sep 2025 13:27:40 -0700 Subject: [PATCH 1/4] botocore: Add support for AWS Secrets Manager semantic convention attribute AWS Secrets Manager defines semantic convention attribute: AWS_SECRETSMANAGER_SECRET_ARN: Final = "aws.secretsmanager.secret.arn" https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/aws.md#amazon-secrets-manager-attributes Currently, this attribute is not set in the botocore instrumentation library. This PR adds support for them by extracting values from both Request and Response objects. Tests Added new unit tests (passing). Verified with: tox -e py312-test-instrumentation-botocore tox -e spellcheck tox -e lint-instrumentation-botocore tox -e ruff Backward Compatibility This change is backward compatible. It only adds instrumentation for additional AWS resources and does not modify existing behavior in the auto-instrumentation library. --- .../botocore/extensions/__init__.py | 3 + .../botocore/extensions/secretsmanager.py | 45 ++++++++++++ .../tests/test_botocore_secretsmanager.py | 72 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py create mode 100644 instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py index 599be4236c..dd8ba24e9f 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py @@ -35,6 +35,9 @@ def loader(): "bedrock-runtime": _lazy_load(".bedrock", "_BedrockRuntimeExtension"), "dynamodb": _lazy_load(".dynamodb", "_DynamoDbExtension"), "lambda": _lazy_load(".lmbd", "_LambdaExtension"), + "secretsmanager": _lazy_load( + ".secretsmanager", "_SecretsManagerExtension" + ), "stepfunctions": _lazy_load(".sfns", "_StepFunctionsExtension"), "sns": _lazy_load(".sns", "_SnsExtension"), "sqs": _lazy_load(".sqs", "_SqsExtension"), diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py new file mode 100644 index 0000000000..112908cd4b --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py @@ -0,0 +1,45 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from opentelemetry.instrumentation.botocore.extensions.types import ( + _AttributeMapT, + _AwsSdkExtension, + _BotocoreInstrumentorContext, + _BotoResultT, +) +from opentelemetry.semconv._incubating.attributes.aws_attributes import ( + AWS_SECRETSMANAGER_SECRET_ARN, +) +from opentelemetry.trace.span import Span + + +class _SecretsManagerExtension(_AwsSdkExtension): + def extract_attributes(self, attributes: _AttributeMapT): + """ + SecretId can be secret name or secret arn, the function extracts attributes + only if the SecretId parameter is provided as an arn which starts with + `arn:aws:secretsmanager:` + """ + secret_id = self._call_context.params.get("SecretId") + if secret_id and secret_id.startswith("arn:aws:secretsmanager:"): + attributes[AWS_SECRETSMANAGER_SECRET_ARN] = secret_id + + def on_success( + self, + span: Span, + result: _BotoResultT, + instrumentor_context: _BotocoreInstrumentorContext, + ): + secret_arn = result.get("ARN") + if secret_arn: + span.set_attribute(AWS_SECRETSMANAGER_SECRET_ARN, secret_arn) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py new file mode 100644 index 0000000000..1ea7f27d21 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py @@ -0,0 +1,72 @@ +import botocore.session +from moto import mock_aws + +from opentelemetry.instrumentation.botocore import BotocoreInstrumentor +from opentelemetry.semconv._incubating.attributes.aws_attributes import ( + AWS_SECRETSMANAGER_SECRET_ARN, +) +from opentelemetry.test.test_base import TestBase + + +class TestSecretsManagerExtension(TestBase): + def setUp(self): + super().setUp() + BotocoreInstrumentor().instrument() + session = botocore.session.get_session() + session.set_credentials( + access_key="access-key", secret_key="secret-key" + ) + self.region = "us-west-2" + self.client = session.create_client( + "secretsmanager", region_name=self.region + ) + + def tearDown(self): + super().tearDown() + BotocoreInstrumentor().uninstrument() + + def create_secret_and_get_arn(self, name: str = "test-secret") -> str: + """ + Create a secret in mocked Secrets Manager and return its ARN. + """ + # Clear spans before creating secret for helper method + self.memory_exporter.clear() + response = self.client.create_secret( + Name=name, SecretString="test-secret-value" + ) + return response["ARN"] + + @mock_aws + def test_tag_resource_with_arn(self): + secret_arn = self.create_secret_and_get_arn() + + self.client.tag_resource( + SecretId=secret_arn, Tags=[{"Key": "Environment", "Value": "Test"}] + ) + + spans = self.memory_exporter.get_finished_spans() + assert spans + self.assertEqual(len(spans), 2) + span = spans[1] # tag_resource span + self.assertEqual( + span.attributes[AWS_SECRETSMANAGER_SECRET_ARN], + secret_arn, + ) + + @mock_aws + def test_create_secret(self): + secret_name = "test-secret" + response = self.client.create_secret( + Name=secret_name, SecretString="test-secret-value" + ) + secret_arn = response["ARN"] + + spans = self.memory_exporter.get_finished_spans() + assert spans + self.assertEqual(len(spans), 1) + span = spans[0] # create_secret span + # Should capture ARN from response + self.assertEqual( + span.attributes[AWS_SECRETSMANAGER_SECRET_ARN], + secret_arn, + ) From eb87ba8d7d402d03d4ed8ab7133eb9e327101e40 Mon Sep 17 00:00:00 2001 From: Luke Zhang Date: Wed, 17 Sep 2025 13:38:10 -0700 Subject: [PATCH 2/4] add ChangeLog. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbc8a5ec89..c8eeac2b13 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 +### Fixed + +### Added +- `opentelemetry-instrumentation`: botocore: Add support for AWS Secrets Manager semantic convention attribute + ([#3765](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3765)) + ## Version 1.37.0/0.58b0 (2025-09-11) ### Fixed From 20b7a8604d1dd329cb2c3db6b67720beedbefd29 Mon Sep 17 00:00:00 2001 From: "Luke (GuangHui) Zhang" Date: Wed, 17 Sep 2025 15:11:26 -0700 Subject: [PATCH 3/4] Update instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py Co-authored-by: Tammy Baylis <96076570+tammy-baylis-swi@users.noreply.github.com> --- .../instrumentation/botocore/extensions/secretsmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py index 112908cd4b..f1b1d8ba21 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/secretsmanager.py @@ -26,7 +26,7 @@ class _SecretsManagerExtension(_AwsSdkExtension): def extract_attributes(self, attributes: _AttributeMapT): """ - SecretId can be secret name or secret arn, the function extracts attributes + SecretId is extracted if a secret ARN, the function extracts the attribute only if the SecretId parameter is provided as an arn which starts with `arn:aws:secretsmanager:` """ From aa7db409029a2df06f75d61346c9bb8d0f05c7d0 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 23 Sep 2025 15:38:37 +0200 Subject: [PATCH 4/4] Update instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py --- .../tests/test_botocore_secretsmanager.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py index 1ea7f27d21..d2fe8deb91 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_secretsmanager.py @@ -1,3 +1,17 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import botocore.session from moto import mock_aws