Skip to content

Commit ad3a5f5

Browse files
authored
Fix distributed tracing (#28)
* Create span and set parent manually * Close the scope * Set trace and parent IDs correctly * Skipped 0.0.10 by accident * improve language around default value of DD_TRACE_ENABLED
1 parent 358b380 commit ad3a5f5

File tree

3 files changed

+102
-17
lines changed

3 files changed

+102
-17
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ sourceCompatibility = 1.8
4646
targetCompatibility = 1.8
4747

4848
group = 'com.datadoghq'
49-
version= '0.0.9'
49+
version= '0.0.10'
5050

5151
allprojects {
5252
repositories {

src/main/java/com/datadoghq/datadog_lambda_java/DDLambda.java

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package 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;
39
import java.io.IOException;
410
import java.net.URL;
511
import 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
*

src/main/java/com/datadoghq/datadog_lambda_java/Tracing.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
1212
import com.google.gson.Gson;
1313

14-
public class Tracing {
14+
public class Tracing{
1515

1616
protected DDTraceContext cxt;
1717
protected XRayTraceContext xrt;

0 commit comments

Comments
 (0)