22 * Copyright 2023 Amazon.com, Inc. or its affiliates.
33 * Licensed under the Apache License, Version 2.0 (the
44 * "License"); you may not use this file except in compliance
5- * with the License. You may obtain a copy of the License at
5+ * with the License. You may obtain a copy of the License at
6+ *
67 * http://www.apache.org/licenses/LICENSE-2.0
8+ *
79 * Unless required by applicable law or agreed to in writing, software
810 * distributed under the License is distributed on an "AS IS" BASIS,
911 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1416
1517package software .amazon .lambda .powertools .tracing .opentelemetry ;
1618
17- import io .opentelemetry .api .GlobalOpenTelemetry ;
19+ import io .opentelemetry .api .common . Attributes ;
1820import io .opentelemetry .api .trace .Span ;
21+ import io .opentelemetry .api .trace .SpanKind ;
1922import io .opentelemetry .api .trace .Tracer ;
23+ import io .opentelemetry .context .Context ;
24+ import io .opentelemetry .context .propagation .TextMapGetter ;
25+ import io .opentelemetry .context .propagation .TextMapPropagator ;
26+ import io .opentelemetry .context .propagation .TextMapSetter ;
2027import java .util .Objects ;
28+ import software .amazon .lambda .powertools .common .internal .LambdaHandlerProcessor ;
29+ import software .amazon .lambda .powertools .tracing .opentelemetry .internal .OpenTelemetryProvider ;
2130import software .amazon .lambda .powertools .tracing .opentelemetry .internal .SpanOperation ;
2231import software .amazon .lambda .powertools .tracing .opentelemetry .internal .SpanScope ;
2332
24- /**
25- * A wrapper for OpenTelemetry's Tracer that simplifies the creation and management of spans.
26- * This class provides utility functions for starting and controlling spans and their contexts
27- * in the current execution thread.
28- * <p>
29- * This is a final class and cannot be extended.
30- */
31- public final class TracingOpenTelemetry {
3233
33- private static final String INSTRUMENTATION_NAME =
34- "aws-lambda-powertools" ;
34+ public final class TracingOpenTelemetry {
3535
3636 private final Tracer tracer ;
37+ private final TextMapPropagator propagator ;
38+
39+ private TracingOpenTelemetry (Builder builder ) {
40+ this .tracer = Objects .requireNonNull (
41+ builder .tracer ,
42+ "tracer must not be null"
43+ );
44+ this .propagator = Objects .requireNonNull (
45+ builder .propagator ,
46+ "propagator must not be null"
47+ );
48+ }
3749
38- /**
39- * Creates a tracing instance using the provided tracer.
40- *
41- * <p>This constructor is primarily useful for testing.
42- *
43- * @param tracer the OpenTelemetry tracer
44- */
45- TracingOpenTelemetry (Tracer tracer ) {
46- this .tracer = Objects .requireNonNull (tracer , "tracer must not be null" );
47- }
48-
49- /**
50- * Initializes a new instance of the {@code TracingOpenTelemetry} class, using
51- * the global OpenTelemetry tracer identified by the instrumentation name.
52- * <p>
53- * This constructor simplifies the setup process for applications by
54- * automatically leveraging the globally configured instrumentation tracer.
55- */
5650 public TracingOpenTelemetry () {
57- this (GlobalOpenTelemetry .getTracer (INSTRUMENTATION_NAME ));
51+ this (OpenTelemetryProvider .tracer ());
52+ }
53+
54+
55+ public TracingOpenTelemetry (Tracer tracer ) {
56+ this (tracer , createDefaultPropagator ());
5857 }
5958
60- /**
61- * Creates a new span with the specified name and makes it the current span in the thread context.
62- * The span must be manually closed to properly end it and revert the thread context.
63- *
64- * @param name the name of the span to be created
65- * @return an instance of {@link SpanScope}, which represents the created span and its associated context
66- */
59+
60+ public TracingOpenTelemetry (
61+ Tracer tracer ,
62+ TextMapPropagator propagator ) {
63+
64+ this .tracer = Objects .requireNonNull (
65+ tracer ,
66+ "tracer must not be null"
67+ );
68+ this .propagator = Objects .requireNonNull (
69+ propagator ,
70+ "propagator must not be null"
71+ );
72+ }
73+
74+
6775 public SpanScope addSpan (String name ) {
68- Span span = tracer
69- .spanBuilder (name )
76+ return addSpan (name , SpanKind .INTERNAL );
77+ }
78+
79+
80+ public SpanScope addSpan (
81+ String name ,
82+ SpanKind kind ) {
83+
84+ return addSpan (name , kind , Attributes .empty ());
85+ }
86+
87+
88+ public SpanScope addSpan (
89+ String name ,
90+ SpanKind kind ,
91+ Attributes attributes ) {
92+
93+ Objects .requireNonNull (name , "name must not be null" );
94+ Objects .requireNonNull (kind , "kind must not be null" );
95+ Objects .requireNonNull (attributes , "attributes must not be null" );
96+
97+ Span span = tracer .spanBuilder (name )
98+ .setSpanKind (kind )
99+ .setAllAttributes (attributes )
70100 .startSpan ();
71101
72102 return new SpanScope (span );
73103 }
74104
75- /**
76- * Retrieves the current active span in the execution context.
77- *
78- * @return the current {@link Span} if one is active, or a default no-op {@link Span} if none is active
79- */
105+ public SpanScope addSpan (
106+ String name ,
107+ SpanKind kind ,
108+ Attributes attributes ,
109+ Context parentContext ) {
110+
111+ Objects .requireNonNull (parentContext , "parentContext must not be null" );
112+
113+ Span span = tracer .spanBuilder (name )
114+ .setParent (parentContext )
115+ .setSpanKind (kind )
116+ .setAllAttributes (attributes )
117+ .startSpan ();
118+
119+ return new SpanScope (span );
120+ }
121+
122+
80123 public Span currentSpan () {
81124 return Span .current ();
82125 }
83126
84- /**
85- * Executes the specified operation within the context of a new span.
86- * The span is automatically managed and closed when the operation completes
87- * or an exception is thrown.
88- *
89- * @param name the name of the span to be created
90- * @param operation the operation to be executed within the span's context
91- * @throws Exception if the provided operation throws an exception during execution
92- */
93- public <T > T withSpan (String name , SpanOperation <T > operation ) throws Exception {
127+
128+ public <T > T withSpan (
129+ String name ,
130+ SpanOperation <T > operation ) throws Exception {
131+
132+ Objects .requireNonNull (operation , "operation must not be null" );
133+
94134 try (SpanScope scope = addSpan (name )) {
95135 try {
96136 return operation .execute (scope .span ());
97- } catch (Exception e ) {
98- scope .recordException (e );
99- throw e ;
137+ } catch (Exception exception ) {
138+ scope .recordException (exception );
139+ throw exception ;
100140 }
101141 }
102142 }
103143
104- /**
105- * Creates a new tracing instance using the global OpenTelemetry tracer.
106- *
107- * @return a new tracing instance
108- */
144+ public <T > T captureLambdaHandler (
145+ String name ,
146+ com .amazonaws .services .lambda .runtime .Context lambdaContext ,
147+ io .opentelemetry .context .Context parentContext ,
148+ SpanOperation <T > operation ) throws Exception {
149+
150+ Objects .requireNonNull (name , "name must not be null" );
151+ Objects .requireNonNull (parentContext , "parentContext must not be null" );
152+ Objects .requireNonNull (operation , "operation must not be null" );
153+
154+ Span span = tracer .spanBuilder (name )
155+ .setParent (parentContext )
156+ .setSpanKind (SpanKind .SERVER )
157+ .setAttribute ("faas.coldstart" , LambdaHandlerProcessor .isColdStart ())
158+ .setAttribute ("faas.invocation_id" , lambdaContext .getAwsRequestId ())
159+ .startSpan ();
160+
161+ try (SpanScope scope = new SpanScope (span )) {
162+ try {
163+ T result = operation .execute (span );
164+
165+ LambdaHandlerProcessor .coldStartDone ();
166+
167+ return result ;
168+ } catch (Exception exception ) {
169+ scope .recordException (exception );
170+ throw exception ;
171+ }
172+ }
173+ }
174+
175+ public <T > Context extractContext (
176+ T carrier ,
177+ TextMapGetter <T > getter ) {
178+
179+ return extractContext (Context .current (), carrier , getter );
180+ }
181+
182+
183+ public <T > Context extractContext (
184+ Context context ,
185+ T carrier ,
186+ TextMapGetter <T > getter ) {
187+
188+ Objects .requireNonNull (context , "context must not be null" );
189+ Objects .requireNonNull (getter , "getter must not be null" );
190+
191+ return propagator .extract (
192+ context ,
193+ carrier ,
194+ getter
195+ );
196+ }
197+
198+
199+ public <T > void injectContext (
200+ T carrier ,
201+ TextMapSetter <T > setter ) {
202+
203+ injectContext (Context .current (), carrier , setter );
204+ }
205+
206+
207+ public <T > void injectContext (
208+ Context context ,
209+ T carrier ,
210+ TextMapSetter <T > setter ) {
211+
212+ Objects .requireNonNull (context , "context must not be null" );
213+ Objects .requireNonNull (setter , "setter must not be null" );
214+
215+ propagator .inject (
216+ context ,
217+ carrier ,
218+ setter
219+ );
220+ }
221+
222+ private static TextMapPropagator createDefaultPropagator () {
223+ return OpenTelemetryProvider .propagator ();
224+ }
225+
226+
109227 public static TracingOpenTelemetry create () {
110228 return new TracingOpenTelemetry ();
111229 }
112230
231+
232+ public static Builder builder () {
233+ return new Builder ();
234+ }
235+
236+ public static final class Builder {
237+
238+ private Tracer tracer ;
239+ private TextMapPropagator propagator = createDefaultPropagator ();
240+
241+ public Builder tracer (Tracer tracer ) {
242+ this .tracer = tracer ;
243+ return this ;
244+ }
245+
246+ public Builder propagator (TextMapPropagator propagator ) {
247+ this .propagator = propagator ;
248+ return this ;
249+ }
250+
251+ public TracingOpenTelemetry build () {
252+ return new TracingOpenTelemetry (this );
253+ }
254+ }
113255}
0 commit comments