11package com .datadoghq .datadog_lambda_java ;
22
3+ import io .opentracing .Scope ;
4+ import io .opentracing .SpanContext ;
5+ import io .opentracing .Tracer ;
6+ import io .opentracing .Tracer .SpanBuilder ;
7+ import io .opentracing .propagation .Format .Builtin ;
8+ import io .opentracing .propagation .TextMapAdapter ;
39import java .io .IOException ;
410import java .net .URL ;
511import java .net .URLConnection ;
@@ -28,8 +34,10 @@ public class DDLambda {
2834 private String MDC_TRACE_CONTEXT_FIELD = "dd.trace_context" ;
2935 private String JSON_TRACE_ID = "dd.trace_id" ;
3036 private String JSON_SPAN_ID = "dd.span_id" ;
37+ private String TRACE_ENABLED_ENV = "DD_TRACE_ENABLED" ;
3138 private Tracing tracing ;
3239 private boolean enhanced = true ;
40+ private Scope tracingScope ;
3341
3442 /**
3543 * Create a new DDLambda instrumenter given some Lambda context
@@ -41,7 +49,7 @@ public DDLambda(Context cxt) {
4149 this .enhanced = checkEnhanced ();
4250 recordEnhanced (INVOCATION , cxt );
4351 addTraceContextToMDC ();
44- addTagsToSpan ( cxt );
52+ startSpan ( null , cxt );
4553 }
4654
4755 /**
@@ -55,7 +63,7 @@ protected DDLambda(Context cxt, String xrayTraceInfo) {
5563 this .enhanced = checkEnhanced ();
5664 recordEnhanced (INVOCATION , cxt );
5765 addTraceContextToMDC ();
58- addTagsToSpan ( cxt );
66+ startSpan ( null , cxt );
5967 }
6068
6169 /**
@@ -71,7 +79,7 @@ public DDLambda(APIGatewayProxyRequestEvent req, Context cxt) {
7179 this .tracing = new Tracing (req );
7280 this .tracing .submitSegment ();
7381 addTraceContextToMDC ();
74- addTagsToSpan ( cxt );
82+ startSpan ( req . getHeaders (), cxt );
7583 }
7684
7785 /**
@@ -87,7 +95,7 @@ public DDLambda(APIGatewayV2ProxyRequestEvent req, Context cxt) {
8795 this .tracing = new Tracing (req );
8896 this .tracing .submitSegment ();
8997 addTraceContextToMDC ();
90- addTagsToSpan ( cxt );
98+ startSpan ( req . getHeaders (), cxt );
9199 }
92100
93101 /**
@@ -103,10 +111,69 @@ public DDLambda(Headerable req, Context cxt) {
103111 this .tracing = new Tracing (req );
104112 this .tracing .submitSegment ();
105113 addTraceContextToMDC ();
106- addTagsToSpan ( cxt );
114+ startSpan ( req . getHeaders (), cxt );
107115 }
108116
109- private void addTagsToSpan (Context cxt ) {
117+ /**
118+ * startSpan is called by the DDLambda constructors. If there is a dd-agent-java present, it will start
119+ * a span. If not, startSpan is a noop.
120+ * @param headers are the headers from the Lambda request. If Headers are null or empty, distributed tracing
121+ * is impossible but a span will still start.
122+ * @param cxt is the Lambda Context passed to the Handler function.
123+ */
124+ private void startSpan (Map <String ,String > headers , Context cxt ){
125+ //If the user has not set DD_TRACE_ENABLED=true, don't start a span
126+ if (!checkTraceEnabled ()){
127+ return ;
128+ }
129+
130+ String functionName = "" ;
131+ if (cxt != null ){
132+ functionName = cxt .getFunctionName ();
133+ }
134+
135+ //Get the Datadog tracer, if it exists
136+ Tracer tracer = GlobalTracer .get ();
137+ //Datadog tracer will be able to extract datadog trace headers from an incoming request
138+ SpanContext parentContext = tracer .extract (Builtin .HTTP_HEADERS , new TextMapAdapter (headers ));
139+
140+ SpanBuilder spanBuilder = tracer .buildSpan (functionName ).asChildOf (parentContext );
141+ spanBuilder = addDDTags (spanBuilder , cxt );
142+ Span thisSpan = spanBuilder .start ();
143+
144+ //Hang on to the scope, we'll need it later to close.
145+ this .tracingScope = tracer .activateSpan (thisSpan );
146+ }
147+
148+ /**
149+ * Finish the active span. If you have installed the dd-trace-java Lambda
150+ * layer, you MUST call DDLambda.finish() at the end of your Handler
151+ * in order to finish spans.
152+ */
153+ public void finish (){
154+ Span span = GlobalTracer .get ().activeSpan ();
155+
156+ if (this .tracingScope == null ){
157+ DDLogger .getLoggerImpl ().debug ("Unable to close tracing scope because it is null." );
158+ return ;
159+ }
160+ this .tracingScope .close ();
161+
162+ if (span != null ) {
163+ span .finish ();
164+ } else {
165+ DDLogger .getLoggerImpl ().debug ("Unable to finish span because it is null." );
166+ return ;
167+ }
168+ }
169+
170+ /**
171+ * addDDTags adds Datadog tags to a span's tags.
172+ * @param spanBuilder is the SpanBuilder being used to build the span to which these tags will be applied
173+ * @param cxt is the Lambda Context that contains the information necessary to build these tags
174+ * @return a SpanBuilder with the necessary tags.
175+ */
176+ private SpanBuilder addDDTags (SpanBuilder spanBuilder , Context cxt ) {
110177 String requestId = "" ;
111178 String functionName = "" ;
112179 String functionArn = "" ;
@@ -116,17 +183,19 @@ private void addTagsToSpan(Context cxt) {
116183 functionName = cxt .getFunctionName ();
117184 functionArn = santitizeFunctionArn (cxt .getInvokedFunctionArn ());
118185 functionVersion = cxt .getFunctionVersion ();
186+ } else {
187+ return spanBuilder ;
119188 }
120- Span span = GlobalTracer .get ().activeSpan ();
121- if (span != null ) {
122- span .setTag ("request_id" , requestId );
123- span .setTag ("service" , "aws.lambda" );
124- span .setTag ("function_arn" , functionArn );
125- span .setTag ("cold_start" , ColdStart .getColdStart (cxt ));
126- span .setTag ("datadog_lambda" , BuildConfig .datadog_lambda_version );
127- span .setTag ("resource_names" , functionName );
128- span .setTag ("function_version" , functionVersion );
189+ if (spanBuilder != null ) {
190+ spanBuilder .withTag ("request_id" , requestId );
191+ spanBuilder .withTag ("service" , "aws.lambda" );
192+ spanBuilder .withTag ("function_arn" , functionArn );
193+ spanBuilder .withTag ("cold_start" , ColdStart .getColdStart (cxt ));
194+ spanBuilder .withTag ("datadog_lambda" , BuildConfig .datadog_lambda_version );
195+ spanBuilder .withTag ("resource_names" , functionName );
196+ spanBuilder .withTag ("function_version" , functionVersion );
129197 }
198+ return spanBuilder ;
130199 }
131200
132201 protected String santitizeFunctionArn (String functionArn ){
@@ -169,6 +238,22 @@ protected boolean checkEnhanced() {
169238 return true ;
170239 }
171240
241+ /**
242+ * Check to see if the user has set DD_TRACE_ENABLED
243+ * @return true if DD_TRACE_ENABLED has been set to "true" (or "TRUE" or "tRuE" or ...), false otherwise
244+ */
245+ protected boolean checkTraceEnabled (){
246+ String sysTraceEnabled = System .getenv (TRACE_ENABLED_ENV );
247+ if (sysTraceEnabled == null ) {
248+ return false ;
249+ }
250+
251+ if (sysTraceEnabled .toLowerCase ().equals ("true" )){
252+ return true ;
253+ }
254+ return false ;
255+ }
256+
172257 /**
173258 * metric allows the user to record their own custom metric that will be sent to Datadog.
174259 *
0 commit comments