Skip to content

add lettuce db client metrics #13032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 17, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package io.opentelemetry.javaagent.instrumentation.lettuce.v5_1;

import static io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil.NOOP_OPERATION_LISTENER;

import io.lettuce.core.tracing.Tracing;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.lettuce.v5_1.LettuceTelemetry;
Expand All @@ -15,6 +17,9 @@ public final class TracingHolder {
public static final Tracing TRACING =
LettuceTelemetry.builder(GlobalOpenTelemetry.get())
.setStatementSanitizationEnabled(AgentCommonConfig.get().isStatementSanitizationEnabled())
.setMetrics(
meter ->
NOOP_OPERATION_LISTENER) // javaagent uses bytecode instrumentation for metrics
.build()
.newTracing();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.RedisCommandSanitizer;
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;

/** Entrypoint for instrumenting Lettuce or clients. */
Expand All @@ -31,8 +32,13 @@ public static LettuceTelemetryBuilder builder(OpenTelemetry openTelemetry) {

private final Tracer tracer;
private final RedisCommandSanitizer sanitizer;
private final OperationListener metrics;

LettuceTelemetry(OpenTelemetry openTelemetry, boolean statementSanitizationEnabled) {
LettuceTelemetry(
OpenTelemetry openTelemetry,
boolean statementSanitizationEnabled,
OperationListener metrics) {
this.metrics = metrics;
TracerBuilder tracerBuilder = openTelemetry.tracerBuilder(INSTRUMENTATION_NAME);
String version = EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME);
if (version != null) {
Expand All @@ -47,6 +53,6 @@ public static LettuceTelemetryBuilder builder(OpenTelemetry openTelemetry) {
* io.lettuce.core.resource.ClientResources.Builder#tracing(Tracing)}.
*/
public Tracing newTracing() {
return new OpenTelemetryTracing(tracer, sanitizer);
return new OpenTelemetryTracing(tracer, sanitizer, metrics);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@

package io.opentelemetry.instrumentation.lettuce.v5_1;

import static io.opentelemetry.instrumentation.lettuce.v5_1.LettuceTelemetry.INSTRUMENTATION_NAME;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;

/** A builder of {@link LettuceTelemetry}. */
public final class LettuceTelemetryBuilder {

private final OpenTelemetry openTelemetry;

private boolean statementSanitizationEnabled = true;
private OperationMetrics metrics;

LettuceTelemetryBuilder(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
metrics = DbClientMetrics.get();
}

/**
Expand All @@ -31,11 +37,20 @@ public LettuceTelemetryBuilder setStatementSanitizationEnabled(
return this;
}

@CanIgnoreReturnValue
public LettuceTelemetryBuilder setMetrics(OperationMetrics metrics) {
this.metrics = metrics;
return this;
}

/**
* Returns a new {@link LettuceTelemetry} with the settings of this {@link
* LettuceTelemetryBuilder}.
*/
public LettuceTelemetry build() {
return new LettuceTelemetry(openTelemetry, statementSanitizationEnabled);
return new LettuceTelemetry(
openTelemetry,
statementSanitizationEnabled,
metrics.create(openTelemetry.getMeterProvider().get(INSTRUMENTATION_NAME)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.RedisCommandSanitizer;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.api.semconv.network.NetworkAttributesExtractor;
import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor;
Expand Down Expand Up @@ -54,8 +55,11 @@ final class OpenTelemetryTracing implements Tracing {
NetworkAttributesExtractor.create(new LettuceServerAttributesGetter());
private final TracerProvider tracerProvider;

OpenTelemetryTracing(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
this.tracerProvider = new OpenTelemetryTracerProvider(tracer, sanitizer);
OpenTelemetryTracing(
io.opentelemetry.api.trace.Tracer tracer,
RedisCommandSanitizer sanitizer,
OperationListener metrics) {
this.tracerProvider = new OpenTelemetryTracerProvider(tracer, sanitizer, metrics);
}

@Override
Expand Down Expand Up @@ -93,8 +97,10 @@ private static class OpenTelemetryTracerProvider implements TracerProvider {
private final Tracer openTelemetryTracer;

OpenTelemetryTracerProvider(
io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
openTelemetryTracer = new OpenTelemetryTracer(tracer, sanitizer);
io.opentelemetry.api.trace.Tracer tracer,
RedisCommandSanitizer sanitizer,
OperationListener metrics) {
openTelemetryTracer = new OpenTelemetryTracer(tracer, sanitizer, metrics);
}

@Override
Expand Down Expand Up @@ -135,10 +141,15 @@ private static class OpenTelemetryTracer extends Tracer {

private final io.opentelemetry.api.trace.Tracer tracer;
private final RedisCommandSanitizer sanitizer;
private final OperationListener metrics;

OpenTelemetryTracer(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
OpenTelemetryTracer(
io.opentelemetry.api.trace.Tracer tracer,
RedisCommandSanitizer sanitizer,
OperationListener metrics) {
this.tracer = tracer;
this.sanitizer = sanitizer;
this.metrics = metrics;
}

@Override
Expand All @@ -165,7 +176,7 @@ private OpenTelemetrySpan nextSpan(Context context) {
.setSpanKind(SpanKind.CLIENT)
.setParent(context)
.setAttribute(DB_SYSTEM, REDIS);
return new OpenTelemetrySpan(context, spanBuilder, sanitizer);
return new OpenTelemetrySpan(context, spanBuilder, sanitizer, metrics);
}
}

Expand All @@ -178,18 +189,26 @@ private static class OpenTelemetrySpan extends Tracer.Span {
private final Context context;
private final SpanBuilder spanBuilder;
private final RedisCommandSanitizer sanitizer;
private final OperationListener metrics;

@Nullable private String name;
@Nullable private List<Object> events;
@Nullable private Throwable error;
@Nullable private Span span;
private long spanStartTime;
private final AttributesBuilder attributesBuilder = Attributes.builder();
@Nullable private List<String> argsList;
@Nullable private String argsString;

OpenTelemetrySpan(Context context, SpanBuilder spanBuilder, RedisCommandSanitizer sanitizer) {
OpenTelemetrySpan(
Context context,
SpanBuilder spanBuilder,
RedisCommandSanitizer sanitizer,
OperationListener metrics) {
this.context = context;
this.spanBuilder = spanBuilder;
this.sanitizer = sanitizer;
this.metrics = metrics;
}

@Override
Expand Down Expand Up @@ -218,11 +237,13 @@ private void fillEndpoint(OpenTelemetryEndpoint endpoint) {
Context currentContext = span == null ? context : context.with(span);
serverAttributesExtractor.onStart(attributesBuilder, currentContext, endpoint);
networkAttributesExtractor.onEnd(attributesBuilder, currentContext, endpoint, null, null);
Attributes attributes = attributesBuilder.build();
if (span != null) {
span.setAllAttributes(attributesBuilder.build());
span.setAllAttributes(attributes);
} else {
spanBuilder.setAllAttributes(attributesBuilder.build());
spanBuilder.setAllAttributes(attributes);
}
attributesBuilder.putAll(attributes);
}

// Added and called in 6.0+
Expand All @@ -231,6 +252,7 @@ private void fillEndpoint(OpenTelemetryEndpoint endpoint) {
@SuppressWarnings("UnusedMethod")
public synchronized Tracer.Span start(RedisCommand<?, ?, ?> command) {
start();
long startTime = System.nanoTime();

Span span = this.span;
if (span == null) {
Expand Down Expand Up @@ -258,7 +280,7 @@ public synchronized Tracer.Span start(RedisCommand<?, ?, ?> command) {
}
}

finish(span);
finish(span, startTime);
});
}

Expand All @@ -270,6 +292,7 @@ public synchronized Tracer.Span start(RedisCommand<?, ?, ?> command) {
@CanIgnoreReturnValue
public synchronized Tracer.Span start() {
span = spanBuilder.startSpan();
spanStartTime = System.nanoTime();
if (name != null) {
span.updateName(name);
}
Expand Down Expand Up @@ -330,6 +353,7 @@ public synchronized Tracer.Span tag(String key, String value) {
} else {
spanBuilder.setAttribute(key, value);
}
attributesBuilder.put(key, value);
return this;
}

Expand All @@ -347,16 +371,18 @@ public synchronized Tracer.Span error(Throwable throwable) {
@Override
public synchronized void finish() {
if (span != null) {
finish(span);
finish(span, spanStartTime);
}
}

private void finish(Span span) {
private void finish(Span span, long startTime) {
if (name != null) {
String statement =
sanitizer.sanitize(name, argsList != null ? argsList : splitArgs(argsString));
if (SemconvStability.emitStableDatabaseSemconv()) {
span.setAttribute(DB_QUERY_TEXT, statement);
Context c = metrics.onStart(Context.current(), Attributes.empty(), startTime);
metrics.onEnd(c, attributesBuilder.build(), System.nanoTime());
}
if (SemconvStability.emitOldDatabaseSemconv()) {
span.setAttribute(DB_STATEMENT, statement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.instrumentation.lettuce.v5_1;

import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
Expand All @@ -13,6 +14,9 @@
import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_COLLECTION_NAME;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAMESPACE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_NAME;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static java.nio.charset.StandardCharsets.UTF_8;
Expand Down Expand Up @@ -135,6 +139,14 @@ void testSetCommand() {
.hasEventsSatisfyingExactly(
event -> event.hasName("redis.encode.start"),
event -> event.hasName("redis.encode.end"))));

assertDurationMetric(
testing(),
"io.opentelemetry.lettuce-5.1",
DB_SYSTEM,
DB_COLLECTION_NAME,
DB_NAMESPACE,
DB_OPERATION_NAME);
}

@Test
Expand Down
Loading