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 + 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:* +