Skip to content

Commit 08b3c55

Browse files
committed
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.
1 parent 7819be1 commit 08b3c55

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def loader():
3535
"bedrock-runtime": _lazy_load(".bedrock", "_BedrockRuntimeExtension"),
3636
"dynamodb": _lazy_load(".dynamodb", "_DynamoDbExtension"),
3737
"lambda": _lazy_load(".lmbd", "_LambdaExtension"),
38+
"secretsmanager": _lazy_load(
39+
".secretsmanager", "_SecretsManagerExtension"
40+
),
3841
"stepfunctions": _lazy_load(".sfns", "_StepFunctionsExtension"),
3942
"sns": _lazy_load(".sns", "_SnsExtension"),
4043
"sqs": _lazy_load(".sqs", "_SqsExtension"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from opentelemetry.instrumentation.botocore.extensions.types import (
15+
_AttributeMapT,
16+
_AwsSdkExtension,
17+
_BotocoreInstrumentorContext,
18+
_BotoResultT,
19+
)
20+
from opentelemetry.semconv._incubating.attributes.aws_attributes import (
21+
AWS_SECRETSMANAGER_SECRET_ARN,
22+
)
23+
from opentelemetry.trace.span import Span
24+
25+
26+
class _SecretsManagerExtension(_AwsSdkExtension):
27+
def extract_attributes(self, attributes: _AttributeMapT):
28+
"""
29+
SecretId can be secret name or secret arn, the function extracts attributes
30+
only if the SecretId parameter is provided as an arn which starts with
31+
`arn:aws:secretsmanager:`
32+
"""
33+
secret_id = self._call_context.params.get("SecretId")
34+
if secret_id and secret_id.startswith("arn:aws:secretsmanager:"):
35+
attributes[AWS_SECRETSMANAGER_SECRET_ARN] = secret_id
36+
37+
def on_success(
38+
self,
39+
span: Span,
40+
result: _BotoResultT,
41+
instrumentor_context: _BotocoreInstrumentorContext,
42+
):
43+
secret_arn = result.get("ARN")
44+
if secret_arn:
45+
span.set_attribute(AWS_SECRETSMANAGER_SECRET_ARN, secret_arn)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import botocore.session
2+
from moto import mock_aws
3+
4+
from opentelemetry.instrumentation.botocore import BotocoreInstrumentor
5+
from opentelemetry.semconv._incubating.attributes.aws_attributes import (
6+
AWS_SECRETSMANAGER_SECRET_ARN,
7+
)
8+
from opentelemetry.test.test_base import TestBase
9+
10+
11+
class TestSecretsManagerExtension(TestBase):
12+
def setUp(self):
13+
super().setUp()
14+
BotocoreInstrumentor().instrument()
15+
session = botocore.session.get_session()
16+
session.set_credentials(
17+
access_key="access-key", secret_key="secret-key"
18+
)
19+
self.region = "us-west-2"
20+
self.client = session.create_client(
21+
"secretsmanager", region_name=self.region
22+
)
23+
24+
def tearDown(self):
25+
super().tearDown()
26+
BotocoreInstrumentor().uninstrument()
27+
28+
def create_secret_and_get_arn(self, name: str = "test-secret") -> str:
29+
"""
30+
Create a secret in mocked Secrets Manager and return its ARN.
31+
"""
32+
# Clear spans before creating secret for helper method
33+
self.memory_exporter.clear()
34+
response = self.client.create_secret(
35+
Name=name, SecretString="test-secret-value"
36+
)
37+
return response["ARN"]
38+
39+
@mock_aws
40+
def test_tag_resource_with_arn(self):
41+
secret_arn = self.create_secret_and_get_arn()
42+
43+
self.client.tag_resource(
44+
SecretId=secret_arn, Tags=[{"Key": "Environment", "Value": "Test"}]
45+
)
46+
47+
spans = self.memory_exporter.get_finished_spans()
48+
assert spans
49+
self.assertEqual(len(spans), 2)
50+
span = spans[1] # tag_resource span
51+
self.assertEqual(
52+
span.attributes[AWS_SECRETSMANAGER_SECRET_ARN],
53+
secret_arn,
54+
)
55+
56+
@mock_aws
57+
def test_create_secret(self):
58+
secret_name = "test-secret"
59+
response = self.client.create_secret(
60+
Name=secret_name, SecretString="test-secret-value"
61+
)
62+
secret_arn = response["ARN"]
63+
64+
spans = self.memory_exporter.get_finished_spans()
65+
assert spans
66+
self.assertEqual(len(spans), 1)
67+
span = spans[0] # create_secret span
68+
# Should capture ARN from response
69+
self.assertEqual(
70+
span.attributes[AWS_SECRETSMANAGER_SECRET_ARN],
71+
secret_arn,
72+
)

0 commit comments

Comments
 (0)