Skip to content

Commit afa21b2

Browse files
Extract arn and account access key
1 parent e997cf8 commit afa21b2

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,7 +27,14 @@
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
@@ -31,6 +44,10 @@
3144
_BotocoreInstrumentorContext,
3245
_BotoResultT,
3346
)
47+
from opentelemetry.instrumentation.utils import (
48+
is_instrumentation_enabled,
49+
suppress_http_instrumentation,
50+
)
3451
from opentelemetry.semconv.trace import SpanAttributes
3552
from opentelemetry.trace.span import Span
3653

@@ -40,6 +57,7 @@ def _apply_botocore_instrumentation_patches() -> None:
4057
4158
Adds patches to provide additional support and Java parity for Kinesis, S3, and SQS.
4259
"""
60+
_apply_botocore_api_call_patch()
4361
_apply_botocore_kinesis_patch()
4462
_apply_botocore_s3_patch()
4563
_apply_botocore_sqs_patch()
@@ -48,6 +66,7 @@ def _apply_botocore_instrumentation_patches() -> None:
4866
_apply_botocore_sns_patch()
4967
_apply_botocore_stepfunctions_patch()
5068
_apply_botocore_lambda_patch()
69+
_apply_botocore_dynamodb_patch()
5170

5271

5372
def _apply_botocore_lambda_patch() -> None:
@@ -209,6 +228,85 @@ def _apply_botocore_bedrock_patch() -> None:
209228
_KNOWN_EXTENSIONS["bedrock-runtime"] = _lazy_load(".", "_BedrockRuntimeExtension")
210229

211230

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

0 commit comments

Comments
 (0)