diff --git a/driver-opentelemetry/pom.xml b/driver-opentelemetry/pom.xml
new file mode 100644
index 00000000000..6c59be7b9ad
--- /dev/null
+++ b/driver-opentelemetry/pom.xml
@@ -0,0 +1,149 @@
+
+
+
+
+ 4.0.0
+
+
+ com.scylladb
+ scylla-driver-parent
+ 3.11.2.1-SNAPSHOT
+
+
+ scylla-driver-opentelemetry
+ Java Driver for Scylla and Apache Cassandra - OpenTelemetry integration
+ An extension of Java Driver for Scylla and Apache Cassandra by adding
+ functionality of creating traces and spans in OpenTelemetry format.
+
+
+
+
+
+
+
+ io.opentelemetry
+ opentelemetry-bom
+ 1.9.1
+ pom
+ import
+
+
+
+
+
+
+
+
+
+
+
+ com.scylladb
+ scylla-driver-core
+
+
+
+
+
+ io.opentelemetry
+ opentelemetry-api
+ 1.9.1
+
+
+
+
+
+ com.scylladb
+ scylla-driver-core
+ test-jar
+ test
+
+
+
+ io.opentelemetry
+ opentelemetry-sdk
+ test
+
+
+
+ org.apache.commons
+ commons-exec
+ test
+
+
+
+ org.testng
+ testng
+ test
+
+
+
+ org.slf4j
+ slf4j-log4j12
+ test
+
+
+
+
+
+
+
+
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+
+
+ org.codehaus.mojo
+ animal-sniffer-maven-plugin
+ 1.15
+
+
+ check-jdk6
+ process-classes
+
+ check
+
+
+ true
+
+
+
+ check-jdk8
+
+ check
+
+
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/driver-opentelemetry/src/main/java/com/datastax/driver/opentelemetry/OpenTelemetryTracingInfo.java b/driver-opentelemetry/src/main/java/com/datastax/driver/opentelemetry/OpenTelemetryTracingInfo.java
new file mode 100644
index 00000000000..2c93ab4949e
--- /dev/null
+++ b/driver-opentelemetry/src/main/java/com/datastax/driver/opentelemetry/OpenTelemetryTracingInfo.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2021 ScyllaDB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.datastax.driver.opentelemetry;
+
+import com.datastax.driver.core.ConsistencyLevel;
+import com.datastax.driver.core.policies.LoadBalancingPolicy;
+import com.datastax.driver.core.policies.RetryPolicy;
+import com.datastax.driver.core.policies.SpeculativeExecutionPolicy;
+import com.datastax.driver.core.tracing.PrecisionLevel;
+import com.datastax.driver.core.tracing.TracingInfo;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.context.Context;
+import java.net.InetAddress;
+
+public class OpenTelemetryTracingInfo implements TracingInfo {
+ private Span span;
+ private final Tracer tracer;
+ private final Context context;
+ private boolean tracingStarted;
+ private final PrecisionLevel precision;
+
+ protected OpenTelemetryTracingInfo(Tracer tracer, Context context, PrecisionLevel precision) {
+ this.tracer = tracer;
+ this.context = context;
+ this.precision = precision;
+ this.tracingStarted = false;
+ }
+
+ public Tracer getTracer() {
+ return tracer;
+ }
+
+ public Context getContext() {
+ return context.with(span);
+ }
+
+ private void assertStarted() {
+ assert tracingStarted : "TracingInfo.setStartTime must be called before any other method";
+ }
+
+ public PrecisionLevel getPrecision() {
+ return precision;
+ }
+
+ @Override
+ public void setNameAndStartTime(String name) {
+ assert !tracingStarted : "TracingInfo.setStartTime may only be called once.";
+ tracingStarted = true;
+ span = tracer.spanBuilder(name).setParent(context).startSpan();
+ }
+
+ @Override
+ public void setConsistencyLevel(ConsistencyLevel consistency) {
+ assertStarted();
+ span.setAttribute("db.scylla.consistency_level", consistency.toString());
+ }
+
+ public void setStatement(String statement) {
+ assertStarted();
+ if (currentPrecisionLevelIsAtLeast(PrecisionLevel.FULL)) {
+ span.setAttribute("db.scylla.statement", statement);
+ }
+ }
+
+ public void setHostname(String hostname) {
+ assertStarted();
+ if (currentPrecisionLevelIsAtLeast(PrecisionLevel.FULL)) {
+ span.setAttribute("net.peer.name", hostname);
+ }
+ }
+
+ @Override
+ public void setStatementType(String statementType) {
+ assertStarted();
+ span.setAttribute("db.scylla.statement_type", statementType);
+ }
+
+ @Override
+ public void setRetryPolicy(RetryPolicy retryPolicy) {
+ assertStarted();
+ span.setAttribute("db.scylla.retry_policy", retryPolicy.getClass().getSimpleName());
+ }
+
+ @Override
+ public void setLoadBalancingPolicy(LoadBalancingPolicy loadBalancingPolicy) {
+ assertStarted();
+ span.setAttribute(
+ "db.scylla.load_balancing_policy", loadBalancingPolicy.getClass().getSimpleName());
+ }
+
+ @Override
+ public void setSpeculativeExecutionPolicy(SpeculativeExecutionPolicy speculativeExecutionPolicy) {
+ assertStarted();
+ span.setAttribute(
+ "db.scylla.speculative_execution_policy",
+ speculativeExecutionPolicy.getClass().getSimpleName());
+ }
+
+ @Override
+ public void setBatchSize(int batchSize) {
+ assertStarted();
+ span.setAttribute("db.scylla.batch_size", String.valueOf(batchSize));
+ }
+
+ @Override
+ public void setAttemptCount(int attemptCount) {
+ assertStarted();
+ span.setAttribute("db.scylla.attempt_count", String.valueOf(attemptCount));
+ }
+
+ @Override
+ public void setShardID(int shardID) {
+ assertStarted();
+ span.setAttribute("db.scylla.shard_id", String.valueOf(shardID));
+ }
+
+ @Override
+ public void setPeerName(String peerName) {
+ assertStarted();
+ span.setAttribute("net.peer.name", peerName);
+ }
+
+ @Override
+ public void setPeerIP(InetAddress peerIP) {
+ assertStarted();
+ span.setAttribute("net.peer.ip", peerIP.getHostAddress());
+ }
+
+ @Override
+ public void setPeerPort(int peerPort) {
+ assertStarted();
+ span.setAttribute("net.peer.port", String.valueOf(peerPort));
+ }
+
+ @Override
+ public void setFetchSize(int fetchSize) {
+ assertStarted();
+ span.setAttribute("db.scylla.fetch_size", Integer.toString(fetchSize));
+ }
+
+ @Override
+ public void setHasMorePages(boolean hasMorePages) {
+ assertStarted();
+ span.setAttribute("db.scylla.has_more_pages", Boolean.toString(hasMorePages));
+ }
+
+ @Override
+ public void setRowsCount(int rowsCount) {
+ assertStarted();
+ span.setAttribute("db.scylla.rows_count", Integer.toString(rowsCount));
+ }
+
+ @Override
+ public void setStatement(String statement, int limit) {
+ assertStarted();
+ if (currentPrecisionLevelIsAtLeast(PrecisionLevel.FULL)) {
+ if (statement.length() > limit) statement = statement.substring(0, limit);
+ span.setAttribute("db.scylla.statement", statement);
+ }
+ }
+
+ @Override
+ public void setKeyspace(String keyspace) {
+ assertStarted();
+ span.setAttribute("db.scylla.keyspace", keyspace);
+ }
+
+ @Override
+ public void setPartitionKey(String partitionKey) {
+ assertStarted();
+ span.setAttribute("db.scylla.partition_key", partitionKey);
+ }
+
+ @Override
+ public void setTable(String table) {
+ assertStarted();
+ span.setAttribute("db.scylla.table", table);
+ }
+
+ @Override
+ public void setOperationType(String operationType) {
+ assertStarted();
+ span.setAttribute("db.operation", operationType);
+ }
+
+ @Override
+ public void setReplicas(String replicas) {
+ assertStarted();
+ span.setAttribute("db.scylla.replicas", replicas);
+ }
+
+ private io.opentelemetry.api.trace.StatusCode mapStatusCode(StatusCode code) {
+ switch (code) {
+ case OK:
+ return io.opentelemetry.api.trace.StatusCode.OK;
+ case ERROR:
+ return io.opentelemetry.api.trace.StatusCode.ERROR;
+ }
+ return null;
+ }
+
+ @Override
+ public void recordException(Exception exception) {
+ assertStarted();
+ span.recordException(exception);
+ }
+
+ @Override
+ public void setStatus(StatusCode code, String description) {
+ assertStarted();
+ span.setStatus(mapStatusCode(code), description);
+ }
+
+ @Override
+ public void setStatus(StatusCode code) {
+ assertStarted();
+ span.setStatus(mapStatusCode(code));
+ }
+
+ @Override
+ public void tracingFinished() {
+ assertStarted();
+ span.end();
+ }
+
+ private boolean currentPrecisionLevelIsAtLeast(PrecisionLevel requiredLevel) {
+ return requiredLevel.compareTo(precision) <= 0;
+ }
+}
diff --git a/driver-opentelemetry/src/main/java/com/datastax/driver/opentelemetry/OpenTelemetryTracingInfoFactory.java b/driver-opentelemetry/src/main/java/com/datastax/driver/opentelemetry/OpenTelemetryTracingInfoFactory.java
new file mode 100644
index 00000000000..64ff29f82a9
--- /dev/null
+++ b/driver-opentelemetry/src/main/java/com/datastax/driver/opentelemetry/OpenTelemetryTracingInfoFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 ScyllaDB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.datastax.driver.opentelemetry;
+
+import com.datastax.driver.core.tracing.NoopTracingInfoFactory;
+import com.datastax.driver.core.tracing.PrecisionLevel;
+import com.datastax.driver.core.tracing.TracingInfo;
+import com.datastax.driver.core.tracing.TracingInfoFactory;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.context.Context;
+
+public class OpenTelemetryTracingInfoFactory implements TracingInfoFactory {
+ private final Tracer tracer;
+ private final PrecisionLevel precision;
+
+ public OpenTelemetryTracingInfoFactory(final Tracer tracer) {
+ this(tracer, PrecisionLevel.NORMAL);
+ }
+
+ public OpenTelemetryTracingInfoFactory(final Tracer tracer, final PrecisionLevel precision) {
+ this.tracer = tracer;
+ this.precision = precision;
+ }
+
+ @Override
+ public TracingInfo buildTracingInfo() {
+ final Context current = Context.current();
+ return new OpenTelemetryTracingInfo(tracer, current, precision);
+ }
+
+ @Override
+ public TracingInfo buildTracingInfo(TracingInfo parent) {
+ if (parent instanceof OpenTelemetryTracingInfo) {
+ final OpenTelemetryTracingInfo castedParent = (OpenTelemetryTracingInfo) parent;
+ return new OpenTelemetryTracingInfo(
+ castedParent.getTracer(), castedParent.getContext(), castedParent.getPrecision());
+ }
+
+ return new NoopTracingInfoFactory().buildTracingInfo();
+ }
+
+ public TracingInfo buildTracingInfo(Span parent) {
+ final Context current = Context.current().with(parent);
+ return new OpenTelemetryTracingInfo(tracer, current, precision);
+ }
+}
diff --git a/pom.xml b/pom.xml
index 307e2df8864..e925b5a3c6e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,6 +44,7 @@
driver-examples
driver-tests
driver-dist
+ driver-opentelemetry
@@ -133,6 +134,12 @@
${project.parent.version}
+
+ com.scylladb
+ scylla-driver-opentelemetry
+ ${project.parent.version}
+
+
com.google.guava
guava
@@ -727,6 +734,9 @@
java18
1.0
+
+ io.opentelemetry:*
+