Skip to content

Commit 502046d

Browse files
authored
Merge branch 'main' into add-layer-sha256
2 parents 2b1685c + 08a9c1a commit 502046d

File tree

6 files changed

+139
-2
lines changed

6 files changed

+139
-2
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
AWS_SDK_DESCENDANT: str = "aws.sdk.descendant"
1414
AWS_CONSUMER_PARENT_SPAN_KIND: str = "aws.consumer.parent.span.kind"
1515
AWS_TRACE_FLAG_SAMPLED: str = "aws.trace.flag.sampled"
16+
AWS_TRACE_LAMBDA_FLAG_MULTIPLE_SERVER: str = "aws.trace.lambda.multiple-server"
1617
AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER: str = "aws.remote.resource.cfn.primary.identifier"
1718

1819
# AWS_#_NAME attributes are not supported in python as they are not part of the Semantic Conventions.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ def get_ingress_operation(__, span: ReadableSpan) -> str:
5757
with the first API path parameter" if the default span name is None, UnknownOperation or http.method value.
5858
"""
5959
operation: str = span.name
60-
if _AWS_LAMBDA_FUNCTION_NAME in os.environ:
60+
scope = getattr(span, "instrumentation_scope", None)
61+
if _AWS_LAMBDA_FUNCTION_NAME in os.environ and scope.name != "opentelemetry.instrumentation.flask":
6162
operation = os.environ.get(_AWS_LAMBDA_FUNCTION_NAME) + "/FunctionHandler"
6263
elif should_use_internal_operation(span):
6364
operation = INTERNAL_OPERATION
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
from typing import Optional
4+
5+
from typing_extensions import override
6+
7+
from amazon.opentelemetry.distro._aws_attribute_keys import AWS_TRACE_LAMBDA_FLAG_MULTIPLE_SERVER
8+
from opentelemetry.context import Context, get_value
9+
from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor
10+
from opentelemetry.trace import SpanKind
11+
from opentelemetry.trace.propagation import _SPAN_KEY
12+
13+
14+
class AwsLambdaSpanProcessor(SpanProcessor):
15+
def __init__(self, instrumentation_names=None):
16+
self.instrumentation_names = set(instrumentation_names or ["opentelemetry.instrumentation.flask"])
17+
18+
@override
19+
def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None:
20+
21+
scope = getattr(span, "instrumentation_scope", None)
22+
if scope.name in self.instrumentation_names:
23+
parent_span = get_value(_SPAN_KEY, context=parent_context)
24+
25+
if parent_span is None:
26+
return
27+
28+
parent_scope = getattr(parent_span, "instrumentation_scope", None)
29+
if parent_scope.name == "opentelemetry.instrumentation.aws_lambda":
30+
span._kind = SpanKind.SERVER
31+
parent_span.set_attribute(AWS_TRACE_LAMBDA_FLAG_MULTIPLE_SERVER, True)
32+
33+
return
34+
35+
# pylint: disable=no-self-use
36+
@override
37+
def on_end(self, span: ReadableSpan) -> None:
38+
return
39+
40+
@override
41+
def shutdown(self) -> None:
42+
self.force_flush()
43+
44+
# pylint: disable=no-self-use
45+
@override
46+
def force_flush(self, timeout_millis: int = None) -> bool:
47+
return True

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
AttributePropagatingSpanProcessorBuilder,
1717
)
1818
from amazon.opentelemetry.distro.aws_batch_unsampled_span_processor import BatchUnsampledSpanProcessor
19+
from amazon.opentelemetry.distro.aws_lambda_span_processor import AwsLambdaSpanProcessor
1920
from amazon.opentelemetry.distro.aws_metric_attributes_span_exporter_builder import (
2021
AwsMetricAttributesSpanExporterBuilder,
2122
)
@@ -343,6 +344,7 @@ def _customize_span_processors(provider: TracerProvider, resource: Resource) ->
343344
# Export 100% spans and not export Application-Signals metrics if on Lambda.
344345
if _is_lambda_environment():
345346
_export_unsampled_span_for_lambda(provider, resource)
347+
provider.add_span_processor(AwsLambdaSpanProcessor())
346348
return
347349

348350
# Construct meterProvider
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
from unittest import TestCase
4+
from unittest.mock import MagicMock, patch
5+
6+
from amazon.opentelemetry.distro._aws_attribute_keys import AWS_TRACE_LAMBDA_FLAG_MULTIPLE_SERVER
7+
from amazon.opentelemetry.distro.aws_lambda_span_processor import AwsLambdaSpanProcessor
8+
from opentelemetry.context import Context, set_value
9+
from opentelemetry.trace import Span, SpanContext, SpanKind
10+
from opentelemetry.trace.propagation import _SPAN_KEY
11+
12+
13+
class TestAwsLambdaSpanProcessor(TestCase):
14+
15+
def setUp(self):
16+
self.processor = AwsLambdaSpanProcessor()
17+
self.lambda_span: Span = MagicMock()
18+
self.lambda_span.instrumentation_scope.name = "opentelemetry.instrumentation.aws_lambda"
19+
self.lambda_span.kind = SpanKind.SERVER
20+
21+
self.lambda_span_context: SpanContext = MagicMock()
22+
self.lambda_span_context.trace_id = "ABC"
23+
self.lambda_span_context.span_id = "lambda_id"
24+
25+
self.lambda_context: Context = set_value(_SPAN_KEY, self.lambda_span)
26+
27+
self.lambda_span.get_span_context.return_value = self.lambda_span_context
28+
self.processor.on_start(self.lambda_span)
29+
30+
def tearDown(self):
31+
self.processor.on_end(self.lambda_span)
32+
self.processor.shutdown()
33+
34+
@patch("opentelemetry.sdk.trace.Span")
35+
def test_lambda_span_multiple_server_flag_internal_api(self, mock_span_class):
36+
37+
flask_span = mock_span_class.return_value
38+
flask_span.instrumentation_scope.name = "opentelemetry.instrumentation.flask"
39+
flask_span.kind = SpanKind.INTERNAL
40+
flask_span.parent = self.lambda_span_context
41+
42+
self.processor.on_start(flask_span, self.lambda_context)
43+
44+
self.assertEqual(flask_span._kind, SpanKind.SERVER)
45+
self.assertIn(AWS_TRACE_LAMBDA_FLAG_MULTIPLE_SERVER, self.lambda_span.set_attribute.call_args_list[0][0][0])
46+
47+
self.processor.on_end(flask_span)
48+
self.processor.on_end(self.lambda_span)
49+
50+
self.processor.shutdown()
51+
52+
@patch("opentelemetry.sdk.trace.Span")
53+
def test_lambda_span_multiple_server_flag_server_api(self, mock_span_class):
54+
55+
flask_span = mock_span_class.return_value
56+
flask_span.instrumentation_scope.name = "opentelemetry.instrumentation.flask"
57+
flask_span.kind = SpanKind.SERVER
58+
flask_span.parent = self.lambda_span_context
59+
60+
self.processor.on_start(flask_span, self.lambda_context)
61+
62+
self.assertEqual(flask_span.kind, SpanKind.SERVER)
63+
self.assertIn(AWS_TRACE_LAMBDA_FLAG_MULTIPLE_SERVER, self.lambda_span.set_attribute.call_args_list[0][0][0])
64+
65+
self.processor.on_end(flask_span)
66+
self.processor.on_end(self.lambda_span)
67+
68+
self.processor.shutdown()
69+
70+
@patch("opentelemetry.sdk.trace.Span")
71+
def test_lambda_span_single_server_span(self, mock_span_class):
72+
73+
flask_span = mock_span_class.return_value
74+
flask_span.instrumentation_scope.name = "opentelemetry.instrumentation.http"
75+
flask_span.kind = SpanKind.CLIENT
76+
flask_span.parent = self.lambda_span_context
77+
78+
self.processor.on_start(flask_span, self.lambda_context)
79+
80+
self.assertEqual(flask_span.kind, SpanKind.CLIENT)
81+
flask_span.set_attribute.assert_not_called()
82+
83+
self.processor.on_end(flask_span)
84+
self.processor.on_end(self.lambda_span)
85+
86+
self.processor.shutdown()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def test_customize_span_processors_lambda(self):
318318
os.environ.setdefault("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "True")
319319
os.environ.setdefault("AWS_LAMBDA_FUNCTION_NAME", "myLambdaFunc")
320320
_customize_span_processors(mock_tracer_provider, Resource.get_empty())
321-
self.assertEqual(mock_tracer_provider.add_span_processor.call_count, 2)
321+
self.assertEqual(mock_tracer_provider.add_span_processor.call_count, 3)
322322
first_processor: SpanProcessor = mock_tracer_provider.add_span_processor.call_args_list[0].args[0]
323323
self.assertIsInstance(first_processor, AttributePropagatingSpanProcessor)
324324
second_processor: SpanProcessor = mock_tracer_provider.add_span_processor.call_args_list[1].args[0]

0 commit comments

Comments
 (0)