Skip to content

Commit 4cd8ed4

Browse files
authored
[Lambda Java v2.11.x] Merge All Code Changes from v1.33.x Branch into v2.11.x (#1114)
This change merges all private Lambda Java updates from the v1.33 branch into the v2.11.x branch. I performed a 'git rebase v2.11' on the v1.33 branch, reviewed all changes, and completed the build and testing process. The resulting Lambda layer generated trace data identical to the version built directly from the v2.11.x branch (excluding this PR). Here is the list of all migrated PRs: Build layer during CI/CD workflows + some minor refactoring #989 support java11 runtime for lambda #1001 Unique artifact names for upload and merge for download #1014 Bug fixes] Lambda - duplicate lambda spans + appsignals from unsampled spans #1000 Fix: Lambda Topology Issue (#1016) Fix: Lambda Topology Issue (#1016) #1085 feat: Support microservice span in Lambda Java environment. #1053 Test Tested Java11, 17, and 21 Lambda functions. Manually tested PR-1000 and PR-1053. Both work as expected in the v2.11 branch. MicroService (SpringBoot) support works well. I verified attribute Trace.lambda.multiple server can be found in the Lambda server span, once we have Servlet instrumentation enabled with OTEL_INSTRUMENTATION_SERVLET_ENABLED. Note: The changes in the patch files are not included in this PR. They should have been reviewed and incorporated as part of this migration: Upgrade Java Lambda Layer to 2.x #1076 Lambda with SpringBoot MicroService: <img width="1367" alt="lambda" src="https://github.com/user-attachments/assets/5cf5be29-4986-454c-b61b-773d6cde3848" /> Service Map and added microservice attribute 'Trace.lambda.multiple server'. <img width="1864" alt="traceMap" src="https://github.com/user-attachments/assets/f7ff1771-61f0-4013-b571-90370a726aa9" /> AppSignals <img width="1875" alt="appSignals" src="https://github.com/user-attachments/assets/24f1b3a8-851c-4c97-bb50-087ee275b86d" /> By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent f04f3bf commit 4cd8ed4

File tree

9 files changed

+373
-4
lines changed

9 files changed

+373
-4
lines changed

.github/workflows/main-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ jobs:
256256
aws s3 cp ./build/distributions/aws-opentelemetry-java-layer.zip s3://adot-main-build-staging-jar/adot-java-lambda-layer-${{ github.run_id }}.zip
257257
258258
application-signals-e2e-test:
259-
needs: [build]
259+
needs: [build, application-signals-lambda-layer-build]
260260
uses: ./.github/workflows/application-signals-e2e-test.yml
261261
secrets: inherit
262262
with:

.github/workflows/release-lambda.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ jobs:
109109
aws lambda publish-layer-version \
110110
--layer-name ${{ env.LAYER_NAME }} \
111111
--content S3Bucket=${{ env.BUCKET_NAME }},S3Key=aws-opentelemetry-java-layer.zip \
112-
--compatible-runtimes java17 java21 \
112+
--compatible-runtimes java11 java17 java21 \
113113
--compatible-architectures "arm64" "x86_64" \
114114
--license-info "Apache-2.0" \
115115
--description "AWS Distro of OpenTelemetry Lambda Layer for Java Runtime" \
@@ -140,7 +140,7 @@ jobs:
140140
if: ${{ success() }}
141141
uses: actions/upload-artifact@v4
142142
with:
143-
name: ${{ env.LAYER_NAME }}
143+
name: ${{ env.LAYER_NAME }}-${{ matrix.aws_region }}
144144
path: ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}
145145

146146
- name: clean s3
@@ -154,20 +154,24 @@ jobs:
154154
steps:
155155
- name: Checkout Repo @ SHA - ${{ github.sha }}
156156
uses: actions/checkout@v4
157+
157158
- uses: hashicorp/setup-terraform@v2
159+
158160
- name: download layerARNs
159161
uses: actions/download-artifact@v4
160162
with:
161163
pattern: ${{ env.LAYER_NAME }}-*
162164
path: ${{ env.LAYER_NAME }}
163165
merge-multiple: true
166+
164167
- name: show layerARNs
165168
run: |
166169
for file in ${{ env.LAYER_NAME }}/*
167170
do
168171
echo $file
169172
cat $file
170173
done
174+
171175
- name: generate layer-note
172176
working-directory: ${{ env.LAYER_NAME }}
173177
run: |

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,10 @@ private Sampler customizeSampler(Sampler sampler, ConfigProperties configProps)
283283

284284
private SdkTracerProviderBuilder customizeTracerProviderBuilder(
285285
SdkTracerProviderBuilder tracerProviderBuilder, ConfigProperties configProps) {
286+
if (isLambdaEnvironment()) {
287+
tracerProviderBuilder.addSpanProcessor(new AwsLambdaSpanProcessor());
288+
}
289+
286290
if (isApplicationSignalsEnabled(configProps)) {
287291
logger.info("AWS Application Signals enabled");
288292
Duration exportInterval =
@@ -294,9 +298,27 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
294298

295299
// If running on Lambda, we just need to export 100% spans and skip generating any Application
296300
// Signals metrics.
297-
if (isLambdaEnvironment()) {
301+
if (isLambdaEnvironment()
302+
&& System.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG) == null) {
303+
String tracesEndpoint =
304+
Optional.ofNullable(System.getenv(AWS_XRAY_DAEMON_ADDRESS_CONFIG))
305+
.orElse(DEFAULT_UDP_ENDPOINT);
306+
SpanExporter spanExporter =
307+
new OtlpUdpSpanExporterBuilder()
308+
.setPayloadSampleDecision(TracePayloadSampleDecision.UNSAMPLED)
309+
.setEndpoint(tracesEndpoint)
310+
.build();
311+
312+
// Wrap the udp exporter with the AwsMetricsAttributesSpanExporter to add Application
313+
// Signals attributes to unsampled spans too
314+
SpanExporter appSignalsSpanExporter =
315+
AwsMetricAttributesSpanExporterBuilder.create(
316+
spanExporter, ResourceHolder.getResource())
317+
.build();
318+
298319
tracerProviderBuilder.addSpanProcessor(
299320
AwsUnsampledOnlySpanProcessorBuilder.create()
321+
.setSpanExporter(appSignalsSpanExporter)
300322
.setMaxExportBatchSize(LAMBDA_SPAN_EXPORT_BATCH_SIZE)
301323
.build());
302324
return tracerProviderBuilder;

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,7 @@ private AwsAttributeKeys() {}
9797
AttributeKey.stringKey("aws.bedrock.guardrail.id");
9898
static final AttributeKey<String> AWS_GUARDRAIL_ARN =
9999
AttributeKey.stringKey("aws.bedrock.guardrail.arn");
100+
101+
static final AttributeKey<Boolean> AWS_TRACE_LAMBDA_MULTIPLE_SERVER =
102+
AttributeKey.booleanKey("aws.trace.lambda.multiple-server");
100103
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.javaagent.providers;
17+
18+
import io.opentelemetry.api.trace.Span;
19+
import io.opentelemetry.context.Context;
20+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
21+
import io.opentelemetry.sdk.trace.ReadableSpan;
22+
import io.opentelemetry.sdk.trace.SpanProcessor;
23+
import javax.annotation.concurrent.Immutable;
24+
25+
@Immutable
26+
public final class AwsLambdaSpanProcessor implements SpanProcessor {
27+
@Override
28+
public void onStart(Context parentContext, ReadWriteSpan span) {
29+
if (AwsSpanProcessingUtil.isServletServerSpan(span)) {
30+
Span parentSpan = Span.fromContextOrNull(parentContext);
31+
if (parentSpan == null || !(parentSpan instanceof ReadWriteSpan)) {
32+
return;
33+
}
34+
35+
ReadWriteSpan parentReadWriteSpan = (ReadWriteSpan) parentSpan;
36+
if (!AwsSpanProcessingUtil.isLambdaServerSpan(parentReadWriteSpan)) {
37+
return;
38+
}
39+
parentReadWriteSpan.setAttribute(AwsAttributeKeys.AWS_TRACE_LAMBDA_MULTIPLE_SERVER, true);
40+
}
41+
}
42+
43+
@Override
44+
public boolean isStartRequired() {
45+
return true;
46+
}
47+
48+
@Override
49+
public void onEnd(ReadableSpan span) {}
50+
51+
@Override
52+
public boolean isEndRequired() {
53+
return false;
54+
}
55+
}

awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanProcessingUtil.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import io.opentelemetry.api.trace.SpanContext;
3838
import io.opentelemetry.api.trace.SpanKind;
3939
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
40+
import io.opentelemetry.sdk.trace.ReadableSpan;
4041
import io.opentelemetry.sdk.trace.data.SpanData;
4142
import java.io.IOException;
4243
import java.io.InputStream;
@@ -68,6 +69,10 @@ final class AwsSpanProcessingUtil {
6869

6970
private static final String SQL_DIALECT_KEYWORDS_JSON = "configuration/sql_dialect_keywords.json";
7071

72+
static final AttributeKey<String> OTEL_SCOPE_NAME = AttributeKey.stringKey("otel.scope.name");
73+
static final String LAMBDA_SCOPE_PREFIX = "io.opentelemetry.aws-lambda-";
74+
static final String SERVLET_SCOPE_PREFIX = "io.opentelemetry.servlet-";
75+
7176
static List<String> getDialectKeywords() {
7277
try (InputStream jsonFile =
7378
AwsSpanProcessingUtil.class
@@ -91,6 +96,10 @@ static List<String> getDialectKeywords() {
9196
*/
9297
static String getIngressOperation(SpanData span) {
9398
if (isLambdaEnvironment()) {
99+
String op = generateIngressOperation(span);
100+
if (!op.equals(UNKNOWN_OPERATION)) {
101+
return op;
102+
}
94103
return System.getenv(AWS_LAMBDA_FUNCTION_NAME_CONFIG) + "/FunctionHandler";
95104
}
96105
String operation = span.getName();
@@ -248,4 +257,30 @@ static boolean isDBSpan(SpanData span) {
248257
|| isKeyPresent(span, DB_OPERATION)
249258
|| isKeyPresent(span, DB_STATEMENT);
250259
}
260+
261+
static boolean isLambdaServerSpan(ReadableSpan span) {
262+
String scopeName = null;
263+
if (span != null
264+
&& span.toSpanData() != null
265+
&& span.toSpanData().getInstrumentationScopeInfo() != null) {
266+
scopeName = span.toSpanData().getInstrumentationScopeInfo().getName();
267+
}
268+
269+
return scopeName != null
270+
&& scopeName.startsWith(LAMBDA_SCOPE_PREFIX)
271+
&& SpanKind.SERVER == span.getKind();
272+
}
273+
274+
static boolean isServletServerSpan(ReadableSpan span) {
275+
String scopeName = null;
276+
if (span != null
277+
&& span.toSpanData() != null
278+
&& span.toSpanData().getInstrumentationScopeInfo() != null) {
279+
scopeName = span.toSpanData().getInstrumentationScopeInfo().getName();
280+
}
281+
282+
return scopeName != null
283+
&& scopeName.startsWith(SERVLET_SCOPE_PREFIX)
284+
&& SpanKind.SERVER == span.getKind();
285+
}
251286
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.javaagent.providers;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.junit.jupiter.api.Assertions.assertNull;
20+
import static org.mockito.Mockito.*;
21+
22+
import io.opentelemetry.api.common.AttributeKey;
23+
import io.opentelemetry.api.trace.Span;
24+
import io.opentelemetry.api.trace.SpanContext;
25+
import io.opentelemetry.api.trace.SpanKind;
26+
import io.opentelemetry.api.trace.Tracer;
27+
import io.opentelemetry.context.Context;
28+
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
29+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
30+
import io.opentelemetry.sdk.trace.ReadableSpan;
31+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
32+
import io.opentelemetry.sdk.trace.data.SpanData;
33+
import java.util.Map;
34+
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.Test;
36+
37+
class AwsLambdaSpanProcessorTest {
38+
39+
private AwsLambdaSpanProcessor processor;
40+
private ReadWriteSpan mockLambdaServerSpan;
41+
private SpanData mockLambdaSpanData;
42+
private InstrumentationScopeInfo mockLambdaScopeInfo;
43+
private Map<AttributeKey<?>, Object> attributeMapForLambdaSpan;
44+
private SpanContext mockSpanContext;
45+
46+
private ReadWriteSpan mockServletServerSpan;
47+
private SpanData mockServletSpanData;
48+
private InstrumentationScopeInfo mockServletScopeInfo;
49+
50+
private Tracer lambdaTracer;
51+
private Tracer servletTracer;
52+
private Tracer otherTracer;
53+
54+
@BeforeEach
55+
public void setup() {
56+
processor = new AwsLambdaSpanProcessor();
57+
lambdaTracer =
58+
SdkTracerProvider.builder()
59+
.addSpanProcessor(processor)
60+
.build()
61+
.get(AwsSpanProcessingUtil.LAMBDA_SCOPE_PREFIX + "core-1.0");
62+
63+
servletTracer =
64+
SdkTracerProvider.builder()
65+
.addSpanProcessor(processor)
66+
.build()
67+
.get(AwsSpanProcessingUtil.SERVLET_SCOPE_PREFIX + "lib-3.0");
68+
69+
otherTracer =
70+
SdkTracerProvider.builder().addSpanProcessor(processor).build().get("other-lib-2.0");
71+
}
72+
73+
@Test
74+
void testOnStart_servletServerSpan_withLambdaServerSpan() {
75+
Span parentSpan =
76+
lambdaTracer.spanBuilder("parent-lambda").setSpanKind(SpanKind.SERVER).startSpan();
77+
servletTracer
78+
.spanBuilder("child-servlet")
79+
.setSpanKind(SpanKind.SERVER)
80+
.setParent(Context.current().with(parentSpan))
81+
.startSpan();
82+
83+
ReadableSpan parentReadableSpan = (ReadableSpan) parentSpan;
84+
assertThat(parentReadableSpan.getAttribute(AwsAttributeKeys.AWS_TRACE_LAMBDA_MULTIPLE_SERVER))
85+
.isEqualTo(true);
86+
}
87+
88+
@Test
89+
void testOnStart_servletInternalSpan_withLambdaServerSpan() {
90+
Span parentSpan =
91+
lambdaTracer.spanBuilder("parent-lambda").setSpanKind(SpanKind.SERVER).startSpan();
92+
93+
servletTracer
94+
.spanBuilder("child-servlet")
95+
.setSpanKind(SpanKind.INTERNAL)
96+
.setParent(Context.current().with(parentSpan))
97+
.startSpan();
98+
99+
ReadableSpan parentReadableSpan = (ReadableSpan) parentSpan;
100+
assertNull(parentReadableSpan.getAttribute(AwsAttributeKeys.AWS_TRACE_LAMBDA_MULTIPLE_SERVER));
101+
}
102+
103+
@Test
104+
void testOnStart_servletServerSpan_withLambdaInternalSpan() {
105+
Span parentSpan =
106+
lambdaTracer.spanBuilder("parent-lambda").setSpanKind(SpanKind.INTERNAL).startSpan();
107+
108+
servletTracer
109+
.spanBuilder("child-servlet")
110+
.setSpanKind(SpanKind.SERVER)
111+
.setParent(Context.current().with(parentSpan))
112+
.startSpan();
113+
114+
ReadableSpan parentReadableSpan = (ReadableSpan) parentSpan;
115+
assertNull(parentReadableSpan.getAttribute(AwsAttributeKeys.AWS_TRACE_LAMBDA_MULTIPLE_SERVER));
116+
}
117+
118+
@Test
119+
void testOnStart_servletServerSpan_withLambdaServerSpanAsGrandParent() {
120+
Span grandParentSpan =
121+
lambdaTracer.spanBuilder("grandparent-lambda").setSpanKind(SpanKind.SERVER).startSpan();
122+
123+
Span parentSpan =
124+
otherTracer
125+
.spanBuilder("parent-other")
126+
.setSpanKind(SpanKind.SERVER)
127+
.setParent(Context.current().with(grandParentSpan))
128+
.startSpan();
129+
130+
servletTracer
131+
.spanBuilder("child-servlet")
132+
.setSpanKind(SpanKind.SERVER)
133+
.setParent(Context.current().with(parentSpan))
134+
.startSpan();
135+
136+
ReadableSpan grandParentReadableSpan = (ReadableSpan) grandParentSpan;
137+
assertNull(
138+
grandParentReadableSpan.getAttribute(AwsAttributeKeys.AWS_TRACE_LAMBDA_MULTIPLE_SERVER));
139+
}
140+
}

0 commit comments

Comments
 (0)