Skip to content

Commit

Permalink
propagate Lambda span attribute cloud.resource_id to child spans (#284)
Browse files Browse the repository at this point in the history
*Issue #, if available:*

AppSignals lambda dependency metrics don't show Lambda Alias correctly

*Description of changes:*
Propagate span attribute cloud.resource_id if there is.


By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
  • Loading branch information
wangzlei authored Oct 19, 2024
1 parent de63a81 commit 095c109
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def get_ingress_operation(__, span: ReadableSpan) -> str:
"""
operation: str = span.name
if _AWS_LAMBDA_FUNCTION_NAME in os.environ:
operation = os.environ.get(_AWS_LAMBDA_FUNCTION_NAME) + "/Handler"
operation = os.environ.get(_AWS_LAMBDA_FUNCTION_NAME) + "/FunctionHandler"
elif should_use_internal_operation(span):
operation = INTERNAL_OPERATION
elif not _is_valid_operation(span, operation):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from amazon.opentelemetry.distro._aws_span_processing_util import is_aws_sdk_span, is_local_root
from opentelemetry.context import Context
from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import SpanKind
from opentelemetry.trace.propagation import get_current_span

Expand Down Expand Up @@ -62,6 +63,12 @@ def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None
if _is_consumer_kind(span) and _is_consumer_kind(parent_span):
span.set_attribute(AWS_CONSUMER_PARENT_SPAN_KIND, parent_span.kind.name)

# Propagate span attribute cloud.resource_id for extracting lambda alias for dependency metrics.
parent_resource_id = parent_span.attributes.get(SpanAttributes.CLOUD_RESOURCE_ID)
current_resource_id = span.attributes.get(SpanAttributes.CLOUD_RESOURCE_ID)
if current_resource_id is None and parent_resource_id is not None:
span.set_attribute(SpanAttributes.CLOUD_RESOURCE_ID, parent_resource_id)

propagation_data: str = None
if is_local_root(span):
if not _is_server_kind(span):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,26 @@ def _validate_span_attributes_inheritance(
self.assertEqual(propagation_value2, leaf_span.attributes.get(_TEST_KEY_2))
else:
self.assertIsNone(leaf_span.attributes.get(_TEST_KEY_2))

def test_attributes_propagation_cloud_resource_id(self):
cloud_resource_id = "arn:x1"
grand_parent_span: Span = self.tracer.start_span(
name="grandparent", kind=SpanKind.INTERNAL, attributes={_TEST_KEY_1: "testValue1"}
)
parent_span: Span = self.tracer.start_span(
name="parent",
kind=SpanKind.SERVER,
attributes={_TEST_KEY_2: "testValue2", SpanAttributes.CLOUD_RESOURCE_ID: cloud_resource_id},
context=set_span_in_context(grand_parent_span),
)
child_span: Span = self.tracer.start_span(
name="child", kind=SpanKind.INTERNAL, context=set_span_in_context(parent_span)
)
grand_child_span: Span = self.tracer.start_span(
name="child", kind=SpanKind.CLIENT, context=set_span_in_context(child_span)
)

self.assertIsNone(grand_parent_span.attributes.get(SpanAttributes.CLOUD_RESOURCE_ID))
self.assertIsNotNone(parent_span.attributes.get(SpanAttributes.CLOUD_RESOURCE_ID))
self.assertEqual(child_span.attributes.get(SpanAttributes.CLOUD_RESOURCE_ID), cloud_resource_id)
self.assertEqual(grand_child_span.attributes.get(SpanAttributes.CLOUD_RESOURCE_ID), cloud_resource_id)
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_get_ingress_operation_in_lambda(self):
self.span_data_mock.name = valid_name
self.span_data_mock.kind = SpanKind.SERVER
actual_operation: str = get_ingress_operation(self, self.span_data_mock)
self.assertEqual(actual_operation, "MyLambda/Handler")
self.assertEqual(actual_operation, "MyLambda/FunctionHandler")

def test_get_ingress_operation_http_method_name_and_no_fallback(self):
invalid_name: str = "GET"
Expand Down

0 comments on commit 095c109

Please sign in to comment.