Skip to content

Commit b55bead

Browse files
committed
resolve conflict
1 parent 9801444 commit b55bead

File tree

4 files changed

+138
-2
lines changed

4 files changed

+138
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
1212
## Unreleased
1313

14+
### Fixed
15+
1416
### Added
15-
- `opentelemetry-instrumentation-dbapi`: Add support for `commenter_options` in `trace_integration` function to control SQLCommenter behavior
16-
([#3743](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3743))
17+
- `opentelemetry-instrumentation`: botocore: Add support for AWS Secrets Manager semantic convention attribute
18+
([#3765](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3765))
1719

1820
## Version 1.37.0/0.58b0 (2025-09-11)
1921

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"),
Lines changed: 45 additions & 0 deletions
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 is extracted if a secret ARN, the function extracts the attribute
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: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
15+
import botocore.session
16+
from moto import mock_aws
17+
18+
from opentelemetry.instrumentation.botocore import BotocoreInstrumentor
19+
from opentelemetry.semconv._incubating.attributes.aws_attributes import (
20+
AWS_SECRETSMANAGER_SECRET_ARN,
21+
)
22+
from opentelemetry.test.test_base import TestBase
23+
24+
25+
class TestSecretsManagerExtension(TestBase):
26+
def setUp(self):
27+
super().setUp()
28+
BotocoreInstrumentor().instrument()
29+
session = botocore.session.get_session()
30+
session.set_credentials(
31+
access_key="access-key", secret_key="secret-key"
32+
)
33+
self.region = "us-west-2"
34+
self.client = session.create_client(
35+
"secretsmanager", region_name=self.region
36+
)
37+
38+
def tearDown(self):
39+
super().tearDown()
40+
BotocoreInstrumentor().uninstrument()
41+
42+
def create_secret_and_get_arn(self, name: str = "test-secret") -> str:
43+
"""
44+
Create a secret in mocked Secrets Manager and return its ARN.
45+
"""
46+
# Clear spans before creating secret for helper method
47+
self.memory_exporter.clear()
48+
response = self.client.create_secret(
49+
Name=name, SecretString="test-secret-value"
50+
)
51+
return response["ARN"]
52+
53+
@mock_aws
54+
def test_tag_resource_with_arn(self):
55+
secret_arn = self.create_secret_and_get_arn()
56+
57+
self.client.tag_resource(
58+
SecretId=secret_arn, Tags=[{"Key": "Environment", "Value": "Test"}]
59+
)
60+
61+
spans = self.memory_exporter.get_finished_spans()
62+
assert spans
63+
self.assertEqual(len(spans), 2)
64+
span = spans[1] # tag_resource span
65+
self.assertEqual(
66+
span.attributes[AWS_SECRETSMANAGER_SECRET_ARN],
67+
secret_arn,
68+
)
69+
70+
@mock_aws
71+
def test_create_secret(self):
72+
secret_name = "test-secret"
73+
response = self.client.create_secret(
74+
Name=secret_name, SecretString="test-secret-value"
75+
)
76+
secret_arn = response["ARN"]
77+
78+
spans = self.memory_exporter.get_finished_spans()
79+
assert spans
80+
self.assertEqual(len(spans), 1)
81+
span = spans[0] # create_secret span
82+
# Should capture ARN from response
83+
self.assertEqual(
84+
span.attributes[AWS_SECRETSMANAGER_SECRET_ARN],
85+
secret_arn,
86+
)

0 commit comments

Comments
 (0)