Skip to content

Commit 1a7e0f3

Browse files
SimoneGiussotrask
andauthored
Instrumenting cassandra executeReactive method (#6441)
It follows the [issue](#6395 (comment)) I opened some days ago. The `executeReactive` method use the same processor used by `executeAsync` (see [here](https://github.com/datastax/java-driver/blob/65d2c19c401175dcc6c370560dd5f783d05b05b9/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/CqlRequestReactiveProcessor.java#L53)) and wrap the callback in the `DefaultReactiveResultSet` publisher. Here I'm simply overriding the `executeReactive` method doing the same thing: call the already instrumented `executeAsync` method and wrapping the callback using the `DefaultReactiveResultSet` publisher. ~~I did an upgrade of the `java-driver-core` library to have `TracingCqlSession.java` extending the `ReactiveSession`. I have to probably rename the `cassandra-4.0` module in `cassandra-4.14` but I'll let you confirm this.~~ -> Cassandra-4.4 is enough. --------- Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 583e2a7 commit 1a7e0f3

File tree

25 files changed

+1095
-77
lines changed

25 files changed

+1095
-77
lines changed

docs/supported-libraries.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ These are the supported libraries and frameworks:
4242
| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | [opentelemetry-aws-lambda-core-1.0](../instrumentation/aws-lambda/aws-lambda-core-1.0/library),<br>[opentelemetry-aws-lambda-events-2.2](../instrumentation/aws-lambda/aws-lambda-events-2.2/library) | [FaaS Server Spans] |
4343
| [AWS SDK](https://aws.amazon.com/sdk-for-java/) | 1.11.x and 2.2.0+ | [opentelemetry-aws-sdk-1.11](../instrumentation/aws-sdk/aws-sdk-1.11/library),<br>[opentelemetry-aws-sdk-1.11-autoconfigure](../instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure),<br>[opentelemetry-aws-sdk-2.2](../instrumentation/aws-sdk/aws-sdk-2.2/library),<br>[opentelemetry-aws-sdk-2.2-autoconfigure](../instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure) | [Messaging Spans], [Database Client Spans], [HTTP Client Spans] |
4444
| [Azure Core](https://docs.microsoft.com/en-us/java/api/overview/azure/core-readme) | 1.14+ | N/A | Context propagation |
45-
| [Cassandra Driver](https://github.com/datastax/java-driver) | 3.0+ | N/A | [Database Client Spans] |
45+
| [Cassandra Driver](https://github.com/datastax/java-driver) | 3.0+ | [opentelemetry-cassandra-4.4](../instrumentation/cassandra/cassandra-4.4/library) | [Database Client Spans] |
4646
| [Couchbase Client](https://github.com/couchbase/couchbase-java-client) | 2.0+ and 3.1+ | N/A | [Database Client Spans] |
4747
| [c3p0](https://github.com/swaldman/c3p0) | 0.9.2+ | [opentelemetry-c3p0-0.9](../instrumentation/c3p0-0.9/library) | [Database Pool Metrics] |
4848
| [Dropwizard Metrics](https://metrics.dropwizard.io/) | 4.0+ (disabled by default) | N/A | none |

instrumentation/cassandra/cassandra-3.0/javaagent/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ dependencies {
3535
testInstrumentation(project(":instrumentation:guava-10.0:javaagent"))
3636

3737
latestDepTestLibrary("com.datastax.cassandra:cassandra-driver-core:3.+") // see cassandra-4.0 module
38+
39+
testInstrumentation(project(":instrumentation:cassandra:cassandra-4.0:javaagent"))
40+
testInstrumentation(project(":instrumentation:cassandra:cassandra-4.4:javaagent"))
3841
}
3942

4043
// Requires old Guava. Can't use enforcedPlatform since predates BOM
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
}
4+
5+
dependencies {
6+
api(project(":testing-common"))
7+
8+
implementation("org.testcontainers:testcontainers:1.17.5")
9+
implementation("com.datastax.oss:java-driver-core:4.0.0")
10+
}

instrumentation/cassandra/cassandra-4.0/javaagent/src/test/java/CassandraClientTest.java renamed to instrumentation/cassandra/cassandra-4-common/testing/src/main/java/io/opentelemetry/cassandra/v4/common/AbstractCassandraTest.java

Lines changed: 88 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
package io.opentelemetry.cassandra.v4.common;
7+
68
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
79
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
810
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_CASSANDRA_CONSISTENCY_LEVEL;
@@ -26,14 +28,12 @@
2628
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
2729
import com.datastax.oss.driver.internal.core.config.typesafe.DefaultDriverConfigLoader;
2830
import io.opentelemetry.api.trace.SpanKind;
29-
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
3031
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
3132
import java.net.InetSocketAddress;
3233
import java.time.Duration;
3334
import java.util.stream.Stream;
3435
import org.junit.jupiter.api.AfterAll;
3536
import org.junit.jupiter.api.BeforeAll;
36-
import org.junit.jupiter.api.extension.RegisterExtension;
3737
import org.junit.jupiter.params.ParameterizedTest;
3838
import org.junit.jupiter.params.provider.Arguments;
3939
import org.junit.jupiter.params.provider.MethodSource;
@@ -42,17 +42,20 @@
4242
import org.testcontainers.containers.GenericContainer;
4343
import org.testcontainers.containers.output.Slf4jLogConsumer;
4444

45-
public class CassandraClientTest {
46-
47-
private static final Logger logger = LoggerFactory.getLogger(CassandraClientTest.class);
45+
public abstract class AbstractCassandraTest {
4846

49-
@RegisterExtension
50-
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
47+
private static final Logger logger = LoggerFactory.getLogger(AbstractCassandraTest.class);
5148

5249
@SuppressWarnings("rawtypes")
5350
private static GenericContainer cassandra;
5451

55-
private static int cassandraPort;
52+
protected static int cassandraPort;
53+
54+
protected abstract InstrumentationExtension testing();
55+
56+
protected CqlSession wrap(CqlSession session) {
57+
return session;
58+
}
5659

5760
@BeforeAll
5861
static void beforeAll() {
@@ -79,30 +82,33 @@ void syncTest(Parameter parameter) {
7982

8083
session.execute(parameter.statement);
8184

82-
testing.waitAndAssertTraces(
83-
trace ->
84-
trace.hasSpansSatisfyingExactly(
85-
span ->
86-
span.hasName(parameter.spanName)
87-
.hasKind(SpanKind.CLIENT)
88-
.hasNoParent()
89-
.hasAttributesSatisfyingExactly(
90-
equalTo(NET_SOCK_PEER_ADDR, "127.0.0.1"),
91-
equalTo(NET_SOCK_PEER_NAME, "localhost"),
92-
equalTo(NET_SOCK_PEER_PORT, cassandraPort),
93-
equalTo(DB_SYSTEM, "cassandra"),
94-
equalTo(DB_NAME, parameter.keyspace),
95-
equalTo(DB_STATEMENT, parameter.expectedStatement),
96-
equalTo(DB_OPERATION, parameter.operation),
97-
equalTo(DB_CASSANDRA_CONSISTENCY_LEVEL, "LOCAL_ONE"),
98-
equalTo(DB_CASSANDRA_COORDINATOR_DC, "datacenter1"),
99-
satisfies(
100-
DB_CASSANDRA_COORDINATOR_ID, val -> val.isInstanceOf(String.class)),
101-
satisfies(
102-
DB_CASSANDRA_IDEMPOTENCE, val -> val.isInstanceOf(Boolean.class)),
103-
equalTo(DB_CASSANDRA_PAGE_SIZE, 5000),
104-
equalTo(DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT, 0),
105-
equalTo(DB_CASSANDRA_TABLE, parameter.table))));
85+
testing()
86+
.waitAndAssertTraces(
87+
trace ->
88+
trace.hasSpansSatisfyingExactly(
89+
span ->
90+
span.hasName(parameter.spanName)
91+
.hasKind(SpanKind.CLIENT)
92+
.hasNoParent()
93+
.hasAttributesSatisfyingExactly(
94+
equalTo(NET_SOCK_PEER_ADDR, "127.0.0.1"),
95+
equalTo(NET_SOCK_PEER_NAME, "localhost"),
96+
equalTo(NET_SOCK_PEER_PORT, cassandraPort),
97+
equalTo(DB_SYSTEM, "cassandra"),
98+
equalTo(DB_NAME, parameter.keyspace),
99+
equalTo(DB_STATEMENT, parameter.expectedStatement),
100+
equalTo(DB_OPERATION, parameter.operation),
101+
equalTo(DB_CASSANDRA_CONSISTENCY_LEVEL, "LOCAL_ONE"),
102+
equalTo(DB_CASSANDRA_COORDINATOR_DC, "datacenter1"),
103+
satisfies(
104+
DB_CASSANDRA_COORDINATOR_ID,
105+
val -> val.isInstanceOf(String.class)),
106+
satisfies(
107+
DB_CASSANDRA_IDEMPOTENCE,
108+
val -> val.isInstanceOf(Boolean.class)),
109+
equalTo(DB_CASSANDRA_PAGE_SIZE, 5000),
110+
equalTo(DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT, 0),
111+
equalTo(DB_CASSANDRA_TABLE, parameter.table))));
106112

107113
session.close();
108114
}
@@ -112,42 +118,48 @@ void syncTest(Parameter parameter) {
112118
void asyncTest(Parameter parameter) throws Exception {
113119
CqlSession session = getSession(parameter.keyspace);
114120

115-
testing.runWithSpan(
116-
"parent",
117-
() ->
118-
session
119-
.executeAsync(parameter.statement)
120-
.toCompletableFuture()
121-
.whenComplete((result, throwable) -> testing.runWithSpan("child", () -> {}))
122-
.get());
121+
testing()
122+
.runWithSpan(
123+
"parent",
124+
() ->
125+
session
126+
.executeAsync(parameter.statement)
127+
.toCompletableFuture()
128+
.whenComplete((result, throwable) -> testing().runWithSpan("child", () -> {}))
129+
.get());
123130

124-
testing.waitAndAssertTraces(
125-
trace ->
126-
trace.hasSpansSatisfyingExactly(
127-
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
128-
span ->
129-
span.hasName(parameter.spanName)
130-
.hasKind(SpanKind.CLIENT)
131-
.hasParent(trace.getSpan(0))
132-
.hasAttributesSatisfyingExactly(
133-
equalTo(NET_SOCK_PEER_ADDR, "127.0.0.1"),
134-
equalTo(NET_SOCK_PEER_NAME, "localhost"),
135-
equalTo(NET_SOCK_PEER_PORT, cassandraPort),
136-
equalTo(DB_SYSTEM, "cassandra"),
137-
equalTo(DB_NAME, parameter.keyspace),
138-
equalTo(DB_STATEMENT, parameter.expectedStatement),
139-
equalTo(DB_OPERATION, parameter.operation),
140-
equalTo(DB_CASSANDRA_CONSISTENCY_LEVEL, "LOCAL_ONE"),
141-
equalTo(DB_CASSANDRA_COORDINATOR_DC, "datacenter1"),
142-
satisfies(
143-
DB_CASSANDRA_COORDINATOR_ID, val -> val.isInstanceOf(String.class)),
144-
satisfies(
145-
DB_CASSANDRA_IDEMPOTENCE, val -> val.isInstanceOf(Boolean.class)),
146-
equalTo(DB_CASSANDRA_PAGE_SIZE, 5000),
147-
equalTo(DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT, 0),
148-
equalTo(DB_CASSANDRA_TABLE, parameter.table)),
149-
span ->
150-
span.hasName("child").hasKind(SpanKind.INTERNAL).hasParent(trace.getSpan(0))));
131+
testing()
132+
.waitAndAssertTraces(
133+
trace ->
134+
trace.hasSpansSatisfyingExactly(
135+
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
136+
span ->
137+
span.hasName(parameter.spanName)
138+
.hasKind(SpanKind.CLIENT)
139+
.hasParent(trace.getSpan(0))
140+
.hasAttributesSatisfyingExactly(
141+
equalTo(NET_SOCK_PEER_ADDR, "127.0.0.1"),
142+
equalTo(NET_SOCK_PEER_NAME, "localhost"),
143+
equalTo(NET_SOCK_PEER_PORT, cassandraPort),
144+
equalTo(DB_SYSTEM, "cassandra"),
145+
equalTo(DB_NAME, parameter.keyspace),
146+
equalTo(DB_STATEMENT, parameter.expectedStatement),
147+
equalTo(DB_OPERATION, parameter.operation),
148+
equalTo(DB_CASSANDRA_CONSISTENCY_LEVEL, "LOCAL_ONE"),
149+
equalTo(DB_CASSANDRA_COORDINATOR_DC, "datacenter1"),
150+
satisfies(
151+
DB_CASSANDRA_COORDINATOR_ID,
152+
val -> val.isInstanceOf(String.class)),
153+
satisfies(
154+
DB_CASSANDRA_IDEMPOTENCE,
155+
val -> val.isInstanceOf(Boolean.class)),
156+
equalTo(DB_CASSANDRA_PAGE_SIZE, 5000),
157+
equalTo(DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT, 0),
158+
equalTo(DB_CASSANDRA_TABLE, parameter.table)),
159+
span ->
160+
span.hasName("child")
161+
.hasKind(SpanKind.INTERNAL)
162+
.hasParent(trace.getSpan(0))));
151163

152164
session.close();
153165
}
@@ -260,7 +272,7 @@ private static Stream<Arguments> provideAsyncParameters() {
260272
"users"))));
261273
}
262274

263-
private static class Parameter {
275+
protected static class Parameter {
264276
public final String keyspace;
265277
public final String statement;
266278
public final String expectedStatement;
@@ -284,16 +296,17 @@ public Parameter(
284296
}
285297
}
286298

287-
CqlSession getSession(String keyspace) {
299+
protected CqlSession getSession(String keyspace) {
288300
DriverConfigLoader configLoader =
289301
DefaultDriverConfigLoader.builder()
290302
.withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofSeconds(0))
291303
.build();
292-
return CqlSession.builder()
293-
.addContactPoint(new InetSocketAddress("localhost", cassandraPort))
294-
.withConfigLoader(configLoader)
295-
.withLocalDatacenter("datacenter1")
296-
.withKeyspace(keyspace)
297-
.build();
304+
return wrap(
305+
CqlSession.builder()
306+
.addContactPoint(new InetSocketAddress("localhost", cassandraPort))
307+
.withConfigLoader(configLoader)
308+
.withLocalDatacenter("datacenter1")
309+
.withKeyspace(keyspace)
310+
.build());
298311
}
299312
}

instrumentation/cassandra/cassandra-4.0/javaagent/build.gradle.kts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ muzzle {
66
pass {
77
group.set("com.datastax.oss")
88
module.set("java-driver-core")
9-
versions.set("[4.0,)")
9+
versions.set("[4.0,4.4)")
1010
assertInverse.set(true)
1111
}
1212
}
@@ -16,6 +16,11 @@ dependencies {
1616

1717
compileOnly("com.google.auto.value:auto-value-annotations")
1818
annotationProcessor("com.google.auto.value:auto-value")
19+
20+
testImplementation(project(":instrumentation:cassandra:cassandra-4-common:testing"))
21+
22+
testInstrumentation(project(":instrumentation:cassandra:cassandra-3.0:javaagent"))
23+
testInstrumentation(project(":instrumentation:cassandra:cassandra-4.4:javaagent"))
1924
}
2025

2126
tasks {

instrumentation/cassandra/cassandra-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/cassandra/v4_0/CassandraClientInstrumentationModule.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@
55

66
package io.opentelemetry.javaagent.instrumentation.cassandra.v4_0;
77

8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
89
import static java.util.Collections.singletonList;
10+
import static net.bytebuddy.matcher.ElementMatchers.not;
911

1012
import com.google.auto.service.AutoService;
1113
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
1214
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
1315
import java.util.List;
16+
import net.bytebuddy.matcher.ElementMatcher;
1417

1518
@AutoService(InstrumentationModule.class)
1619
public class CassandraClientInstrumentationModule extends InstrumentationModule {
20+
1721
public CassandraClientInstrumentationModule() {
1822
super("cassandra", "cassandra-4.0");
1923
}
@@ -22,4 +26,10 @@ public CassandraClientInstrumentationModule() {
2226
public List<TypeInstrumentation> typeInstrumentations() {
2327
return singletonList(new SessionBuilderInstrumentation());
2428
}
29+
30+
@Override
31+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
32+
// new public interface introduced in version 4.4
33+
return not(hasClassesNamed("com.datastax.dse.driver.api.core.cql.reactive.ReactiveSession"));
34+
}
2535
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import io.opentelemetry.cassandra.v4.common.AbstractCassandraTest;
7+
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
8+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
9+
import org.junit.jupiter.api.extension.RegisterExtension;
10+
11+
public class CassandraTest extends AbstractCassandraTest {
12+
13+
@RegisterExtension
14+
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
15+
16+
@Override
17+
protected InstrumentationExtension testing() {
18+
return testing;
19+
}
20+
}

0 commit comments

Comments
 (0)