Skip to content

Commit 40f8c13

Browse files
Multi-tracing support for Couchbase 3.2+
1 parent 296ecfb commit 40f8c13

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/main/java/datadog/trace/instrumentation/couchbase_32/client/CoreEnvironmentBuilderInstrumentation.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package datadog.trace.instrumentation.couchbase_32.client;
22

3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
34
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
5+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
46

57
import com.google.auto.service.AutoService;
68
import datadog.trace.agent.tooling.Instrumenter;
@@ -23,6 +25,8 @@ public String[] helperClassNames() {
2325
packageName + ".DatadogRequestSpan",
2426
packageName + ".DatadogRequestSpan$1",
2527
packageName + ".DatadogRequestTracer",
28+
packageName + ".DelegatingRequestSpan",
29+
packageName + ".DelegatingRequestTracer"
2630
};
2731
}
2832

@@ -39,5 +43,8 @@ public String instrumentedType() {
3943
@Override
4044
public void methodAdvice(MethodTransformer transformer) {
4145
transformer.applyAdvice(isConstructor(), packageName + ".CoreEnvironmentBuilderAdvice");
46+
transformer.applyAdvice(
47+
isMethod().and(named("requestTracer")),
48+
packageName + ".CoreEnvironmentBuilderRequestTracerAdvice");
4249
}
4350
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package datadog.trace.instrumentation.couchbase_32.client;
2+
3+
import com.couchbase.client.core.Core;
4+
import com.couchbase.client.core.cnc.RequestTracer;
5+
import datadog.trace.bootstrap.ContextStore;
6+
import datadog.trace.bootstrap.InstrumentationContext;
7+
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
8+
import net.bytebuddy.asm.Advice;
9+
10+
public class CoreEnvironmentBuilderRequestTracerAdvice {
11+
@Advice.OnMethodEnter(suppress = Throwable.class)
12+
public static void onEnter(
13+
@Advice.Argument(value = 0, readOnly = false) RequestTracer requestTracer) {
14+
15+
// already a delegating tracer
16+
if (requestTracer instanceof DelegatingRequestTracer) {
17+
return;
18+
}
19+
20+
// already a datadog tracer
21+
if (requestTracer instanceof DatadogRequestTracer) {
22+
return;
23+
}
24+
25+
ContextStore<Core, String> coreContext = InstrumentationContext.get(Core.class, String.class);
26+
27+
DatadogRequestTracer datadogTracer = new DatadogRequestTracer(AgentTracer.get(), coreContext);
28+
29+
// if the app didn't set a custom tracer, use only datadog tracer
30+
if (requestTracer == null) {
31+
requestTracer = datadogTracer;
32+
return;
33+
}
34+
35+
// Wrap custom datadog and cnc tracers into a delegating
36+
requestTracer = new DelegatingRequestTracer(datadogTracer, requestTracer);
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package datadog.trace.instrumentation.couchbase_32.client;
2+
3+
import com.couchbase.client.core.cnc.RequestSpan;
4+
import com.couchbase.client.core.msg.RequestContext;
5+
import java.time.Instant;
6+
7+
/** RequestSpan, which delegates all calls to two other RequestSpans */
8+
public class DelegatingRequestSpan implements RequestSpan {
9+
10+
private final RequestSpan ddSpan;
11+
private final RequestSpan cncSpan;
12+
13+
public DelegatingRequestSpan(RequestSpan ddSpan, RequestSpan cncSpan) {
14+
this.ddSpan = ddSpan;
15+
this.cncSpan = cncSpan;
16+
}
17+
18+
public RequestSpan getDatadogSpan() {
19+
return ddSpan;
20+
}
21+
22+
public RequestSpan getCncSpan() {
23+
return cncSpan;
24+
}
25+
26+
@Override
27+
public void attribute(String key, String value) {
28+
// TODO: add null checks
29+
ddSpan.attribute(key, value);
30+
cncSpan.attribute(key, value);
31+
}
32+
33+
@Override
34+
public void attribute(String key, boolean value) {
35+
ddSpan.attribute(key, value);
36+
cncSpan.attribute(key, value);
37+
}
38+
39+
@Override
40+
public void attribute(String key, long value) {
41+
ddSpan.attribute(key, value);
42+
cncSpan.attribute(key, value);
43+
}
44+
45+
@Override
46+
public void event(String name, Instant timestamp) {
47+
ddSpan.event(name, timestamp);
48+
cncSpan.event(name, timestamp);
49+
}
50+
51+
@Override
52+
public void status(StatusCode status) {
53+
ddSpan.status(status);
54+
cncSpan.status(status);
55+
}
56+
57+
@Override
58+
public void end() {
59+
try {
60+
ddSpan.end();
61+
} finally {
62+
// guarantee cnc spans get ended even if ddSpan.end() throws exception
63+
cncSpan.end();
64+
}
65+
}
66+
67+
@Override
68+
public void requestContext(RequestContext requestContext) {
69+
ddSpan.requestContext(requestContext);
70+
cncSpan.requestContext(requestContext);
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package datadog.trace.instrumentation.couchbase_32.client;
2+
3+
import com.couchbase.client.core.cnc.RequestSpan;
4+
import com.couchbase.client.core.cnc.RequestTracer;
5+
import com.couchbase.client.core.cnc.tracing.NoopRequestSpan;
6+
import java.time.Duration;
7+
import reactor.core.publisher.Mono;
8+
9+
public class DelegatingRequestTracer implements RequestTracer {
10+
11+
private final DatadogRequestTracer ddTracer;
12+
private final RequestTracer cncTracer;
13+
14+
public DelegatingRequestTracer(DatadogRequestTracer ddTracer, RequestTracer cncTracer) {
15+
this.ddTracer = ddTracer;
16+
this.cncTracer = cncTracer;
17+
}
18+
19+
@Override
20+
public RequestSpan requestSpan(String name, RequestSpan parent) {
21+
RequestSpan ddParentSpan = unwrapDatadogParentSpan(parent);
22+
RequestSpan cncParentSpan = unwrapCncParentSpan(parent);
23+
24+
RequestSpan ddSpan = ddTracer != null ? ddTracer.requestSpan(name, ddParentSpan) : null;
25+
RequestSpan cncSpan = cncTracer != null ? cncTracer.requestSpan(name, cncParentSpan) : null;
26+
27+
// no tracers are present - return noop span
28+
if (ddSpan == null && cncSpan == null) {
29+
return NoopRequestSpan.INSTANCE;
30+
}
31+
32+
// only one tracer is present - no need to delegate
33+
if (ddSpan == null) {
34+
return cncSpan;
35+
}
36+
if (cncSpan == null) {
37+
return ddSpan;
38+
}
39+
40+
return new DelegatingRequestSpan(ddSpan, cncSpan);
41+
}
42+
43+
@Override
44+
public Mono<Void> start() {
45+
Mono<Void> primary = ddTracer != null ? ddTracer.start() : Mono.empty();
46+
Mono<Void> secondary = cncTracer != null ? cncTracer.start() : Mono.empty();
47+
return Mono.when(primary, secondary);
48+
}
49+
50+
@Override
51+
public Mono<Void> stop(Duration timeout) {
52+
Mono<Void> primary = ddTracer != null ? ddTracer.stop(timeout) : Mono.empty();
53+
Mono<Void> secondary = cncTracer != null ? cncTracer.stop(timeout) : Mono.empty();
54+
return Mono.when(primary, secondary);
55+
}
56+
57+
private static RequestSpan unwrapDatadogParentSpan(RequestSpan parent) {
58+
if (parent instanceof DelegatingRequestSpan) {
59+
return ((DelegatingRequestSpan) parent).getDatadogSpan();
60+
}
61+
return parent;
62+
}
63+
64+
private static RequestSpan unwrapCncParentSpan(RequestSpan parent) {
65+
if (parent instanceof DelegatingRequestSpan) {
66+
return ((DelegatingRequestSpan) parent).getCncSpan();
67+
}
68+
return parent;
69+
}
70+
}

0 commit comments

Comments
 (0)