Refactor DatabaseClientTracer (in preparation for low-cardinality span names) (#2398)
* Refactor DatabaseClientTracer (in preparation for low-cardinality span names) * spotless
This commit is contained in:
parent
605485b998
commit
fd55ce226a
|
@ -8,16 +8,22 @@ package io.opentelemetry.instrumentation.api.tracer;
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
||||||
|
|
||||||
import io.opentelemetry.api.OpenTelemetry;
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.api.trace.StatusCode;
|
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.ExecutionException;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public abstract class DatabaseClientTracer<CONNECTION, QUERY> extends BaseTracer {
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for implementing Tracers for database clients.
|
||||||
|
*
|
||||||
|
* @param <CONNECTION> type of the database connection.
|
||||||
|
* @param <STATEMENT> type of the database statement being executed.
|
||||||
|
* @param <SANITIZEDSTATEMENT> type of the database statement after sanitization.
|
||||||
|
*/
|
||||||
|
public abstract class DatabaseClientTracer<CONNECTION, STATEMENT, SANITIZEDSTATEMENT>
|
||||||
|
extends BaseTracer {
|
||||||
protected static final String DB_QUERY = "DB Query";
|
protected static final String DB_QUERY = "DB Query";
|
||||||
|
|
||||||
public DatabaseClientTracer() {}
|
public DatabaseClientTracer() {}
|
||||||
|
@ -30,61 +36,68 @@ public abstract class DatabaseClientTracer<CONNECTION, QUERY> extends BaseTracer
|
||||||
return shouldStartSpan(CLIENT, parentContext);
|
return shouldStartSpan(CLIENT, parentContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context startSpan(Context parentContext, CONNECTION connection, QUERY query) {
|
public Context startSpan(Context parentContext, CONNECTION connection, STATEMENT statement) {
|
||||||
String normalizedQuery = normalizeQuery(query);
|
SANITIZEDSTATEMENT sanitizedStatement = sanitizeStatement(statement);
|
||||||
|
|
||||||
Span span =
|
SpanBuilder span =
|
||||||
tracer
|
tracer
|
||||||
.spanBuilder(spanName(connection, query, normalizedQuery))
|
.spanBuilder(spanName(connection, statement, sanitizedStatement))
|
||||||
.setParent(parentContext)
|
.setParent(parentContext)
|
||||||
.setSpanKind(CLIENT)
|
.setSpanKind(CLIENT)
|
||||||
.setAttribute(SemanticAttributes.DB_SYSTEM, dbSystem(connection))
|
.setAttribute(SemanticAttributes.DB_SYSTEM, dbSystem(connection));
|
||||||
.startSpan();
|
|
||||||
|
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
onConnection(span, connection);
|
onConnection(span, connection);
|
||||||
setNetSemanticConvention(span, connection);
|
setNetSemanticConvention(span, connection);
|
||||||
}
|
}
|
||||||
onStatement(span, normalizedQuery);
|
onStatement(span, connection, statement, sanitizedStatement);
|
||||||
|
|
||||||
return withClientSpan(parentContext, span);
|
return withClientSpan(parentContext, span.startSpan());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endExceptionally(Context context, Throwable throwable) {
|
protected abstract SANITIZEDSTATEMENT sanitizeStatement(STATEMENT statement);
|
||||||
Span span = Span.fromContext(context);
|
|
||||||
onError(span, throwable);
|
protected String spanName(
|
||||||
end(span);
|
CONNECTION connection, STATEMENT statement, SANITIZEDSTATEMENT sanitizedStatement) {
|
||||||
|
return conventionSpanName(
|
||||||
|
dbName(connection), dbOperation(connection, statement, sanitizedStatement), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper method for constructing the span name formatting according to DB semantic conventions:
|
||||||
|
* {@code <db.operation> <db.name><table>}.
|
||||||
|
*/
|
||||||
|
public static String conventionSpanName(
|
||||||
|
@Nullable String dbName, @Nullable String operation, @Nullable String table) {
|
||||||
|
if (operation == null) {
|
||||||
|
return dbName == null ? DB_QUERY : dbName;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder name = new StringBuilder(operation);
|
||||||
|
if (dbName != null || table != null) {
|
||||||
|
name.append(' ');
|
||||||
|
}
|
||||||
|
if (dbName != null) {
|
||||||
|
name.append(dbName);
|
||||||
|
if (table != null) {
|
||||||
|
name.append('.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (table != null) {
|
||||||
|
name.append(table);
|
||||||
|
}
|
||||||
|
return name.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String dbSystem(CONNECTION connection);
|
||||||
|
|
||||||
/** This should be called when the connection is being used, not when it's created. */
|
/** This should be called when the connection is being used, not when it's created. */
|
||||||
protected Span onConnection(Span span, CONNECTION connection) {
|
protected void onConnection(SpanBuilder span, CONNECTION connection) {
|
||||||
span.setAttribute(SemanticAttributes.DB_USER, dbUser(connection));
|
span.setAttribute(SemanticAttributes.DB_USER, dbUser(connection));
|
||||||
span.setAttribute(SemanticAttributes.DB_NAME, dbName(connection));
|
span.setAttribute(SemanticAttributes.DB_NAME, dbName(connection));
|
||||||
span.setAttribute(SemanticAttributes.DB_CONNECTION_STRING, dbConnectionString(connection));
|
span.setAttribute(SemanticAttributes.DB_CONNECTION_STRING, dbConnectionString(connection));
|
||||||
return span;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onError(Span span, Throwable throwable) {
|
|
||||||
if (throwable != null) {
|
|
||||||
span.setStatus(StatusCode.ERROR);
|
|
||||||
addThrowable(
|
|
||||||
span, throwable instanceof ExecutionException ? throwable.getCause() : throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setNetSemanticConvention(Span span, CONNECTION connection) {
|
|
||||||
NetPeerUtils.INSTANCE.setNetPeer(span, peerAddress(connection));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onStatement(Span span, String statement) {
|
|
||||||
span.setAttribute(SemanticAttributes.DB_STATEMENT, statement);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract String normalizeQuery(QUERY query);
|
|
||||||
|
|
||||||
protected abstract String dbSystem(CONNECTION connection);
|
|
||||||
|
|
||||||
protected String dbUser(CONNECTION connection) {
|
protected String dbUser(CONNECTION connection) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -97,17 +110,30 @@ public abstract class DatabaseClientTracer<CONNECTION, QUERY> extends BaseTracer
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setNetSemanticConvention(SpanBuilder span, CONNECTION connection) {
|
||||||
|
NetPeerUtils.INSTANCE.setNetPeer(span, peerAddress(connection));
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract InetSocketAddress peerAddress(CONNECTION connection);
|
protected abstract InetSocketAddress peerAddress(CONNECTION connection);
|
||||||
|
|
||||||
protected String spanName(CONNECTION connection, QUERY query, String normalizedQuery) {
|
protected void onStatement(
|
||||||
if (normalizedQuery != null) {
|
SpanBuilder span,
|
||||||
return normalizedQuery;
|
CONNECTION connection,
|
||||||
}
|
STATEMENT statement,
|
||||||
|
SANITIZEDSTATEMENT sanitizedStatement) {
|
||||||
|
span.setAttribute(
|
||||||
|
SemanticAttributes.DB_STATEMENT, dbStatement(connection, statement, sanitizedStatement));
|
||||||
|
span.setAttribute(
|
||||||
|
SemanticAttributes.DB_OPERATION, dbOperation(connection, statement, sanitizedStatement));
|
||||||
|
}
|
||||||
|
|
||||||
String result = null;
|
protected String dbStatement(
|
||||||
if (connection != null) {
|
CONNECTION connection, STATEMENT statement, SANITIZEDSTATEMENT sanitizedStatement) {
|
||||||
result = dbName(connection);
|
return null;
|
||||||
}
|
}
|
||||||
return result == null ? DB_QUERY : result;
|
|
||||||
|
protected String dbOperation(
|
||||||
|
CONNECTION connection, STATEMENT statement, SANITIZEDSTATEMENT sanitizedStatement) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,18 @@ import com.datastax.driver.core.ExecutionInfo;
|
||||||
import com.datastax.driver.core.Host;
|
import com.datastax.driver.core.Host;
|
||||||
import com.datastax.driver.core.Session;
|
import com.datastax.driver.core.Session;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
public class CassandraDatabaseClientTracer extends DatabaseClientTracer<Session, String> {
|
public class CassandraDatabaseClientTracer
|
||||||
|
extends DatabaseClientTracer<Session, String, SqlStatementInfo> {
|
||||||
private static final CassandraDatabaseClientTracer TRACER = new CassandraDatabaseClientTracer();
|
private static final CassandraDatabaseClientTracer TRACER = new CassandraDatabaseClientTracer();
|
||||||
|
|
||||||
public static CassandraDatabaseClientTracer tracer() {
|
public static CassandraDatabaseClientTracer tracer() {
|
||||||
|
@ -30,8 +33,23 @@ public class CassandraDatabaseClientTracer extends DatabaseClientTracer<Session,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(String query) {
|
protected SqlStatementInfo sanitizeStatement(String statement) {
|
||||||
return SqlStatementSanitizer.sanitize(query).getFullStatement();
|
return SqlStatementSanitizer.sanitize(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use the <operation> <db.name>.<table> naming scheme
|
||||||
|
protected String spanName(
|
||||||
|
Session connection, String statement, SqlStatementInfo sanitizedStatement) {
|
||||||
|
String fullStatement = sanitizedStatement.getFullStatement();
|
||||||
|
if (fullStatement != null) {
|
||||||
|
return fullStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = null;
|
||||||
|
if (connection != null) {
|
||||||
|
result = dbName(connection);
|
||||||
|
}
|
||||||
|
return result == null ? DB_QUERY : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,20 +57,26 @@ public class CassandraDatabaseClientTracer extends DatabaseClientTracer<Session,
|
||||||
return DbSystemValues.CASSANDRA;
|
return DbSystemValues.CASSANDRA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onConnection(SpanBuilder span, Session session) {
|
||||||
|
span.setAttribute(SemanticAttributes.DB_CASSANDRA_KEYSPACE, session.getLoggedKeyspace());
|
||||||
|
super.onConnection(span, session);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbName(Session session) {
|
protected String dbName(Session session) {
|
||||||
return session.getLoggedKeyspace();
|
return session.getLoggedKeyspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Span onConnection(Span span, Session session) {
|
protected InetSocketAddress peerAddress(Session session) {
|
||||||
span.setAttribute(SemanticAttributes.DB_CASSANDRA_KEYSPACE, session.getLoggedKeyspace());
|
return null;
|
||||||
return super.onConnection(span, session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InetSocketAddress peerAddress(Session session) {
|
protected String dbStatement(
|
||||||
return null;
|
Session connection, String statement, SqlStatementInfo sanitizedStatement) {
|
||||||
|
return sanitizedStatement.getFullStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void end(Context context, ExecutionInfo executionInfo) {
|
public void end(Context context, ExecutionInfo executionInfo) {
|
||||||
|
|
|
@ -14,16 +14,19 @@ import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
|
||||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||||
import com.datastax.oss.driver.api.core.metadata.Node;
|
import com.datastax.oss.driver.api.core.metadata.Node;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
public class CassandraDatabaseClientTracer extends DatabaseClientTracer<CqlSession, String> {
|
public class CassandraDatabaseClientTracer
|
||||||
|
extends DatabaseClientTracer<CqlSession, String, SqlStatementInfo> {
|
||||||
|
|
||||||
private static final CassandraDatabaseClientTracer TRACER = new CassandraDatabaseClientTracer();
|
private static final CassandraDatabaseClientTracer TRACER = new CassandraDatabaseClientTracer();
|
||||||
|
|
||||||
|
@ -37,8 +40,23 @@ public class CassandraDatabaseClientTracer extends DatabaseClientTracer<CqlSessi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(String query) {
|
protected SqlStatementInfo sanitizeStatement(String statement) {
|
||||||
return SqlStatementSanitizer.sanitize(query).getFullStatement();
|
return SqlStatementSanitizer.sanitize(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use the <operation> <db.name>.<table> naming scheme
|
||||||
|
protected String spanName(
|
||||||
|
CqlSession connection, String statement, SqlStatementInfo sanitizedStatement) {
|
||||||
|
String fullStatement = sanitizedStatement.getFullStatement();
|
||||||
|
if (fullStatement != null) {
|
||||||
|
return fullStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = null;
|
||||||
|
if (connection != null) {
|
||||||
|
result = dbName(connection);
|
||||||
|
}
|
||||||
|
return result == null ? DB_QUERY : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,9 +145,13 @@ public class CassandraDatabaseClientTracer extends DatabaseClientTracer<CqlSessi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStatement(Span span, String statement) {
|
protected void onStatement(
|
||||||
super.onStatement(span, statement);
|
SpanBuilder span,
|
||||||
String table = SqlStatementSanitizer.sanitize(statement).getTable();
|
CqlSession connection,
|
||||||
|
String statement,
|
||||||
|
SqlStatementInfo sanitizedStatement) {
|
||||||
|
super.onStatement(span, connection, statement, sanitizedStatement);
|
||||||
|
String table = sanitizedStatement.getTable();
|
||||||
if (table != null) {
|
if (table != null) {
|
||||||
// account for splitting out the keyspace, <keyspace>.<table>
|
// account for splitting out the keyspace, <keyspace>.<table>
|
||||||
int i = table.indexOf('.');
|
int i = table.indexOf('.');
|
||||||
|
@ -139,4 +161,10 @@ public class CassandraDatabaseClientTracer extends DatabaseClientTracer<CqlSessi
|
||||||
span.setAttribute(SemanticAttributes.DB_CASSANDRA_TABLE, table);
|
span.setAttribute(SemanticAttributes.DB_CASSANDRA_TABLE, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
CqlSession connection, String statement, SqlStatementInfo sanitizedStatement) {
|
||||||
|
return sanitizedStatement.getFullStatement();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValu
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
public class CouchbaseClientTracer extends DatabaseClientTracer<Void, Method> {
|
public class CouchbaseClientTracer extends DatabaseClientTracer<Void, Method, Void> {
|
||||||
private static final CouchbaseClientTracer TRACER = new CouchbaseClientTracer();
|
private static final CouchbaseClientTracer TRACER = new CouchbaseClientTracer();
|
||||||
|
|
||||||
public static CouchbaseClientTracer tracer() {
|
public static CouchbaseClientTracer tracer() {
|
||||||
|
@ -18,13 +18,18 @@ public class CouchbaseClientTracer extends DatabaseClientTracer<Void, Method> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(Method method) {
|
protected String spanName(Void connection, Method method, Void sanitizedStatement) {
|
||||||
Class<?> declaringClass = method.getDeclaringClass();
|
Class<?> declaringClass = method.getDeclaringClass();
|
||||||
String className =
|
String className =
|
||||||
declaringClass.getSimpleName().replace("CouchbaseAsync", "").replace("DefaultAsync", "");
|
declaringClass.getSimpleName().replace("CouchbaseAsync", "").replace("DefaultAsync", "");
|
||||||
return className + "." + method.getName();
|
return className + "." + method.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void sanitizeStatement(Method method) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbSystem(Void connection) {
|
protected String dbSystem(Void connection) {
|
||||||
return DbSystemValues.COUCHBASE;
|
return DbSystemValues.COUCHBASE;
|
||||||
|
|
|
@ -13,7 +13,7 @@ import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import org.elasticsearch.client.Response;
|
import org.elasticsearch.client.Response;
|
||||||
|
|
||||||
public class ElasticsearchRestClientTracer extends DatabaseClientTracer<Void, String> {
|
public class ElasticsearchRestClientTracer extends DatabaseClientTracer<Void, String, String> {
|
||||||
private static final ElasticsearchRestClientTracer TRACER = new ElasticsearchRestClientTracer();
|
private static final ElasticsearchRestClientTracer TRACER = new ElasticsearchRestClientTracer();
|
||||||
|
|
||||||
public static ElasticsearchRestClientTracer tracer() {
|
public static ElasticsearchRestClientTracer tracer() {
|
||||||
|
@ -29,13 +29,8 @@ public class ElasticsearchRestClientTracer extends DatabaseClientTracer<Void, St
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStatement(Span span, String statement) {
|
protected String sanitizeStatement(String operation) {
|
||||||
span.setAttribute(SemanticAttributes.DB_OPERATION, statement);
|
return operation;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String normalizeQuery(String query) {
|
|
||||||
return query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,6 +43,11 @@ public class ElasticsearchRestClientTracer extends DatabaseClientTracer<Void, St
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbOperation(Void connection, String operation, String ignored) {
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getInstrumentationName() {
|
protected String getInstrumentationName() {
|
||||||
return "io.opentelemetry.javaagent.elasticsearch";
|
return "io.opentelemetry.javaagent.elasticsearch";
|
||||||
|
|
|
@ -8,12 +8,11 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import org.elasticsearch.action.Action;
|
import org.elasticsearch.action.Action;
|
||||||
|
|
||||||
public class ElasticsearchTransportClientTracer
|
public class ElasticsearchTransportClientTracer
|
||||||
extends DatabaseClientTracer<Void, Action<?, ?, ?>> {
|
extends DatabaseClientTracer<Void, Action<?, ?, ?>, String> {
|
||||||
private static final ElasticsearchTransportClientTracer TRACER =
|
private static final ElasticsearchTransportClientTracer TRACER =
|
||||||
new ElasticsearchTransportClientTracer();
|
new ElasticsearchTransportClientTracer();
|
||||||
|
|
||||||
|
@ -21,15 +20,15 @@ public class ElasticsearchTransportClientTracer
|
||||||
return TRACER;
|
return TRACER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRequest(Context context, Class action, Class request) {
|
public void onRequest(Context context, Class<?> action, Class<?> request) {
|
||||||
Span span = Span.fromContext(context);
|
Span span = Span.fromContext(context);
|
||||||
span.setAttribute("elasticsearch.action", action.getSimpleName());
|
span.setAttribute("elasticsearch.action", action.getSimpleName());
|
||||||
span.setAttribute("elasticsearch.request", request.getSimpleName());
|
span.setAttribute("elasticsearch.request", request.getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(Action<?, ?, ?> query) {
|
protected String sanitizeStatement(Action<?, ?, ?> action) {
|
||||||
return query.getClass().getSimpleName();
|
return action.getClass().getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,8 +42,8 @@ public class ElasticsearchTransportClientTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStatement(Span span, String statement) {
|
protected String dbOperation(Void connection, Action<?, ?, ?> action, String operation) {
|
||||||
span.setAttribute(SemanticAttributes.DB_OPERATION, statement);
|
return operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,13 +8,15 @@ package io.opentelemetry.javaagent.instrumentation.geode;
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import org.apache.geode.cache.Region;
|
import org.apache.geode.cache.Region;
|
||||||
|
|
||||||
public class GeodeTracer extends DatabaseClientTracer<Region<?, ?>, String> {
|
public class GeodeTracer extends DatabaseClientTracer<Region<?, ?>, String, SqlStatementInfo> {
|
||||||
private static final GeodeTracer TRACER = new GeodeTracer();
|
private static final GeodeTracer TRACER = new GeodeTracer();
|
||||||
|
|
||||||
public static GeodeTracer tracer() {
|
public static GeodeTracer tracer() {
|
||||||
|
@ -22,33 +24,30 @@ public class GeodeTracer extends DatabaseClientTracer<Region<?, ?>, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Span startSpan(String operation, Region<?, ?> connection, String query) {
|
public Span startSpan(String operation, Region<?, ?> connection, String query) {
|
||||||
String normalizedQuery = normalizeQuery(query);
|
SqlStatementInfo sanitizedStatement = sanitizeStatement(query);
|
||||||
|
|
||||||
Span span =
|
SpanBuilder span =
|
||||||
tracer
|
tracer
|
||||||
.spanBuilder(operation)
|
.spanBuilder(operation)
|
||||||
.setSpanKind(CLIENT)
|
.setSpanKind(CLIENT)
|
||||||
.setAttribute(SemanticAttributes.DB_SYSTEM, dbSystem(connection))
|
.setAttribute(SemanticAttributes.DB_SYSTEM, dbSystem(connection))
|
||||||
.setAttribute(SemanticAttributes.DB_OPERATION, operation)
|
.setAttribute(SemanticAttributes.DB_OPERATION, operation);
|
||||||
.startSpan();
|
|
||||||
|
|
||||||
onConnection(span, connection);
|
onConnection(span, connection);
|
||||||
setNetSemanticConvention(span, connection);
|
setNetSemanticConvention(span, connection);
|
||||||
onStatement(span, normalizedQuery);
|
onStatement(span, connection, query, sanitizedStatement);
|
||||||
|
|
||||||
return span;
|
return span.startSpan();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(String query) {
|
protected SqlStatementInfo sanitizeStatement(String statement) {
|
||||||
return SqlStatementSanitizer.sanitize(query).getFullStatement();
|
return SqlStatementSanitizer.sanitize(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbSystem(Region<?, ?> region) {
|
protected String dbSystem(Region<?, ?> region) {
|
||||||
// TODO(anuraaga): Replace with semantic attribute
|
return SemanticAttributes.DbSystemValues.GEODE;
|
||||||
// https://github.com/open-telemetry/opentelemetry-specification/pull/1321
|
|
||||||
return "geode";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,6 +60,12 @@ public class GeodeTracer extends DatabaseClientTracer<Region<?, ?>, String> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
Region<?, ?> connection, String statement, SqlStatementInfo sanitizedStatement) {
|
||||||
|
return sanitizedStatement.getFullStatement();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getInstrumentationName() {
|
protected String getInstrumentationName() {
|
||||||
return "io.opentelemetry.javaagent.geode";
|
return "io.opentelemetry.javaagent.geode";
|
||||||
|
|
|
@ -14,7 +14,6 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
|
||||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -49,7 +48,7 @@ public class ConnectionInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
public static void addDbInfo(
|
public static void addDbInfo(
|
||||||
@Advice.Argument(0) String sql, @Advice.Return PreparedStatement statement) {
|
@Advice.Argument(0) String sql, @Advice.Return PreparedStatement statement) {
|
||||||
JdbcMaps.preparedStatements.put(statement, SqlStatementSanitizer.sanitize(sql));
|
JdbcMaps.preparedStatements.put(statement, sql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ package io.opentelemetry.javaagent.instrumentation.jdbc;
|
||||||
import static io.opentelemetry.javaagent.instrumentation.api.WeakMap.Provider.newWeakMap;
|
import static io.opentelemetry.javaagent.instrumentation.api.WeakMap.Provider.newWeakMap;
|
||||||
|
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.WeakMap;
|
import io.opentelemetry.javaagent.instrumentation.api.WeakMap;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
|
|
||||||
|
@ -19,6 +18,5 @@ import java.sql.PreparedStatement;
|
||||||
*/
|
*/
|
||||||
public class JdbcMaps {
|
public class JdbcMaps {
|
||||||
public static final WeakMap<Connection, DbInfo> connectionInfo = newWeakMap();
|
public static final WeakMap<Connection, DbInfo> connectionInfo = newWeakMap();
|
||||||
public static final WeakMap<PreparedStatement, SqlStatementInfo> preparedStatements =
|
public static final WeakMap<PreparedStatement, String> preparedStatements = newWeakMap();
|
||||||
newWeakMap();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,6 @@ import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcUtils.connecti
|
||||||
|
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
|
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
|
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementInfo;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
import io.opentelemetry.javaagent.instrumentation.api.db.SqlStatementSanitizer;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
@ -20,7 +18,7 @@ import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
|
||||||
public class JdbcTracer extends DatabaseClientTracer<DbInfo, SqlStatementInfo> {
|
public class JdbcTracer extends DatabaseClientTracer<DbInfo, String, SqlStatementInfo> {
|
||||||
private static final JdbcTracer TRACER = new JdbcTracer();
|
private static final JdbcTracer TRACER = new JdbcTracer();
|
||||||
|
|
||||||
public static JdbcTracer tracer() {
|
public static JdbcTracer tracer() {
|
||||||
|
@ -32,6 +30,33 @@ public class JdbcTracer extends DatabaseClientTracer<DbInfo, SqlStatementInfo> {
|
||||||
return "io.opentelemetry.javaagent.jdbc";
|
return "io.opentelemetry.javaagent.jdbc";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Context startSpan(Context parentContext, PreparedStatement statement) {
|
||||||
|
return startSpan(parentContext, statement, JdbcMaps.preparedStatements.get(statement));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context startSpan(Context parentContext, Statement statement, String query) {
|
||||||
|
Connection connection = connectionFromStatement(statement);
|
||||||
|
if (connection == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DbInfo dbInfo = extractDbInfo(connection);
|
||||||
|
|
||||||
|
return startSpan(parentContext, dbInfo, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SqlStatementInfo sanitizeStatement(String statement) {
|
||||||
|
return SqlStatementSanitizer.sanitize(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String spanName(
|
||||||
|
DbInfo connection, String statement, SqlStatementInfo sanitizedStatement) {
|
||||||
|
return conventionSpanName(
|
||||||
|
dbName(connection), sanitizedStatement.getOperation(), sanitizedStatement.getTable());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbSystem(DbInfo info) {
|
protected String dbSystem(DbInfo info) {
|
||||||
return info.getSystem();
|
return info.getSystem();
|
||||||
|
@ -51,6 +76,11 @@ public class JdbcTracer extends DatabaseClientTracer<DbInfo, SqlStatementInfo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbConnectionString(DbInfo info) {
|
||||||
|
return info.getShortUrl();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO find a way to implement
|
// TODO find a way to implement
|
||||||
@Override
|
@Override
|
||||||
protected InetSocketAddress peerAddress(DbInfo dbInfo) {
|
protected InetSocketAddress peerAddress(DbInfo dbInfo) {
|
||||||
|
@ -58,58 +88,9 @@ public class JdbcTracer extends DatabaseClientTracer<DbInfo, SqlStatementInfo> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbConnectionString(DbInfo info) {
|
protected String dbStatement(
|
||||||
return info.getShortUrl();
|
DbInfo connection, String statement, SqlStatementInfo sanitizedStatement) {
|
||||||
}
|
return sanitizedStatement.getFullStatement();
|
||||||
|
|
||||||
public CallDepth getCallDepth() {
|
|
||||||
return CallDepthThreadLocalMap.getCallDepth(Statement.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context startSpan(Context parentContext, PreparedStatement statement) {
|
|
||||||
return startSpan(parentContext, statement, JdbcMaps.preparedStatements.get(statement));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context startSpan(Context parentContext, Statement statement, String query) {
|
|
||||||
return startSpan(parentContext, statement, SqlStatementSanitizer.sanitize(query));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Context startSpan(
|
|
||||||
Context parentContext, Statement statement, SqlStatementInfo queryInfo) {
|
|
||||||
Connection connection = connectionFromStatement(statement);
|
|
||||||
if (connection == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DbInfo dbInfo = extractDbInfo(connection);
|
|
||||||
|
|
||||||
return startSpan(parentContext, dbInfo, queryInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String normalizeQuery(SqlStatementInfo query) {
|
|
||||||
return query.getFullStatement();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String spanName(DbInfo connection, SqlStatementInfo query, String normalizedQuery) {
|
|
||||||
String dbName = dbName(connection);
|
|
||||||
if (query.getOperation() == null) {
|
|
||||||
return dbName == null ? DB_QUERY : dbName;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder name = new StringBuilder();
|
|
||||||
name.append(query.getOperation()).append(' ');
|
|
||||||
if (dbName != null) {
|
|
||||||
name.append(dbName);
|
|
||||||
if (query.getTable() != null) {
|
|
||||||
name.append('.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (query.getTable() != null) {
|
|
||||||
name.append(query.getTable());
|
|
||||||
}
|
|
||||||
return name.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DbInfo extractDbInfo(Connection connection) {
|
private DbInfo extractDbInfo(Connection connection) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import java.util.List;
|
||||||
import redis.clients.jedis.Connection;
|
import redis.clients.jedis.Connection;
|
||||||
import redis.clients.jedis.Protocol.Command;
|
import redis.clients.jedis.Protocol.Command;
|
||||||
|
|
||||||
public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandWithArgs> {
|
public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandWithArgs, String> {
|
||||||
private static final JedisClientTracer TRACER = new JedisClientTracer();
|
private static final JedisClientTracer TRACER = new JedisClientTracer();
|
||||||
|
|
||||||
public static JedisClientTracer tracer() {
|
public static JedisClientTracer tracer() {
|
||||||
|
@ -23,13 +23,14 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String spanName(Connection connection, CommandWithArgs query, String normalizedQuery) {
|
protected String sanitizeStatement(CommandWithArgs command) {
|
||||||
return query.getStringCommand();
|
return RedisCommandSanitizer.sanitize(command.getStringCommand(), command.getArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(CommandWithArgs command) {
|
protected String spanName(
|
||||||
return RedisCommandSanitizer.sanitize(command.getStringCommand(), command.getArgs());
|
Connection connection, CommandWithArgs command, String sanitizedStatement) {
|
||||||
|
return command.getStringCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,6 +48,12 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
||||||
return new InetSocketAddress(connection.getHost(), connection.getPort());
|
return new InetSocketAddress(connection.getHost(), connection.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
Connection connection, CommandWithArgs command, String sanitizedStatement) {
|
||||||
|
return sanitizedStatement;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getInstrumentationName() {
|
protected String getInstrumentationName() {
|
||||||
return "io.opentelemetry.javaagent.jedis";
|
return "io.opentelemetry.javaagent.jedis";
|
||||||
|
|
|
@ -17,7 +17,7 @@ import redis.clients.jedis.Connection;
|
||||||
import redis.clients.jedis.Protocol;
|
import redis.clients.jedis.Protocol;
|
||||||
import redis.clients.jedis.commands.ProtocolCommand;
|
import redis.clients.jedis.commands.ProtocolCommand;
|
||||||
|
|
||||||
public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandWithArgs> {
|
public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandWithArgs, String> {
|
||||||
private static final JedisClientTracer TRACER = new JedisClientTracer();
|
private static final JedisClientTracer TRACER = new JedisClientTracer();
|
||||||
|
|
||||||
public static JedisClientTracer tracer() {
|
public static JedisClientTracer tracer() {
|
||||||
|
@ -25,13 +25,14 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String spanName(Connection connection, CommandWithArgs query, String normalizedQuery) {
|
protected String sanitizeStatement(CommandWithArgs command) {
|
||||||
return query.getStringCommand();
|
return RedisCommandSanitizer.sanitize(command.getStringCommand(), command.getArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(CommandWithArgs command) {
|
protected String spanName(
|
||||||
return RedisCommandSanitizer.sanitize(command.getStringCommand(), command.getArgs());
|
Connection connection, CommandWithArgs command, String sanitizedStatement) {
|
||||||
|
return command.getStringCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -49,6 +50,12 @@ public class JedisClientTracer extends DatabaseClientTracer<Connection, CommandW
|
||||||
return new InetSocketAddress(connection.getHost(), connection.getPort());
|
return new InetSocketAddress(connection.getHost(), connection.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
Connection connection, CommandWithArgs command, String sanitizedStatement) {
|
||||||
|
return sanitizedStatement;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getInstrumentationName() {
|
protected String getInstrumentationName() {
|
||||||
return "io.opentelemetry.javaagent.jedis";
|
return "io.opentelemetry.javaagent.jedis";
|
||||||
|
|
|
@ -6,14 +6,28 @@
|
||||||
package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0;
|
package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0;
|
||||||
|
|
||||||
import com.lambdaworks.redis.RedisURI;
|
import com.lambdaworks.redis.RedisURI;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
public abstract class LettuceAbstractDatabaseClientTracer<QUERY>
|
public abstract class LettuceAbstractDatabaseClientTracer<STATEMENT>
|
||||||
extends DatabaseClientTracer<RedisURI, QUERY> {
|
extends DatabaseClientTracer<RedisURI, STATEMENT, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String spanName(RedisURI connection, STATEMENT statement, String operation) {
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnection(SpanBuilder span, RedisURI connection) {
|
||||||
|
if (connection != null && connection.getDatabase() != 0) {
|
||||||
|
span.setAttribute(
|
||||||
|
SemanticAttributes.DB_REDIS_DATABASE_INDEX, (long) connection.getDatabase());
|
||||||
|
}
|
||||||
|
super.onConnection(span, connection);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbSystem(RedisURI connection) {
|
protected String dbSystem(RedisURI connection) {
|
||||||
|
@ -26,11 +40,8 @@ public abstract class LettuceAbstractDatabaseClientTracer<QUERY>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Span onConnection(Span span, RedisURI connection) {
|
protected String dbStatement(RedisURI connection, STATEMENT statement, String operation) {
|
||||||
if (connection != null && connection.getDatabase() != 0) {
|
return operation;
|
||||||
span.setAttribute(SemanticAttributes.DB_REDIS_DATABASE_INDEX, connection.getDatabase());
|
|
||||||
}
|
|
||||||
return super.onConnection(span, connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class LettuceConnectionDatabaseClientTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(String command) {
|
protected String sanitizeStatement(String command) {
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class LettuceDatabaseClientTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(RedisCommand<?, ?, ?> command) {
|
protected String sanitizeStatement(RedisCommand<?, ?, ?> command) {
|
||||||
return command.getType().name();
|
return command.getType().name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
package io.opentelemetry.javaagent.instrumentation.lettuce.v5_0;
|
package io.opentelemetry.javaagent.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import io.lettuce.core.RedisURI;
|
import io.lettuce.core.RedisURI;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
public abstract class LettuceAbstractDatabaseClientTracer<QUERY>
|
public abstract class LettuceAbstractDatabaseClientTracer<STATEMENT>
|
||||||
extends DatabaseClientTracer<RedisURI, QUERY> {
|
extends DatabaseClientTracer<RedisURI, STATEMENT, String> {
|
||||||
@Override
|
@Override
|
||||||
protected String getInstrumentationName() {
|
protected String getInstrumentationName() {
|
||||||
return "io.opentelemetry.javaagent.lettuce";
|
return "io.opentelemetry.javaagent.lettuce";
|
||||||
|
@ -30,10 +30,17 @@ public abstract class LettuceAbstractDatabaseClientTracer<QUERY>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Span onConnection(Span span, RedisURI connection) {
|
public void onConnection(SpanBuilder span, RedisURI connection) {
|
||||||
if (connection != null && connection.getDatabase() != 0) {
|
if (connection != null && connection.getDatabase() != 0) {
|
||||||
span.setAttribute(SemanticAttributes.DB_REDIS_DATABASE_INDEX, connection.getDatabase());
|
span.setAttribute(
|
||||||
|
SemanticAttributes.DB_REDIS_DATABASE_INDEX, (long) connection.getDatabase());
|
||||||
}
|
}
|
||||||
return super.onConnection(span, connection);
|
super.onConnection(span, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
RedisURI connection, STATEMENT statement, String sanitizedStatement) {
|
||||||
|
return sanitizedStatement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,11 @@ public class LettuceAsyncBiFunction<T, U extends Throwable, R>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R apply(T t, Throwable throwable) {
|
public R apply(T t, Throwable throwable) {
|
||||||
if (throwable instanceof CancellationException) {
|
if (throwable == null) {
|
||||||
|
tracer().end(context);
|
||||||
|
} else if (throwable instanceof CancellationException) {
|
||||||
if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
|
if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
|
||||||
Span span = Span.fromContext(context);
|
Span.fromContext(context).setAttribute("lettuce.command.cancelled", true);
|
||||||
span.setAttribute("lettuce.command.cancelled", true);
|
|
||||||
}
|
}
|
||||||
tracer().end(context);
|
tracer().end(context);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.lettuce.v5_0;
|
package io.opentelemetry.javaagent.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
|
import io.lettuce.core.RedisURI;
|
||||||
|
|
||||||
public class LettuceConnectionDatabaseClientTracer
|
public class LettuceConnectionDatabaseClientTracer
|
||||||
extends LettuceAbstractDatabaseClientTracer<String> {
|
extends LettuceAbstractDatabaseClientTracer<String> {
|
||||||
private static final LettuceConnectionDatabaseClientTracer TRACER =
|
private static final LettuceConnectionDatabaseClientTracer TRACER =
|
||||||
|
@ -15,7 +17,12 @@ public class LettuceConnectionDatabaseClientTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(String query) {
|
protected String sanitizeStatement(String command) {
|
||||||
return query;
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String spanName(RedisURI connection, String command, String ignored) {
|
||||||
|
return command;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,7 @@ public class LettuceDatabaseClientTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String spanName(
|
protected String sanitizeStatement(RedisCommand<?, ?, ?> redisCommand) {
|
||||||
RedisURI connection, RedisCommand<?, ?, ?> query, String normalizedQuery) {
|
|
||||||
return LettuceInstrumentationUtil.getCommandName(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String normalizeQuery(RedisCommand<?, ?, ?> redisCommand) {
|
|
||||||
String command = LettuceInstrumentationUtil.getCommandName(redisCommand);
|
String command = LettuceInstrumentationUtil.getCommandName(redisCommand);
|
||||||
List<String> args =
|
List<String> args =
|
||||||
redisCommand.getArgs() == null
|
redisCommand.getArgs() == null
|
||||||
|
@ -35,4 +29,10 @@ public class LettuceDatabaseClientTracer
|
||||||
: LettuceArgSplitter.splitArgs(redisCommand.getArgs().toCommandString());
|
: LettuceArgSplitter.splitArgs(redisCommand.getArgs().toCommandString());
|
||||||
return RedisCommandSanitizer.sanitize(command, args);
|
return RedisCommandSanitizer.sanitize(command, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String spanName(
|
||||||
|
RedisURI connection, RedisCommand<?, ?, ?> command, String sanitizedStatement) {
|
||||||
|
return LettuceInstrumentationUtil.getCommandName(command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import static java.util.Arrays.asList;
|
||||||
import com.mongodb.ServerAddress;
|
import com.mongodb.ServerAddress;
|
||||||
import com.mongodb.connection.ConnectionDescription;
|
import com.mongodb.connection.ConnectionDescription;
|
||||||
import com.mongodb.event.CommandStartedEvent;
|
import com.mongodb.event.CommandStartedEvent;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
|
||||||
|
@ -20,7 +20,6 @@ import java.lang.reflect.Method;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -30,7 +29,8 @@ import org.bson.BsonValue;
|
||||||
import org.bson.json.JsonWriter;
|
import org.bson.json.JsonWriter;
|
||||||
import org.bson.json.JsonWriterSettings;
|
import org.bson.json.JsonWriterSettings;
|
||||||
|
|
||||||
public class MongoClientTracer extends DatabaseClientTracer<CommandStartedEvent, BsonDocument> {
|
public class MongoClientTracer
|
||||||
|
extends DatabaseClientTracer<CommandStartedEvent, BsonDocument, String> {
|
||||||
private static final MongoClientTracer TRACER = new MongoClientTracer();
|
private static final MongoClientTracer TRACER = new MongoClientTracer();
|
||||||
|
|
||||||
private final int maxNormalizedQueryLength;
|
private final int maxNormalizedQueryLength;
|
||||||
|
@ -54,19 +54,34 @@ public class MongoClientTracer extends DatabaseClientTracer<CommandStartedEvent,
|
||||||
return "io.opentelemetry.javaagent.mongo";
|
return "io.opentelemetry.javaagent.mongo";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String sanitizeStatement(BsonDocument command) {
|
||||||
|
StringWriter stringWriter = new StringWriter(128);
|
||||||
|
writeScrubbed(command, new JsonWriter(stringWriter, jsonWriterSettings), true);
|
||||||
|
// If using MongoDB driver >= 3.7, the substring invocation will be a no-op due to use of
|
||||||
|
// JsonWriterSettings.Builder.maxLength in the static initializer for JSON_WRITER_SETTINGS
|
||||||
|
return stringWriter
|
||||||
|
.getBuffer()
|
||||||
|
.substring(0, Math.min(maxNormalizedQueryLength, stringWriter.getBuffer().length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String spanName(CommandStartedEvent event, BsonDocument document, String normalizedQuery) {
|
||||||
|
return conventionSpanName(dbName(event), event.getCommandName(), collectionName(event));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbSystem(CommandStartedEvent event) {
|
protected String dbSystem(CommandStartedEvent event) {
|
||||||
return DbSystemValues.MONGODB;
|
return DbSystemValues.MONGODB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Span onConnection(Span span, CommandStartedEvent event) {
|
protected void onConnection(SpanBuilder span, CommandStartedEvent event) {
|
||||||
span.setAttribute(SemanticAttributes.DB_OPERATION, event.getCommandName());
|
|
||||||
String collection = collectionName(event);
|
String collection = collectionName(event);
|
||||||
if (collection != null) {
|
if (collection != null) {
|
||||||
span.setAttribute(SemanticAttributes.DB_MONGODB_COLLECTION, collection);
|
span.setAttribute(SemanticAttributes.DB_MONGODB_COLLECTION, collection);
|
||||||
}
|
}
|
||||||
return super.onConnection(span, event);
|
super.onConnection(span, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -74,41 +89,6 @@ public class MongoClientTracer extends DatabaseClientTracer<CommandStartedEvent,
|
||||||
return event.getDatabaseName();
|
return event.getDatabaseName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected InetSocketAddress peerAddress(CommandStartedEvent event) {
|
|
||||||
if (event.getConnectionDescription() != null
|
|
||||||
&& event.getConnectionDescription().getServerAddress() != null) {
|
|
||||||
return event.getConnectionDescription().getServerAddress().getSocketAddress();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String spanName(CommandStartedEvent event, BsonDocument document, String normalizedQuery) {
|
|
||||||
String dbName = dbName(event);
|
|
||||||
if (event.getCommandName() == null) {
|
|
||||||
return dbName == null ? DB_QUERY : dbName;
|
|
||||||
}
|
|
||||||
|
|
||||||
String collectionName = collectionName(event);
|
|
||||||
StringBuilder name = new StringBuilder();
|
|
||||||
name.append(event.getCommandName());
|
|
||||||
if (dbName != null || collectionName != null) {
|
|
||||||
name.append(' ');
|
|
||||||
}
|
|
||||||
if (dbName != null) {
|
|
||||||
name.append(dbName);
|
|
||||||
if (collectionName != null) {
|
|
||||||
name.append('.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (collectionName != null) {
|
|
||||||
name.append(collectionName);
|
|
||||||
}
|
|
||||||
return name.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbConnectionString(CommandStartedEvent event) {
|
protected String dbConnectionString(CommandStartedEvent event) {
|
||||||
ConnectionDescription connectionDescription = event.getConnectionDescription();
|
ConnectionDescription connectionDescription = event.getConnectionDescription();
|
||||||
|
@ -126,6 +106,28 @@ public class MongoClientTracer extends DatabaseClientTracer<CommandStartedEvent,
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InetSocketAddress peerAddress(CommandStartedEvent event) {
|
||||||
|
if (event.getConnectionDescription() != null
|
||||||
|
&& event.getConnectionDescription().getServerAddress() != null) {
|
||||||
|
return event.getConnectionDescription().getServerAddress().getSocketAddress();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
CommandStartedEvent event, BsonDocument command, String sanitizedStatement) {
|
||||||
|
return sanitizedStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbOperation(
|
||||||
|
CommandStartedEvent event, BsonDocument command, String sanitizedStatement) {
|
||||||
|
return event.getCommandName();
|
||||||
|
}
|
||||||
|
|
||||||
private static final Method IS_TRUNCATED_METHOD;
|
private static final Method IS_TRUNCATED_METHOD;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -136,13 +138,6 @@ public class MongoClientTracer extends DatabaseClientTracer<CommandStartedEvent,
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The values of these mongo fields will not be scrubbed out. This allows the non-sensitive
|
|
||||||
* collection names to be captured.
|
|
||||||
*/
|
|
||||||
private static final List<String> UNSCRUBBED_FIELDS =
|
|
||||||
asList("ordered", "insert", "count", "find", "create");
|
|
||||||
|
|
||||||
private JsonWriterSettings createJsonWriterSettings(int maxNormalizedQueryLength) {
|
private JsonWriterSettings createJsonWriterSettings(int maxNormalizedQueryLength) {
|
||||||
JsonWriterSettings settings = null;
|
JsonWriterSettings settings = null;
|
||||||
try {
|
try {
|
||||||
|
@ -192,17 +187,6 @@ public class MongoClientTracer extends DatabaseClientTracer<CommandStartedEvent,
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String normalizeQuery(BsonDocument command) {
|
|
||||||
StringWriter stringWriter = new StringWriter(128);
|
|
||||||
writeScrubbed(command, new JsonWriter(stringWriter, jsonWriterSettings), true);
|
|
||||||
// If using MongoDB driver >= 3.7, the substring invocation will be a no-op due to use of
|
|
||||||
// JsonWriterSettings.Builder.maxLength in the static initializer for JSON_WRITER_SETTINGS
|
|
||||||
return stringWriter
|
|
||||||
.getBuffer()
|
|
||||||
.substring(0, Math.min(maxNormalizedQueryLength, stringWriter.getBuffer().length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String HIDDEN_CHAR = "?";
|
private static final String HIDDEN_CHAR = "?";
|
||||||
|
|
||||||
private static boolean writeScrubbed(BsonDocument origin, JsonWriter writer, boolean isRoot) {
|
private static boolean writeScrubbed(BsonDocument origin, JsonWriter writer, boolean isRoot) {
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import com.mongodb.event.CommandStartedEvent
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList
|
import static java.util.Arrays.asList
|
||||||
|
|
||||||
|
import com.mongodb.event.CommandStartedEvent
|
||||||
import io.opentelemetry.javaagent.instrumentation.mongo.MongoClientTracer
|
import io.opentelemetry.javaagent.instrumentation.mongo.MongoClientTracer
|
||||||
import org.bson.BsonArray
|
import org.bson.BsonArray
|
||||||
import org.bson.BsonDocument
|
import org.bson.BsonDocument
|
||||||
|
@ -15,21 +14,21 @@ import org.bson.BsonString
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
class MongoClientTracerTest extends Specification {
|
class MongoClientTracerTest extends Specification {
|
||||||
def 'should normalize queries to json'() {
|
def 'should sanitize statements to json'() {
|
||||||
setup:
|
setup:
|
||||||
def tracer = new MongoClientTracer()
|
def tracer = new MongoClientTracer()
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
normalizeQueryAcrossVersions(tracer,
|
sanitizeStatementAcrossVersions(tracer,
|
||||||
new BsonDocument("cmd", new BsonInt32(1))) ==
|
new BsonDocument("cmd", new BsonInt32(1))) ==
|
||||||
'{"cmd": "?"}'
|
'{"cmd": "?"}'
|
||||||
|
|
||||||
normalizeQueryAcrossVersions(tracer,
|
sanitizeStatementAcrossVersions(tracer,
|
||||||
new BsonDocument("cmd", new BsonInt32(1))
|
new BsonDocument("cmd", new BsonInt32(1))
|
||||||
.append("sub", new BsonDocument("a", new BsonInt32(1)))) ==
|
.append("sub", new BsonDocument("a", new BsonInt32(1)))) ==
|
||||||
'{"cmd": "?", "sub": {"a": "?"}}'
|
'{"cmd": "?", "sub": {"a": "?"}}'
|
||||||
|
|
||||||
normalizeQueryAcrossVersions(tracer,
|
sanitizeStatementAcrossVersions(tracer,
|
||||||
new BsonDocument("cmd", new BsonInt32(1))
|
new BsonDocument("cmd", new BsonInt32(1))
|
||||||
.append("sub", new BsonArray(asList(new BsonInt32(1))))) ==
|
.append("sub", new BsonArray(asList(new BsonInt32(1))))) ==
|
||||||
'{"cmd": "?", "sub": ["?"]}'
|
'{"cmd": "?", "sub": ["?"]}'
|
||||||
|
@ -40,7 +39,7 @@ class MongoClientTracerTest extends Specification {
|
||||||
def tracer = new MongoClientTracer()
|
def tracer = new MongoClientTracer()
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
normalizeQueryAcrossVersions(tracer,
|
sanitizeStatementAcrossVersions(tracer,
|
||||||
new BsonDocument("cmd", new BsonString("c"))
|
new BsonDocument("cmd", new BsonString("c"))
|
||||||
.append("f", new BsonString("c"))
|
.append("f", new BsonString("c"))
|
||||||
.append("sub", new BsonString("c"))) ==
|
.append("sub", new BsonString("c"))) ==
|
||||||
|
@ -51,7 +50,7 @@ class MongoClientTracerTest extends Specification {
|
||||||
setup:
|
setup:
|
||||||
def tracer = new MongoClientTracer(20)
|
def tracer = new MongoClientTracer(20)
|
||||||
|
|
||||||
def normalized = normalizeQueryAcrossVersions(tracer,
|
def normalized = sanitizeStatementAcrossVersions(tracer,
|
||||||
new BsonDocument("cmd", new BsonString("c"))
|
new BsonDocument("cmd", new BsonString("c"))
|
||||||
.append("f1", new BsonString("c1"))
|
.append("f1", new BsonString("c1"))
|
||||||
.append("f2", new BsonString("c2")))
|
.append("f2", new BsonString("c2")))
|
||||||
|
@ -64,7 +63,7 @@ class MongoClientTracerTest extends Specification {
|
||||||
setup:
|
setup:
|
||||||
def tracer = new MongoClientTracer(27)
|
def tracer = new MongoClientTracer(27)
|
||||||
|
|
||||||
def normalized = normalizeQueryAcrossVersions(tracer,
|
def normalized = sanitizeStatementAcrossVersions(tracer,
|
||||||
new BsonDocument("cmd", new BsonString("c"))
|
new BsonDocument("cmd", new BsonString("c"))
|
||||||
.append("f1", new BsonArray(Arrays.asList(new BsonString("c1"), new BsonString("c2"))))
|
.append("f1", new BsonArray(Arrays.asList(new BsonString("c1"), new BsonString("c2"))))
|
||||||
.append("f2", new BsonString("c3")))
|
.append("f2", new BsonString("c3")))
|
||||||
|
@ -89,11 +88,11 @@ class MongoClientTracerTest extends Specification {
|
||||||
command = "listDatabases"
|
command = "listDatabases"
|
||||||
}
|
}
|
||||||
|
|
||||||
def normalizeQueryAcrossVersions(MongoClientTracer tracer, BsonDocument query) {
|
def sanitizeStatementAcrossVersions(MongoClientTracer tracer, BsonDocument query) {
|
||||||
return normalizeAcrossVersions(tracer.normalizeQuery(query))
|
return sanitizeAcrossVersions(tracer.sanitizeStatement(query))
|
||||||
}
|
}
|
||||||
|
|
||||||
def normalizeAcrossVersions(String json) {
|
def sanitizeAcrossVersions(String json) {
|
||||||
json = json.replaceAll('\\{ ', '{')
|
json = json.replaceAll('\\{ ', '{')
|
||||||
json = json.replaceAll(' }', '}')
|
json = json.replaceAll(' }', '}')
|
||||||
json = json.replaceAll(' :', ':')
|
json = json.replaceAll(' :', ':')
|
||||||
|
|
|
@ -11,7 +11,7 @@ import java.net.InetSocketAddress;
|
||||||
import redis.RedisCommand;
|
import redis.RedisCommand;
|
||||||
|
|
||||||
public class RediscalaClientTracer
|
public class RediscalaClientTracer
|
||||||
extends DatabaseClientTracer<RedisCommand<?, ?>, RedisCommand<?, ?>> {
|
extends DatabaseClientTracer<RedisCommand<?, ?>, RedisCommand<?, ?>, String> {
|
||||||
|
|
||||||
private static final RediscalaClientTracer TRACER = new RediscalaClientTracer();
|
private static final RediscalaClientTracer TRACER = new RediscalaClientTracer();
|
||||||
|
|
||||||
|
@ -20,20 +20,32 @@ public class RediscalaClientTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(RedisCommand redisCommand) {
|
protected String sanitizeStatement(RedisCommand<?, ?> redisCommand) {
|
||||||
return spanNameForClass(redisCommand.getClass());
|
return spanNameForClass(redisCommand.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbSystem(RedisCommand redisCommand) {
|
protected String spanName(
|
||||||
|
RedisCommand<?, ?> connection, RedisCommand<?, ?> statement, String operation) {
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbSystem(RedisCommand<?, ?> redisCommand) {
|
||||||
return DbSystemValues.REDIS;
|
return DbSystemValues.REDIS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InetSocketAddress peerAddress(RedisCommand redisCommand) {
|
protected InetSocketAddress peerAddress(RedisCommand<?, ?> redisCommand) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
RedisCommand<?, ?> connection, RedisCommand<?, ?> command, String operation) {
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getInstrumentationName() {
|
protected String getInstrumentationName() {
|
||||||
return "io.opentelemetry.javaagent.rediscala";
|
return "io.opentelemetry.javaagent.rediscala";
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.redisson;
|
package io.opentelemetry.javaagent.instrumentation.redisson;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
|
@ -13,11 +16,13 @@ import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValu
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.redisson.client.RedisConnection;
|
import org.redisson.client.RedisConnection;
|
||||||
import org.redisson.client.protocol.CommandData;
|
import org.redisson.client.protocol.CommandData;
|
||||||
import org.redisson.client.protocol.CommandsData;
|
import org.redisson.client.protocol.CommandsData;
|
||||||
|
|
||||||
public class RedissonClientTracer extends DatabaseClientTracer<RedisConnection, Object> {
|
public class RedissonClientTracer
|
||||||
|
extends DatabaseClientTracer<RedisConnection, Object, List<String>> {
|
||||||
private static final String UNKNOWN_COMMAND = "Redis Command";
|
private static final String UNKNOWN_COMMAND = "Redis Command";
|
||||||
|
|
||||||
private static final RedissonClientTracer TRACER = new RedissonClientTracer();
|
private static final RedissonClientTracer TRACER = new RedissonClientTracer();
|
||||||
|
@ -27,22 +32,24 @@ public class RedissonClientTracer extends DatabaseClientTracer<RedisConnection,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String spanName(RedisConnection connection, Object query, String normalizedQuery) {
|
protected String spanName(
|
||||||
if (query instanceof CommandsData) {
|
RedisConnection connection, Object ignored, List<String> sanitizedStatements) {
|
||||||
List<CommandData<?, ?>> commands = ((CommandsData) query).getCommands();
|
switch (sanitizedStatements.size()) {
|
||||||
StringBuilder commandStrings = new StringBuilder();
|
case 0:
|
||||||
for (CommandData<?, ?> commandData : commands) {
|
return UNKNOWN_COMMAND;
|
||||||
commandStrings.append(commandData.getCommand().getName()).append(";");
|
// optimize for the most common case
|
||||||
}
|
case 1:
|
||||||
if (commandStrings.length() > 0) {
|
return getCommandName(sanitizedStatements.get(0));
|
||||||
commandStrings.deleteCharAt(commandStrings.length() - 1);
|
default:
|
||||||
}
|
return sanitizedStatements.stream()
|
||||||
return commandStrings.toString();
|
.map(this::getCommandName)
|
||||||
} else if (query instanceof CommandData) {
|
.collect(Collectors.joining(";"));
|
||||||
return ((CommandData<?, ?>) query).getCommand().getName();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return UNKNOWN_COMMAND;
|
private String getCommandName(String statement) {
|
||||||
|
int spacePos = statement.indexOf(' ');
|
||||||
|
return spacePos == -1 ? statement : statement.substring(0, spacePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,22 +58,15 @@ public class RedissonClientTracer extends DatabaseClientTracer<RedisConnection,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String normalizeQuery(Object command) {
|
protected List<String> sanitizeStatement(Object command) {
|
||||||
// get command
|
// get command
|
||||||
if (command instanceof CommandsData) {
|
if (command instanceof CommandsData) {
|
||||||
List<CommandData<?, ?>> commands = ((CommandsData) command).getCommands();
|
List<CommandData<?, ?>> commands = ((CommandsData) command).getCommands();
|
||||||
StringBuilder commandStrings = new StringBuilder();
|
return commands.stream().map(this::normalizeSingleCommand).collect(Collectors.toList());
|
||||||
for (CommandData<?, ?> commandData : commands) {
|
|
||||||
commandStrings.append(normalizeSingleCommand(commandData)).append(";");
|
|
||||||
}
|
|
||||||
if (commandStrings.length() > 0) {
|
|
||||||
commandStrings.deleteCharAt(commandStrings.length() - 1);
|
|
||||||
}
|
|
||||||
return commandStrings.toString();
|
|
||||||
} else if (command instanceof CommandData) {
|
} else if (command instanceof CommandData) {
|
||||||
return normalizeSingleCommand((CommandData<?, ?>) command);
|
return singletonList(normalizeSingleCommand((CommandData<?, ?>) command));
|
||||||
}
|
}
|
||||||
return UNKNOWN_COMMAND;
|
return emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String normalizeSingleCommand(CommandData<?, ?> command) {
|
private String normalizeSingleCommand(CommandData<?, ?> command) {
|
||||||
|
@ -110,4 +110,18 @@ public class RedissonClientTracer extends DatabaseClientTracer<RedisConnection,
|
||||||
InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress();
|
InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress();
|
||||||
return remoteAddress.getHostString() + ":" + remoteAddress.getPort();
|
return remoteAddress.getHostString() + ":" + remoteAddress.getPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbStatement(
|
||||||
|
RedisConnection connection, Object ignored, List<String> sanitizedStatements) {
|
||||||
|
switch (sanitizedStatements.size()) {
|
||||||
|
case 0:
|
||||||
|
return UNKNOWN_COMMAND;
|
||||||
|
// optimize for the most common case
|
||||||
|
case 1:
|
||||||
|
return sanitizedStatements.get(0);
|
||||||
|
default:
|
||||||
|
return String.join(";", sanitizedStatements);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,11 @@ public abstract class CompletionListener<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void closeSyncSpan(Throwable thrown) {
|
protected void closeSyncSpan(Throwable thrown) {
|
||||||
tracer().endExceptionally(context, thrown);
|
if (thrown == null) {
|
||||||
|
tracer().end(context);
|
||||||
|
} else {
|
||||||
|
tracer().endExceptionally(context, thrown);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void processResult(Span span, T future)
|
protected abstract void processResult(Span span, T future)
|
||||||
|
|
|
@ -5,19 +5,33 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.spymemcached;
|
package io.opentelemetry.javaagent.instrumentation.spymemcached;
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
|
||||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import net.spy.memcached.MemcachedConnection;
|
import net.spy.memcached.MemcachedConnection;
|
||||||
|
|
||||||
public class MemcacheClientTracer extends DatabaseClientTracer<MemcachedConnection, String> {
|
public class MemcacheClientTracer
|
||||||
|
extends DatabaseClientTracer<MemcachedConnection, String, String> {
|
||||||
private static final MemcacheClientTracer TRACER = new MemcacheClientTracer();
|
private static final MemcacheClientTracer TRACER = new MemcacheClientTracer();
|
||||||
|
|
||||||
public static MemcacheClientTracer tracer() {
|
public static MemcacheClientTracer tracer() {
|
||||||
return TRACER;
|
return TRACER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String sanitizeStatement(String methodName) {
|
||||||
|
char[] chars =
|
||||||
|
methodName
|
||||||
|
.replaceFirst("^async", "")
|
||||||
|
// 'CAS' name is special, we have to lowercase whole name
|
||||||
|
.replaceFirst("^CAS", "cas")
|
||||||
|
.toCharArray();
|
||||||
|
|
||||||
|
// Lowercase first letter
|
||||||
|
chars[0] = Character.toLowerCase(chars[0]);
|
||||||
|
|
||||||
|
return new String(chars);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String dbSystem(MemcachedConnection memcachedConnection) {
|
protected String dbSystem(MemcachedConnection memcachedConnection) {
|
||||||
return "memcached";
|
return "memcached";
|
||||||
|
@ -29,23 +43,9 @@ public class MemcacheClientTracer extends DatabaseClientTracer<MemcachedConnecti
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStatement(Span span, String statement) {
|
protected String dbOperation(
|
||||||
span.setAttribute(SemanticAttributes.DB_OPERATION, statement);
|
MemcachedConnection connection, String methodName, String operation) {
|
||||||
}
|
return operation;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String normalizeQuery(String query) {
|
|
||||||
char[] chars =
|
|
||||||
query
|
|
||||||
.replaceFirst("^async", "")
|
|
||||||
// 'CAS' name is special, we have to lowercase whole name
|
|
||||||
.replaceFirst("^CAS", "cas")
|
|
||||||
.toCharArray();
|
|
||||||
|
|
||||||
// Lowercase first letter
|
|
||||||
chars[0] = Character.toLowerCase(chars[0]);
|
|
||||||
|
|
||||||
return new String(chars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue