Skip to content

Commit ec26d54

Browse files
zeitlingersteverao
andauthored
add lettuce db client metrics (#13032)
Co-authored-by: Steve Rao <[email protected]>
1 parent e30a90c commit ec26d54

File tree

6 files changed

+79
-17
lines changed

6 files changed

+79
-17
lines changed

instrumentation/jdbc/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jdbc/test/JdbcInstrumentationTest.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
import java.sql.SQLException;
5050
import java.sql.Statement;
5151
import java.util.ArrayList;
52-
import java.util.Arrays;
5352
import java.util.HashMap;
5453
import java.util.List;
5554
import java.util.Locale;
@@ -981,7 +980,7 @@ void testGetConnection(
981980
trace -> {
982981
List<Consumer<SpanDataAssert>> assertions =
983982
new ArrayList<>(
984-
Arrays.asList(
983+
asList(
985984
span1 -> span1.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
986985
span1 ->
987986
span1

instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetry.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.opentelemetry.api.trace.Tracer;
1111
import io.opentelemetry.api.trace.TracerBuilder;
1212
import io.opentelemetry.instrumentation.api.incubator.semconv.db.RedisCommandSanitizer;
13+
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
1314
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
1415

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

3233
private final Tracer tracer;
3334
private final RedisCommandSanitizer sanitizer;
35+
private final OperationListener metrics;
3436

35-
LettuceTelemetry(OpenTelemetry openTelemetry, boolean statementSanitizationEnabled) {
37+
LettuceTelemetry(
38+
OpenTelemetry openTelemetry,
39+
boolean statementSanitizationEnabled,
40+
OperationListener metrics) {
41+
this.metrics = metrics;
3642
TracerBuilder tracerBuilder = openTelemetry.tracerBuilder(INSTRUMENTATION_NAME);
3743
String version = EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME);
3844
if (version != null) {
@@ -47,6 +53,6 @@ public static LettuceTelemetryBuilder builder(OpenTelemetry openTelemetry) {
4753
* io.lettuce.core.resource.ClientResources.Builder#tracing(Tracing)}.
4854
*/
4955
public Tracing newTracing() {
50-
return new OpenTelemetryTracing(tracer, sanitizer);
56+
return new OpenTelemetryTracing(tracer, sanitizer, metrics);
5157
}
5258
}

instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/LettuceTelemetryBuilder.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55

66
package io.opentelemetry.instrumentation.lettuce.v5_1;
77

8+
import static io.opentelemetry.instrumentation.lettuce.v5_1.LettuceTelemetry.INSTRUMENTATION_NAME;
9+
810
import com.google.errorprone.annotations.CanIgnoreReturnValue;
911
import io.opentelemetry.api.OpenTelemetry;
12+
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics;
1013

1114
/** A builder of {@link LettuceTelemetry}. */
1215
public final class LettuceTelemetryBuilder {
@@ -36,6 +39,9 @@ public LettuceTelemetryBuilder setStatementSanitizationEnabled(
3639
* LettuceTelemetryBuilder}.
3740
*/
3841
public LettuceTelemetry build() {
39-
return new LettuceTelemetry(openTelemetry, statementSanitizationEnabled);
42+
return new LettuceTelemetry(
43+
openTelemetry,
44+
statementSanitizationEnabled,
45+
DbClientMetrics.get().create(openTelemetry.getMeterProvider().get(INSTRUMENTATION_NAME)));
4046
}
4147
}

instrumentation/lettuce/lettuce-5.1/library/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/OpenTelemetryTracing.java

+40-12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import io.opentelemetry.context.Context;
2828
import io.opentelemetry.instrumentation.api.incubator.semconv.db.RedisCommandSanitizer;
2929
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
30+
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
3031
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
3132
import io.opentelemetry.instrumentation.api.semconv.network.NetworkAttributesExtractor;
3233
import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor;
@@ -54,8 +55,11 @@ final class OpenTelemetryTracing implements Tracing {
5455
NetworkAttributesExtractor.create(new LettuceServerAttributesGetter());
5556
private final TracerProvider tracerProvider;
5657

57-
OpenTelemetryTracing(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
58-
this.tracerProvider = new OpenTelemetryTracerProvider(tracer, sanitizer);
58+
OpenTelemetryTracing(
59+
io.opentelemetry.api.trace.Tracer tracer,
60+
RedisCommandSanitizer sanitizer,
61+
OperationListener metrics) {
62+
this.tracerProvider = new OpenTelemetryTracerProvider(tracer, sanitizer, metrics);
5963
}
6064

6165
@Override
@@ -93,8 +97,10 @@ private static class OpenTelemetryTracerProvider implements TracerProvider {
9397
private final Tracer openTelemetryTracer;
9498

9599
OpenTelemetryTracerProvider(
96-
io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
97-
openTelemetryTracer = new OpenTelemetryTracer(tracer, sanitizer);
100+
io.opentelemetry.api.trace.Tracer tracer,
101+
RedisCommandSanitizer sanitizer,
102+
OperationListener metrics) {
103+
openTelemetryTracer = new OpenTelemetryTracer(tracer, sanitizer, metrics);
98104
}
99105

100106
@Override
@@ -135,10 +141,15 @@ private static class OpenTelemetryTracer extends Tracer {
135141

136142
private final io.opentelemetry.api.trace.Tracer tracer;
137143
private final RedisCommandSanitizer sanitizer;
144+
private final OperationListener metrics;
138145

139-
OpenTelemetryTracer(io.opentelemetry.api.trace.Tracer tracer, RedisCommandSanitizer sanitizer) {
146+
OpenTelemetryTracer(
147+
io.opentelemetry.api.trace.Tracer tracer,
148+
RedisCommandSanitizer sanitizer,
149+
OperationListener metrics) {
140150
this.tracer = tracer;
141151
this.sanitizer = sanitizer;
152+
this.metrics = metrics;
142153
}
143154

144155
@Override
@@ -165,7 +176,7 @@ private OpenTelemetrySpan nextSpan(Context context) {
165176
.setSpanKind(SpanKind.CLIENT)
166177
.setParent(context)
167178
.setAttribute(DB_SYSTEM, REDIS);
168-
return new OpenTelemetrySpan(context, spanBuilder, sanitizer);
179+
return new OpenTelemetrySpan(context, spanBuilder, sanitizer, metrics);
169180
}
170181
}
171182

@@ -178,18 +189,26 @@ private static class OpenTelemetrySpan extends Tracer.Span {
178189
private final Context context;
179190
private final SpanBuilder spanBuilder;
180191
private final RedisCommandSanitizer sanitizer;
192+
private final OperationListener metrics;
181193

182194
@Nullable private String name;
183195
@Nullable private List<Object> events;
184196
@Nullable private Throwable error;
185197
@Nullable private Span span;
198+
private long spanStartNanos;
199+
private final AttributesBuilder attributesBuilder = Attributes.builder().put(DB_SYSTEM, REDIS);
186200
@Nullable private List<String> argsList;
187201
@Nullable private String argsString;
188202

189-
OpenTelemetrySpan(Context context, SpanBuilder spanBuilder, RedisCommandSanitizer sanitizer) {
203+
OpenTelemetrySpan(
204+
Context context,
205+
SpanBuilder spanBuilder,
206+
RedisCommandSanitizer sanitizer,
207+
OperationListener metrics) {
190208
this.context = context;
191209
this.spanBuilder = spanBuilder;
192210
this.sanitizer = sanitizer;
211+
this.metrics = metrics;
193212
}
194213

195214
@Override
@@ -218,11 +237,13 @@ private void fillEndpoint(OpenTelemetryEndpoint endpoint) {
218237
Context currentContext = span == null ? context : context.with(span);
219238
serverAttributesExtractor.onStart(attributesBuilder, currentContext, endpoint);
220239
networkAttributesExtractor.onEnd(attributesBuilder, currentContext, endpoint, null, null);
240+
Attributes attributes = attributesBuilder.build();
221241
if (span != null) {
222-
span.setAllAttributes(attributesBuilder.build());
242+
span.setAllAttributes(attributes);
223243
} else {
224-
spanBuilder.setAllAttributes(attributesBuilder.build());
244+
spanBuilder.setAllAttributes(attributes);
225245
}
246+
this.attributesBuilder.putAll(attributes);
226247
}
227248

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

235257
Span span = this.span;
236258
if (span == null) {
@@ -258,7 +280,7 @@ public synchronized Tracer.Span start(RedisCommand<?, ?, ?> command) {
258280
}
259281
}
260282

261-
finish(span);
283+
finish(span, startNanos);
262284
});
263285
}
264286

@@ -270,6 +292,7 @@ public synchronized Tracer.Span start(RedisCommand<?, ?, ?> command) {
270292
@CanIgnoreReturnValue
271293
public synchronized Tracer.Span start() {
272294
span = spanBuilder.startSpan();
295+
spanStartNanos = System.nanoTime();
273296
if (name != null) {
274297
span.updateName(name);
275298
}
@@ -330,6 +353,7 @@ public synchronized Tracer.Span tag(String key, String value) {
330353
} else {
331354
spanBuilder.setAttribute(key, value);
332355
}
356+
attributesBuilder.put(key, value);
333357
return this;
334358
}
335359

@@ -347,16 +371,20 @@ public synchronized Tracer.Span error(Throwable throwable) {
347371
@Override
348372
public synchronized void finish() {
349373
if (span != null) {
350-
finish(span);
374+
finish(span, spanStartNanos);
351375
}
352376
}
353377

354-
private void finish(Span span) {
378+
private void finish(Span span, long startTime) {
355379
if (name != null) {
356380
String statement =
357381
sanitizer.sanitize(name, argsList != null ? argsList : splitArgs(argsString));
358382
if (SemconvStability.emitStableDatabaseSemconv()) {
359383
span.setAttribute(DB_QUERY_TEXT, statement);
384+
metrics.onEnd(
385+
metrics.onStart(Context.current(), Attributes.empty(), startTime),
386+
attributesBuilder.build(),
387+
System.nanoTime());
360388
}
361389
if (SemconvStability.emitOldDatabaseSemconv()) {
362390
span.setAttribute(DB_STATEMENT, statement);

instrumentation/lettuce/lettuce-5.1/testing/src/main/java/io/opentelemetry/instrumentation/lettuce/v5_1/AbstractLettuceSyncClientTest.java

+19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.instrumentation.lettuce.v5_1;
77

8+
import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric;
89
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
910
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
1011
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
@@ -13,9 +14,11 @@
1314
import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE;
1415
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
1516
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
17+
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAMESPACE;
1618
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT;
1719
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
1820
import static java.nio.charset.StandardCharsets.UTF_8;
21+
import static java.util.Arrays.asList;
1922
import static org.assertj.core.api.Assertions.assertThat;
2023
import static org.assertj.core.api.Assertions.catchThrowable;
2124

@@ -31,7 +34,9 @@
3134
import io.opentelemetry.instrumentation.test.utils.PortUtils;
3235
import java.net.InetAddress;
3336
import java.net.UnknownHostException;
37+
import java.util.ArrayList;
3438
import java.util.Base64;
39+
import java.util.List;
3540
import java.util.Map;
3641
import org.junit.jupiter.api.AfterAll;
3742
import org.junit.jupiter.api.BeforeAll;
@@ -135,6 +140,20 @@ void testSetCommand() {
135140
.hasEventsSatisfyingExactly(
136141
event -> event.hasName("redis.encode.start"),
137142
event -> event.hasName("redis.encode.end"))));
143+
144+
List<AttributeKey<?>> expected =
145+
new ArrayList<>(
146+
asList(
147+
DB_SYSTEM, SERVER_ADDRESS, SERVER_PORT, NETWORK_PEER_ADDRESS, NETWORK_PEER_PORT));
148+
if (Boolean.getBoolean("testLatestDeps")) {
149+
expected.add(DB_NAMESPACE);
150+
}
151+
assertDurationMetric(testing(), "io.opentelemetry.lettuce-5.1", toArray(expected));
152+
}
153+
154+
@SuppressWarnings("rawtypes")
155+
private static AttributeKey[] toArray(List<AttributeKey<?>> expected) {
156+
return expected.toArray(new AttributeKey[0]);
138157
}
139158

140159
@Test

testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/DbClientMetricsTestUtil.java

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv;
99
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
10+
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
1011

1112
import io.opentelemetry.api.common.AttributeKey;
1213
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
@@ -19,6 +20,9 @@ public static void assertDurationMetric(
1920
InstrumentationExtension testing,
2021
String instrumentationName,
2122
AttributeKey<?>... expectedKeys) {
23+
// db.system is required - see
24+
// https://opentelemetry.io/docs/specs/semconv/database/database-metrics/#metric-dbclientoperationduration
25+
assertThat(expectedKeys).extracting(AttributeKey::getKey).contains(DB_SYSTEM.getKey());
2226
if (!emitStableDatabaseSemconv()) {
2327
return;
2428
}

0 commit comments

Comments
 (0)