Skip to content

Commit a8bd238

Browse files
committed
feat: Add auto-instrumentation support for StepFunctions
1 parent 6fcf4cc commit a8bd238

File tree

5 files changed

+85
-0
lines changed

5 files changed

+85
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@
2525
AWS_BEDROCK_GUARDRAIL_ID: str = "aws.bedrock.guardrail.id"
2626
AWS_SECRETSMANAGER_SECRET_ARN: str = "aws.secretsmanager.secret.arn"
2727
AWS_SNS_TOPIC_ARN: str = "aws.sns.topic.arn"
28+
AWS_STEPFUNCTIONS_STATEMACHINE_ARN: str = "aws.stepfunctions.state_machine.arn"
29+
AWS_STEPFUNCTIONS_ACTIVITY_ARN: str = "aws.stepfunctions.activity.arn"

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
AWS_SPAN_KIND,
2525
AWS_SQS_QUEUE_NAME,
2626
AWS_SQS_QUEUE_URL,
27+
AWS_STEPFUNCTIONS_ACTIVITY_ARN,
28+
AWS_STEPFUNCTIONS_STATEMACHINE_ARN,
2729
)
2830
from amazon.opentelemetry.distro._aws_span_processing_util import (
2931
GEN_AI_REQUEST_MODEL,
@@ -92,6 +94,7 @@
9294
_NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME: str = "AWS::BedrockRuntime"
9395
_NORMALIZED_SECRETSMANAGER_SERVICE_NAME: str = "AWS::SecretsManager"
9496
_NORMALIZED_SNS_SERVICE_NAME: str = "AWS::SNS"
97+
_NORMALIZED_STEPFUNCTIONS_SERVICE_NAME: str = "AWS::StepFunctions"
9598
_DB_CONNECTION_STRING_TYPE: str = "DB::Connection"
9699

97100
# Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present.
@@ -315,6 +318,7 @@ def _normalize_remote_service_name(span: ReadableSpan, service_name: str) -> str
315318
"Bedrock Runtime": _NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME,
316319
"Secrets Manager": _NORMALIZED_SECRETSMANAGER_SERVICE_NAME,
317320
"SNS": _NORMALIZED_SNS_SERVICE_NAME,
321+
"SFN": _NORMALIZED_STEPFUNCTIONS_SERVICE_NAME,
318322
}
319323
return aws_sdk_service_mapping.get(service_name, "AWS::" + service_name)
320324
return service_name
@@ -428,6 +432,12 @@ def _set_remote_type_and_identifier(span: ReadableSpan, attributes: BoundedAttri
428432
elif is_key_present(span, AWS_SNS_TOPIC_ARN):
429433
remote_resource_type = _NORMALIZED_SNS_SERVICE_NAME + "::Topic"
430434
remote_resource_identifier = _escape_delimiters(span.attributes.get(AWS_SNS_TOPIC_ARN))
435+
elif is_key_present(span, AWS_STEPFUNCTIONS_STATEMACHINE_ARN):
436+
remote_resource_type = _NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + "::StateMachine"
437+
remote_resource_identifier = _escape_delimiters(span.attributes.get(AWS_STEPFUNCTIONS_STATEMACHINE_ARN))
438+
elif is_key_present(span, AWS_STEPFUNCTIONS_ACTIVITY_ARN):
439+
remote_resource_type = _NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + "::Activity"
440+
remote_resource_identifier = _escape_delimiters(span.attributes.get(AWS_STEPFUNCTIONS_ACTIVITY_ARN))
431441
elif is_db_span(span):
432442
remote_resource_type = _DB_CONNECTION_STRING_TYPE
433443
remote_resource_identifier = _get_db_connection(span)

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
AWS_SNS_TOPIC_ARN,
1010
AWS_SQS_QUEUE_NAME,
1111
AWS_SQS_QUEUE_URL,
12+
AWS_STEPFUNCTIONS_ACTIVITY_ARN,
13+
AWS_STEPFUNCTIONS_STATEMACHINE_ARN,
1214
)
1315
from amazon.opentelemetry.distro.patches._bedrock_patches import ( # noqa # pylint: disable=unused-import
1416
_BedrockAgentExtension,
@@ -35,6 +37,20 @@ def _apply_botocore_instrumentation_patches() -> None:
3537
_apply_botocore_bedrock_patch()
3638
_apply_botocore_secretsmanager_patch()
3739
_apply_botocore_sns_patch()
40+
_apply_botocore_stepfunctions_patch()
41+
42+
43+
def _apply_botocore_stepfunctions_patch() -> None:
44+
"""Botocore instrumentation patch for StepFunctions
45+
46+
This patch adds an extension to the upstream's list of known extensions for
47+
StepFunctions. Extensions allow for custom logic for adding service-specific
48+
information to spans, such as attributes. Specifically, we are adding logic
49+
to add the `aws.stepfunctions.state_machine.arn` and `aws.stepfunctions.activity.arn`
50+
attributes, to be used to generate RemoteTarget and achieve partity with the
51+
Java instrumentation.
52+
"""
53+
_KNOWN_EXTENSIONS["stepfunctions"] = _lazy_load(".", "_StepFunctionsExtension")
3854

3955

4056
def _apply_botocore_sns_patch() -> None:
@@ -149,6 +165,16 @@ def loader():
149165
# END The OpenTelemetry Authors code
150166

151167

168+
class _StepFunctionsExtension(_AwsSdkExtension):
169+
def extract_attributes(self, attributes: _AttributeMapT):
170+
state_machine_arn = self._call_context.params.get("stateMachineArn")
171+
if state_machine_arn:
172+
attributes[AWS_STEPFUNCTIONS_STATEMACHINE_ARN] = state_machine_arn
173+
activity_arn = self._call_context.params.get("activityArn")
174+
if activity_arn:
175+
attributes[AWS_STEPFUNCTIONS_ACTIVITY_ARN] = activity_arn
176+
177+
152178
class _SecretsManagerExtension(_AwsSdkExtension):
153179
def extract_attributes(self, attributes: _AttributeMapT):
154180
"""

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_metric_attribute_generator.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
AWS_SPAN_KIND,
2727
AWS_SQS_QUEUE_NAME,
2828
AWS_SQS_QUEUE_URL,
29+
AWS_STEPFUNCTIONS_ACTIVITY_ARN,
30+
AWS_STEPFUNCTIONS_STATEMACHINE_ARN,
2931
)
3032
from amazon.opentelemetry.distro._aws_metric_attribute_generator import _AwsMetricAttributeGenerator
3133
from amazon.opentelemetry.distro._aws_span_processing_util import GEN_AI_REQUEST_MODEL
@@ -881,6 +883,7 @@ def test_normalize_remote_service_name_aws_sdk(self):
881883
self.validate_aws_sdk_service_normalization("Bedrock Runtime", "AWS::BedrockRuntime")
882884
self.validate_aws_sdk_service_normalization("Secrets Manager", "AWS::SecretsManager")
883885
self.validate_aws_sdk_service_normalization("SNS", "AWS::SNS")
886+
self.validate_aws_sdk_service_normalization("SFN", "AWS::StepFunctions")
884887

885888
def validate_aws_sdk_service_normalization(self, service_name: str, expected_remote_service: str):
886889
self._mock_attribute([SpanAttributes.RPC_SYSTEM, SpanAttributes.RPC_SERVICE], ["aws-api", service_name])
@@ -1114,6 +1117,31 @@ def test_sdk_client_span_with_remote_resource_attributes(self):
11141117
self._validate_remote_resource_attributes("AWS::SNS::Topic", "arn:aws:sns:us-west-2:012345678901:test_topic")
11151118
self._mock_attribute([AWS_SNS_TOPIC_ARN], [None])
11161119

1120+
# Validate behaviour of AWS_STEPFUNCTIONS_STATEMACHINE_ARN attribute, then remove it.
1121+
self._mock_attribute(
1122+
[AWS_STEPFUNCTIONS_STATEMACHINE_ARN],
1123+
["arn:aws:states:us-east-1:123456789012:stateMachine:test_state_machine"],
1124+
keys,
1125+
values
1126+
)
1127+
self._validate_remote_resource_attributes(
1128+
"AWS::StepFunctions::StateMachine",
1129+
"arn:aws:states:us-east-1:123456789012:stateMachine:test_state_machine"
1130+
)
1131+
self._mock_attribute([AWS_STEPFUNCTIONS_STATEMACHINE_ARN], [None])
1132+
1133+
# Validate behaviour of AWS_STEPFUNCTIONS_ACTIVITY_ARN attribute, then remove it.
1134+
self._mock_attribute(
1135+
[AWS_STEPFUNCTIONS_ACTIVITY_ARN],
1136+
["arn:aws:states:us-east-1:007003123456789012:activity:testActivity"],
1137+
keys,
1138+
values,
1139+
)
1140+
self._validate_remote_resource_attributes(
1141+
"AWS::StepFunctions::Activity", "arn:aws:states:us-east-1:007003123456789012:activity:testActivity"
1142+
)
1143+
self._mock_attribute([AWS_STEPFUNCTIONS_ACTIVITY_ARN], [None])
1144+
11171145
self._mock_attribute([SpanAttributes.RPC_SYSTEM], [None])
11181146

11191147
def test_client_db_span_with_remote_resource_attributes(self):

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_instrumentation_patch.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
_GEN_AI_REQUEST_MODEL: str = "genAiReuqestModelId"
2929
_SECRET_ARN: str = "arn:aws:secretsmanager:us-west-2:000000000000:secret:testSecret-ABCDEF"
3030
_TOPIC_ARN: str = "topicArn"
31+
_STATE_MACHINE_ARN: str = "arn:aws:states:us-west-2:000000000000:stateMachine:testStateMachine"
32+
_ACTIVITY_ARN: str = "arn:aws:states:us-east-1:007003123456789012:activity:testActivity"
3133

3234
# Patch names
3335
GET_DISTRIBUTION_PATCH: str = (
@@ -149,6 +151,9 @@ def _test_unpatched_botocore_instrumentation(self):
149151
# SNS
150152
self.assertTrue("sns" in _KNOWN_EXTENSIONS, "Upstream has removed the SNS extension")
151153

154+
# StepFunctions
155+
self.assertFalse("stepfunctions" in _KNOWN_EXTENSIONS, "Upstream has added a StepFunctions extension")
156+
152157
def _test_unpatched_gevent_instrumentation(self):
153158
self.assertFalse(gevent.monkey.is_module_patched("os"), "gevent os module has been patched")
154159
self.assertFalse(gevent.monkey.is_module_patched("thread"), "gevent thread module has been patched")
@@ -223,6 +228,14 @@ def _test_patched_botocore_instrumentation(self):
223228
self.assertTrue("aws.sns.topic.arn" in sns_attributes)
224229
self.assertEqual(sns_attributes["aws.sns.topic.arn"], _TOPIC_ARN)
225230

231+
# StepFunctions
232+
self.assertTrue("stepfunctions" in _KNOWN_EXTENSIONS)
233+
stepfunctions_attributes: Dict[str, str] = _do_extract_stepfunctions_attributes()
234+
self.assertTrue("aws.stepfunctions.state_machine.arn" in stepfunctions_attributes)
235+
self.assertEqual(stepfunctions_attributes["aws.stepfunctions.state_machine.arn"], _STATE_MACHINE_ARN)
236+
self.assertTrue("aws.stepfunctions.activity.arn" in stepfunctions_attributes)
237+
self.assertEqual(stepfunctions_attributes["aws.stepfunctions.activity.arn"], _ACTIVITY_ARN)
238+
226239
def _test_patched_gevent_os_ssl_instrumentation(self):
227240
# Only ssl and os module should have been patched since the environment variable was set to 'os, ssl'
228241
self.assertTrue(gevent.monkey.is_module_patched("ssl"), "gevent ssl module has not been patched")
@@ -399,6 +412,12 @@ def _do_extract_sns_attributes() -> Dict[str, str]:
399412
return _do_extract_attributes(service_name, params)
400413

401414

415+
def _do_extract_stepfunctions_attributes() -> Dict[str, str]:
416+
service_name: str = "stepfunctions"
417+
params: Dict[str, str] = {"stateMachineArn": _STATE_MACHINE_ARN, "activityArn": _ACTIVITY_ARN}
418+
return _do_extract_attributes(service_name, params)
419+
420+
402421
def _do_extract_attributes(service_name: str, params: Dict[str, Any], operation: str = None) -> Dict[str, str]:
403422
mock_call_context: MagicMock = MagicMock()
404423
mock_call_context.params = params

0 commit comments

Comments
 (0)