Skip to content

Commit 1becaf1

Browse files
Extract arn and account access key
1 parent c6d8143 commit 1becaf1

File tree

3 files changed

+288
-2
lines changed

3 files changed

+288
-2
lines changed

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/_aws_attribute_keys.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
# AWS_#_NAME attributes are not supported in python as they are not part of the Semantic Conventions.
2020
# TODO:Move to Semantic Conventions when these attributes are added.
21+
AWS_AUTH_ACCESS_KEY: str = "aws.auth.account.access_key"
22+
AWS_AUTH_REGION: str = "aws.auth.region"
2123
AWS_SQS_QUEUE_URL: str = "aws.sqs.queue.url"
2224
AWS_SQS_QUEUE_NAME: str = "aws.sqs.queue.name"
25+
AWS_KINESIS_STREAM_ARN: str = "aws.kinesis.stream.arn"
2326
AWS_KINESIS_STREAM_NAME: str = "aws.kinesis.stream.name"
2427
AWS_BEDROCK_DATA_SOURCE_ID: str = "aws.bedrock.data_source.id"
2528
AWS_BEDROCK_KNOWLEDGE_BASE_ID: str = "aws.bedrock.knowledge_base.id"
@@ -33,4 +36,8 @@
3336
AWS_LAMBDA_FUNCTION_NAME: str = "aws.lambda.function.name"
3437
AWS_LAMBDA_RESOURCEMAPPING_ID: str = "aws.lambda.resource_mapping.id"
3538
AWS_LAMBDA_FUNCTION_ARN: str = "aws.lambda.function.arn"
39+
AWS_DYNAMODB_TABLE_ARN: str = "aws.dynamodb.table.arn"
40+
AWS_REMOTE_RESOURCE_ACCESS_KEY: str = "aws.remote.resource.account.access_key"
41+
AWS_REMOTE_RESOURCE_ACCOUNT_ID: str = "aws.remote.resource.account.id"
42+
AWS_REMOTE_RESOURCE_REGION: str = "aws.remote.resource.region"
3643
AWS_SERVICE_TYPE: str = "aws.service.type"

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/patches/_botocore_patches.py

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
# Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License.
44
import importlib
55

6+
from botocore.exceptions import ClientError
7+
68
from amazon.opentelemetry.distro._aws_attribute_keys import (
9+
AWS_AUTH_ACCESS_KEY,
10+
AWS_AUTH_REGION,
11+
AWS_DYNAMODB_TABLE_ARN,
12+
AWS_KINESIS_STREAM_ARN,
713
AWS_KINESIS_STREAM_NAME,
814
AWS_LAMBDA_FUNCTION_ARN,
915
AWS_LAMBDA_FUNCTION_NAME,
@@ -20,7 +26,14 @@
2026
_BedrockAgentRuntimeExtension,
2127
_BedrockExtension,
2228
)
23-
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS
29+
from opentelemetry.instrumentation.botocore import (
30+
BotocoreInstrumentor,
31+
_apply_response_attributes,
32+
_determine_call_context,
33+
_safe_invoke,
34+
)
35+
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS, _find_extension
36+
from opentelemetry.instrumentation.botocore.extensions.dynamodb import _DynamoDbExtension
2437
from opentelemetry.instrumentation.botocore.extensions.lmbd import _LambdaExtension
2538
from opentelemetry.instrumentation.botocore.extensions.sns import _SnsExtension
2639
from opentelemetry.instrumentation.botocore.extensions.sqs import _SqsExtension
@@ -30,6 +43,10 @@
3043
_BotocoreInstrumentorContext,
3144
_BotoResultT,
3245
)
46+
from opentelemetry.instrumentation.utils import (
47+
is_instrumentation_enabled,
48+
suppress_http_instrumentation,
49+
)
3350
from opentelemetry.semconv.trace import SpanAttributes
3451
from opentelemetry.trace.span import Span
3552

@@ -39,6 +56,7 @@ def _apply_botocore_instrumentation_patches() -> None:
3956
4057
Adds patches to provide additional support and Java parity for Kinesis, S3, and SQS.
4158
"""
59+
_apply_botocore_api_call_patch()
4260
_apply_botocore_kinesis_patch()
4361
_apply_botocore_s3_patch()
4462
_apply_botocore_sqs_patch()
@@ -47,6 +65,7 @@ def _apply_botocore_instrumentation_patches() -> None:
4765
_apply_botocore_sns_patch()
4866
_apply_botocore_stepfunctions_patch()
4967
_apply_botocore_lambda_patch()
68+
_apply_botocore_dynamodb_patch()
5069

5170

5271
def _apply_botocore_lambda_patch() -> None:
@@ -208,6 +227,85 @@ def _apply_botocore_bedrock_patch() -> None:
208227
# bedrock-runtime is handled by upstream
209228

210229

230+
def _apply_botocore_dynamodb_patch() -> None:
231+
"""Botocore instrumentation patch for DynamoDB
232+
233+
This patch adds an extension to the upstream's list of known extensions for DynamoDB.
234+
Extensions allow for custom logic for adding service-specific information to
235+
spans, such as attributes. Specifically, we are adding logic to add the
236+
`aws.table.arn` attribute, to be used to generate RemoteTarget and achieve
237+
parity with the Java instrumentation.
238+
"""
239+
old_on_success = _DynamoDbExtension.on_success
240+
241+
def patch_on_success(self, span: Span, result: _BotoResultT):
242+
old_on_success(self, span, result)
243+
table = result.get("Table", {})
244+
table_arn = table.get("TableArn")
245+
if table_arn:
246+
span.set_attribute(AWS_DYNAMODB_TABLE_ARN, table_arn)
247+
248+
_DynamoDbExtension.on_success = patch_on_success
249+
250+
251+
def _apply_botocore_api_call_patch() -> None:
252+
def patched_api_call(self, original_func, instance, args, kwargs):
253+
if not is_instrumentation_enabled():
254+
return original_func(*args, **kwargs)
255+
256+
call_context = _determine_call_context(instance, args)
257+
if call_context is None:
258+
return original_func(*args, **kwargs)
259+
260+
extension = _find_extension(call_context)
261+
if not extension.should_trace_service_call():
262+
return original_func(*args, **kwargs)
263+
264+
attributes = {
265+
SpanAttributes.RPC_SYSTEM: "aws-api",
266+
SpanAttributes.RPC_SERVICE: call_context.service_id,
267+
SpanAttributes.RPC_METHOD: call_context.operation,
268+
"aws.region": call_context.region,
269+
AWS_AUTH_REGION: call_context.region,
270+
}
271+
credentials = instance._get_credentials()
272+
273+
if credentials is not None:
274+
access_key = credentials.access_key
275+
if access_key is not None:
276+
attributes[AWS_AUTH_ACCESS_KEY] = access_key
277+
278+
_safe_invoke(extension.extract_attributes, attributes)
279+
280+
with self._tracer.start_as_current_span(
281+
call_context.span_name,
282+
kind=call_context.span_kind,
283+
attributes=attributes,
284+
) as span:
285+
_safe_invoke(extension.before_service_call, span)
286+
self._call_request_hook(span, call_context)
287+
288+
try:
289+
with suppress_http_instrumentation():
290+
result = None
291+
try:
292+
result = original_func(*args, **kwargs)
293+
except ClientError as error:
294+
result = getattr(error, "response", None)
295+
_apply_response_attributes(span, result)
296+
_safe_invoke(extension.on_error, span, error)
297+
raise
298+
_apply_response_attributes(span, result)
299+
_safe_invoke(extension.on_success, span, result)
300+
finally:
301+
_safe_invoke(extension.after_service_call)
302+
self._call_response_hook(span, call_context, result)
303+
304+
return result
305+
306+
BotocoreInstrumentor._patched_api_call = patched_api_call
307+
308+
211309
# The OpenTelemetry Authors code
212310
def _lazy_load(module, cls):
213311
"""Clone of upstream opentelemetry.instrumentation.botocore.extensions.lazy_load
@@ -265,3 +363,6 @@ def extract_attributes(self, attributes: _AttributeMapT):
265363
stream_name = self._call_context.params.get("StreamName")
266364
if stream_name:
267365
attributes[AWS_KINESIS_STREAM_NAME] = stream_name
366+
stream_arn = self._call_context.params.get("StreamARN")
367+
if stream_arn:
368+
attributes[AWS_KINESIS_STREAM_ARN] = stream_arn

0 commit comments

Comments
 (0)