Skip to content

Commit a496acd

Browse files
Extract arn and account access key
1 parent abe6c6b commit a496acd

File tree

3 files changed

+286
-2
lines changed

3 files changed

+286
-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,3 +36,7 @@
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"

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,
@@ -21,11 +27,22 @@
2127
_BedrockExtension,
2228
_BedrockRuntimeExtension,
2329
)
24-
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS
30+
from opentelemetry.instrumentation.botocore import (
31+
BotocoreInstrumentor,
32+
_apply_response_attributes,
33+
_determine_call_context,
34+
_safe_invoke,
35+
)
36+
from opentelemetry.instrumentation.botocore.extensions import _KNOWN_EXTENSIONS, _find_extension
37+
from opentelemetry.instrumentation.botocore.extensions.dynamodb import _DynamoDbExtension
2538
from opentelemetry.instrumentation.botocore.extensions.lmbd import _LambdaExtension
2639
from opentelemetry.instrumentation.botocore.extensions.sns import _SnsExtension
2740
from opentelemetry.instrumentation.botocore.extensions.sqs import _SqsExtension
2841
from opentelemetry.instrumentation.botocore.extensions.types import _AttributeMapT, _AwsSdkExtension, _BotoResultT
42+
from opentelemetry.instrumentation.utils import (
43+
is_instrumentation_enabled,
44+
suppress_http_instrumentation,
45+
)
2946
from opentelemetry.semconv.trace import SpanAttributes
3047
from opentelemetry.trace.span import Span
3148

@@ -35,6 +52,7 @@ def _apply_botocore_instrumentation_patches() -> None:
3552
3653
Adds patches to provide additional support and Java parity for Kinesis, S3, and SQS.
3754
"""
55+
_apply_botocore_api_call_patch()
3856
_apply_botocore_kinesis_patch()
3957
_apply_botocore_s3_patch()
4058
_apply_botocore_sqs_patch()
@@ -43,6 +61,7 @@ def _apply_botocore_instrumentation_patches() -> None:
4361
_apply_botocore_sns_patch()
4462
_apply_botocore_stepfunctions_patch()
4563
_apply_botocore_lambda_patch()
64+
_apply_botocore_dynamodb_patch()
4665

4766

4867
def _apply_botocore_lambda_patch() -> None:
@@ -204,6 +223,85 @@ def _apply_botocore_bedrock_patch() -> None:
204223
_KNOWN_EXTENSIONS["bedrock-runtime"] = _lazy_load(".", "_BedrockRuntimeExtension")
205224

206225

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

0 commit comments

Comments
 (0)