-
Notifications
You must be signed in to change notification settings - Fork 350
[reference] eval: blind regeneration of datastax-cassandra-4.0 (toolkit output) #11996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
f8c2980
75b06bb
587b271
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| muzzle { | ||
| pass { | ||
| group = "com.datastax.oss" | ||
| module = "java-driver-core" | ||
| versions = "[4.0.0,)" | ||
| } | ||
| } | ||
|
|
||
| apply from: "$rootDir/gradle/java.gradle" | ||
|
|
||
| addTestSuiteForDir('latestDepTest', 'test') | ||
|
|
||
| dependencies { | ||
| compileOnly group: 'com.datastax.oss', name: 'java-driver-core', version: '4.0.0' | ||
|
|
||
| testImplementation group: 'com.datastax.oss', name: 'java-driver-core', version: '4.0.0' | ||
| testImplementation group: 'com.github.jbellis', name: 'jamm', version: '0.3.3' | ||
| testImplementation (group: 'org.testcontainers', name: 'cassandra', version: libs.versions.testcontainers.get()) { | ||
| exclude group: 'com.datastax.cassandra', module: 'cassandra-driver-core' | ||
| } | ||
|
|
||
| latestDepTestImplementation group: 'com.datastax.oss', name: 'java-driver-core', version: '4.+' | ||
| } | ||
|
|
||
| tasks.withType(Test).configureEach { | ||
| jvmArgs '-Dtestcontainers.ryuk.disabled=true' | ||
| environment 'TESTCONTAINERS_RYUK_DISABLED', 'true' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| package datadog.trace.instrumentation.cassandra4; | ||
|
|
||
| import com.datastax.oss.driver.api.core.CqlSession; | ||
| import com.datastax.oss.driver.api.core.cql.ExecutionInfo; | ||
| import com.datastax.oss.driver.api.core.cql.ResultSet; | ||
| import com.datastax.oss.driver.api.core.metadata.EndPoint; | ||
| import com.datastax.oss.driver.api.core.metadata.Node; | ||
| import datadog.trace.api.naming.SpanNaming; | ||
| import datadog.trace.api.normalize.SQLNormalizer; | ||
| import datadog.trace.bootstrap.instrumentation.api.AgentSpan; | ||
| import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; | ||
| import datadog.trace.bootstrap.instrumentation.api.Tags; | ||
| import datadog.trace.bootstrap.instrumentation.decorator.DBTypeProcessingDatabaseClientDecorator; | ||
| import datadog.trace.bootstrap.instrumentation.jdbc.DBQueryInfo; | ||
| import java.net.InetSocketAddress; | ||
| import java.net.SocketAddress; | ||
|
|
||
| public class CassandraClientDecorator extends DBTypeProcessingDatabaseClientDecorator<CqlSession> { | ||
| private static final String DB_TYPE = "cassandra"; | ||
| private static final String SERVICE_NAME = | ||
| SpanNaming.instance().namingSchema().database().service(DB_TYPE); | ||
| public static final String OPERATION_NAME = | ||
| SpanNaming.instance().namingSchema().database().operation(DB_TYPE); | ||
| public static final String JAVA_CASSANDRA = "java-cassandra"; | ||
|
|
||
| public static final CassandraClientDecorator DECORATE = new CassandraClientDecorator(); | ||
|
|
||
| @Override | ||
| protected String[] instrumentationNames() { | ||
| return new String[] {"cassandra"}; | ||
| } | ||
|
|
||
| @Override | ||
| protected String service() { | ||
| return SERVICE_NAME; | ||
| } | ||
|
|
||
| @Override | ||
| protected CharSequence component() { | ||
| return JAVA_CASSANDRA; | ||
| } | ||
|
|
||
| @Override | ||
| protected CharSequence spanType() { | ||
| return InternalSpanTypes.CASSANDRA; | ||
| } | ||
|
|
||
| @Override | ||
| protected String dbType() { | ||
| return DB_TYPE; | ||
| } | ||
|
|
||
| @Override | ||
| protected String dbUser(final CqlSession session) { | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| protected String dbInstance(final CqlSession session) { | ||
| return session.getKeyspace().map(k -> k.asCql(false)).orElse(null); | ||
| } | ||
|
|
||
| @Override | ||
| protected String dbHostname(final CqlSession session) { | ||
| return ContactPointsUtil.getFirstHost(session); | ||
| } | ||
|
|
||
| public void onStatement(final AgentSpan span, final CharSequence statement) { | ||
| span.setResourceName(SQLNormalizer.normalize(statement.toString()).toString()); | ||
| final CharSequence operation = DBQueryInfo.extractOperation(statement); | ||
| if (operation != null) { | ||
| span.setTag(Tags.DB_OPERATION, operation); | ||
| } | ||
| } | ||
|
|
||
| public void onResponse(final AgentSpan span, final ResultSet result) { | ||
| if (result != null) { | ||
| final ExecutionInfo executionInfo = result.getExecutionInfo(); | ||
|
Comment on lines
+76
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with πΒ / π. |
||
| if (executionInfo != null) { | ||
| final Node coordinator = executionInfo.getCoordinator(); | ||
| if (coordinator != null) { | ||
| final EndPoint endPoint = coordinator.getEndPoint(); | ||
| if (endPoint != null) { | ||
| final SocketAddress socketAddress = endPoint.resolve(); | ||
| if (socketAddress instanceof InetSocketAddress) { | ||
| onPeerConnection(span, (InetSocketAddress) socketAddress); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package datadog.trace.instrumentation.cassandra4; | ||
|
|
||
| import com.google.auto.service.AutoService; | ||
| import datadog.trace.agent.tooling.Instrumenter; | ||
| import datadog.trace.agent.tooling.InstrumenterModule; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| @AutoService(InstrumenterModule.class) | ||
| public class CassandraClientModule extends InstrumenterModule.Tracing { | ||
|
|
||
| public CassandraClientModule() { | ||
| super("cassandra"); | ||
| } | ||
|
|
||
| @Override | ||
| public String[] helperClassNames() { | ||
| return new String[] { | ||
| packageName + ".CassandraClientDecorator", | ||
| packageName + ".CassandraDBMUtil", | ||
| packageName + ".ContactPointsUtil", | ||
| packageName + ".SpanFinishingCallback", | ||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public List<Instrumenter> typeInstrumentations() { | ||
| return Collections.singletonList(new CqlSessionExecuteInstrumentation()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package datadog.trace.instrumentation.cassandra4; | ||
|
|
||
| import static datadog.trace.api.Config.DBM_PROPAGATION_MODE_FULL; | ||
| import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DBM_TRACE_INJECTED; | ||
|
|
||
| import datadog.trace.api.Config; | ||
| import datadog.trace.api.propagation.W3CTraceParent; | ||
| import datadog.trace.bootstrap.instrumentation.api.AgentSpan; | ||
| import datadog.trace.bootstrap.instrumentation.dbm.SharedDBCommenter; | ||
|
|
||
| /** | ||
| * Utility class for Cassandra Database Monitoring (DBM) comment injection. When DBM propagation is | ||
| * enabled, this injects trace context as a CQL comment prepended to the query string so that the | ||
| * Datadog database agent can correlate queries back to traces. | ||
| */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we avoid redundant comments? |
||
| public final class CassandraDBMUtil { | ||
|
|
||
| private CassandraDBMUtil() {} | ||
|
|
||
| /** | ||
| * Injects a DBM trace comment into the CQL query string if DBM propagation is enabled. Sets the | ||
| * {@code _dd.dbm_trace_injected} tag on the span when injection occurs. | ||
| * | ||
| * @param span the current agent span | ||
| * @param query the original CQL query string | ||
| * @param hostname the database host (may be null) | ||
| * @param dbName the database/keyspace name (may be null) | ||
| * @return the query string with DBM comment prepended, or the original query if DBM is disabled | ||
| */ | ||
| public static String injectComment(AgentSpan span, String query, String hostname, String dbName) { | ||
| if (!Config.get().isDbmCommentInjectionEnabled()) { | ||
| return query; | ||
| } | ||
|
|
||
| if (query == null || query.isEmpty()) { | ||
| return query; | ||
| } | ||
|
|
||
| if (span.forceSamplingDecision() == null) { | ||
| return query; | ||
| } | ||
|
|
||
| String dbService = span.getServiceName(); | ||
| String traceParent = | ||
| Config.get().getDbmPropagationMode().equals(DBM_PROPAGATION_MODE_FULL) | ||
| ? W3CTraceParent.from(span) | ||
| : null; | ||
|
|
||
| String commentContent = | ||
| SharedDBCommenter.buildComment(dbService, "cassandra", hostname, dbName, traceParent); | ||
| if (commentContent == null || commentContent.isEmpty()) { | ||
| return query; | ||
| } | ||
|
|
||
| // Check for duplicate injection | ||
| if (SharedDBCommenter.containsTraceComment(query)) { | ||
| return query; | ||
| } | ||
|
|
||
| span.setTag(DBM_TRACE_INJECTED, true); | ||
|
|
||
| // Prepend the DBM comment as a CQL comment | ||
| return "/* " + commentContent + " */ " + query; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| package datadog.trace.instrumentation.cassandra4; | ||
|
|
||
| import com.datastax.oss.driver.api.core.CqlIdentifier; | ||
| import com.datastax.oss.driver.api.core.CqlSession; | ||
| import com.datastax.oss.driver.api.core.cql.BoundStatement; | ||
| import com.datastax.oss.driver.api.core.cql.Statement; | ||
| import com.datastax.oss.driver.api.core.metadata.EndPoint; | ||
| import com.datastax.oss.driver.api.core.metadata.Node; | ||
| import java.net.InetSocketAddress; | ||
| import java.net.SocketAddress; | ||
| import java.util.Collection; | ||
| import java.util.Optional; | ||
|
|
||
| public final class ContactPointsUtil { | ||
|
|
||
| private ContactPointsUtil() {} | ||
|
|
||
| public static String getContactPoints(final CqlSession session) { | ||
| try { | ||
| Collection<Node> nodes = session.getMetadata().getNodes().values(); | ||
| if (nodes.isEmpty()) { | ||
| return null; | ||
| } | ||
| StringBuilder sb = new StringBuilder(); | ||
| for (Node node : nodes) { | ||
| if (sb.length() > 0) { | ||
| sb.append(","); | ||
| } | ||
| EndPoint endPoint = node.getEndPoint(); | ||
| if (endPoint != null) { | ||
| SocketAddress socketAddress = endPoint.resolve(); | ||
| if (socketAddress instanceof InetSocketAddress) { | ||
| InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; | ||
| sb.append(inetSocketAddress.getHostString()) | ||
| .append(":") | ||
| .append(inetSocketAddress.getPort()); | ||
| } | ||
| } | ||
| } | ||
| return sb.length() > 0 ? sb.toString() : null; | ||
| } catch (Throwable ignored) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| public static String getFirstHost(final CqlSession session) { | ||
| try { | ||
| Collection<Node> nodes = session.getMetadata().getNodes().values(); | ||
| for (Node node : nodes) { | ||
| EndPoint endPoint = node.getEndPoint(); | ||
| if (endPoint != null) { | ||
| SocketAddress socketAddress = endPoint.resolve(); | ||
| if (socketAddress instanceof InetSocketAddress) { | ||
| return ((InetSocketAddress) socketAddress).getHostString(); | ||
| } | ||
| } | ||
| } | ||
| } catch (Throwable ignored) { | ||
| // Connection metadata may not be available | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| public static String getKeyspace(final CqlSession session) { | ||
| try { | ||
| Optional<CqlIdentifier> keyspace = session.getKeyspace(); | ||
| if (keyspace.isPresent()) { | ||
| return keyspace.get().asCql(false); | ||
| } | ||
| } catch (Throwable ignored) { | ||
| // Keyspace may not be available | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| public static String getQuery(final Statement<?> statement) { | ||
| String query = null; | ||
| if (statement instanceof com.datastax.oss.driver.api.core.cql.SimpleStatement) { | ||
| query = ((com.datastax.oss.driver.api.core.cql.SimpleStatement) statement).getQuery(); | ||
| } else if (statement instanceof BoundStatement) { | ||
| query = ((BoundStatement) statement).getPreparedStatement().getQuery(); | ||
| } | ||
| return query == null ? "" : query; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sessions built with a keyspace,
asCql(false)always renders the identifier in double-quoted CQL form, so a normal keyspace likepeer_testis tagged as"peer_test". That changesdb.instanceand any peer-service or split-by-instance naming derived from it compared with the existing Cassandra instrumentation, so use the internal/unquoted keyspace representation here.Useful? React with πΒ / π.