Skip to content
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

add lettuce db client metrics #13032

Merged
merged 15 commits into from
Jan 17, 2025
Next Next commit
add lettuce db client metrics
zeitlinger committed Jan 13, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 7716367c29b1c7b12714d6168e01f643f66b2012
Original file line number Diff line number Diff line change
@@ -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;
@@ -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();

Original file line number Diff line number Diff line change
@@ -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. */
@@ -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) {
@@ -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
@@ -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();
}

/**
@@ -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
@@ -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;
@@ -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
@@ -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
@@ -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
@@ -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);
}
}

@@ -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
@@ -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+
@@ -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) {
@@ -258,7 +280,7 @@ public synchronized Tracer.Span start(RedisCommand<?, ?, ?> command) {
}
}

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

@@ -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);
}
@@ -330,6 +353,7 @@ public synchronized Tracer.Span tag(String key, String value) {
} else {
spanBuilder.setAttribute(key, value);
}
attributesBuilder.put(key, value);
return this;
}

@@ -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);
Original file line number Diff line number Diff line change
@@ -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;
@@ -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;
@@ -135,6 +139,18 @@ 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);
//
// testing().waitAndAssertMetrics(metric ->
// OpenTelemetryAssertions.assertThat(metric)
// .hasName("db.client.operation.duration"));
}

@Test