From bc546ee92e789954578c71bec0fd7a8f2ce7d83b Mon Sep 17 00:00:00 2001 From: yangtaoran <614169791@qq.com> Date: Tue, 7 Dec 2021 16:46:26 +0800 Subject: [PATCH] add rpc metrics conventions implement add rpc metrics conventions implement fix AutoValue State fix AutoValue State update RpcClientMetrics Visibility update RpcClientMetrics Visibility update the filed serverDurationHistogram be final update the filed serverDurationHistogram be final update MetricsView code style update RpcClientMetrics code style update RpcServerMetrics code style update MetricsView doc update MetricsView doc --- .../api/instrumenter/rpc/MetricsView.java | 59 ++++++++++++++ .../instrumenter/rpc/RpcClientMetrics.java | 77 ++++++++++++++++++ .../instrumenter/rpc/RpcServerMetrics.java | 80 +++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java create mode 100644 instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetrics.java create mode 100644 instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetrics.java diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java new file mode 100644 index 000000000000..2cf5adf66df9 --- /dev/null +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/MetricsView.java @@ -0,0 +1,59 @@ +package io.opentelemetry.instrumentation.api.instrumenter.rpc; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.HashSet; +import java.util.Set; +import java.util.function.BiConsumer; + +/** + * filter rpc metrics unnecessary attributes. + */ +public class MetricsView { + + private static final Set recommended = buildRecommended(); + private static final Set optional = buildOptional(); + + private static Set buildRecommended() { + // the list of Recommended metrics attributes is from + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/rpc.md#attributes + Set view = new HashSet<>(); + view.add(SemanticAttributes.RPC_SYSTEM); + view.add(SemanticAttributes.RPC_SERVICE); + view.add(SemanticAttributes.RPC_METHOD); + return view; + } + + private static Set buildOptional() { + // the list of Recommended metrics attributes is from + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/rpc.md#attributes + Set view = new HashSet<>(); + view.add(SemanticAttributes.NET_PEER_IP); + view.add(SemanticAttributes.NET_PEER_NAME); + view.add(SemanticAttributes.NET_PEER_PORT); + view.add(SemanticAttributes.NET_TRANSPORT); + return view; + } + + static Attributes applyRpcView(Attributes startAttributes, Attributes endAttributes) { + Attributes attributes = startAttributes.toBuilder().putAll(endAttributes).build(); + AttributesBuilder filtered = Attributes.builder(); + applyView(filtered, attributes, recommended); + applyView(filtered, attributes, optional); + return filtered.build(); + } + + @SuppressWarnings("unchecked") + private static void applyView( + AttributesBuilder filtered, Attributes attributes, Set view) { + attributes.forEach( + (BiConsumer) + (key, value) -> { + if (view.contains(key)) { + filtered.put(key, value); + } + }); + } +} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetrics.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetrics.java new file mode 100644 index 000000000000..f38999f40505 --- /dev/null +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcClientMetrics.java @@ -0,0 +1,77 @@ +package io.opentelemetry.instrumentation.api.instrumenter.rpc; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.instrumentation.api.annotations.UnstableApi; +import io.opentelemetry.instrumentation.api.instrumenter.RequestListener; +import io.opentelemetry.instrumentation.api.instrumenter.RequestMetrics; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * guide from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/rpc.md#rpc-client + */ +@UnstableApi +public class RpcClientMetrics implements RequestListener { + + private static final ContextKey RPC_CLIENT_REQUEST_METRICS_STATE = + ContextKey.named("rpc-client-request-metrics-state"); + + private static final Logger logger = LoggerFactory.getLogger(RpcClientMetrics.class); + + /** + * measures duration of outbound RPC. + */ + private final LongHistogram clientDurationHistogram; + + private RpcClientMetrics(Meter meter) { + clientDurationHistogram = meter + .histogramBuilder("rpc.client.duration") + .setDescription("measures duration of outbound RPC") + .setUnit("milliseconds") + .ofLongs().build(); + } + + /** + * Returns a {@link RequestMetrics} which can be used to enable recording of {@link + * RpcClientMetrics} on an {@link + * io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder} + * method addRequestMetrics(). + */ + @UnstableApi + public static RequestMetrics get() { + return RpcClientMetrics::new; + } + + @Override + public Context start(Context context, Attributes startAttributes, long startNanos) { + return context.with(RPC_CLIENT_REQUEST_METRICS_STATE, + new AutoValue_RpcClientMetrics_State(startAttributes, startNanos)); + } + + @Override + public void end(Context context, Attributes endAttributes, long endNanos) { + State state = context.get(RPC_CLIENT_REQUEST_METRICS_STATE); + if (state == null) { + logger.debug( + "No state present when ending context {}. Cannot reset RPC request metrics.", context); + } + clientDurationHistogram.record( + TimeUnit.MILLISECONDS.convert( + endNanos - state.startTimeNanos(), TimeUnit.NANOSECONDS), + MetricsView.applyRpcView(state.startAttributes(), endAttributes), context); + } + + @AutoValue + abstract static class State { + + abstract Attributes startAttributes(); + + abstract long startTimeNanos(); + } +} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetrics.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetrics.java new file mode 100644 index 000000000000..41b3334a6cf6 --- /dev/null +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/rpc/RpcServerMetrics.java @@ -0,0 +1,80 @@ +package io.opentelemetry.instrumentation.api.instrumenter.rpc; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.instrumentation.api.annotations.UnstableApi; +import io.opentelemetry.instrumentation.api.instrumenter.RequestListener; +import io.opentelemetry.instrumentation.api.instrumenter.RequestMetrics; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * guide from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/rpc.md#rpc-server + */ +@UnstableApi +public class RpcServerMetrics implements RequestListener { + + private static final ContextKey RPC_SERVER_REQUEST_METRICS_STATE = + ContextKey.named("rpc-server-request-metrics-state"); + + private static final Logger logger = LoggerFactory.getLogger(RpcServerMetrics.class); + + /** + * measures duration of inbound RPC. + */ + private final LongHistogram serverDurationHistogram; + + private RpcServerMetrics(Meter meter) { + serverDurationHistogram = meter + .histogramBuilder("rpc.server.duration") + .setDescription("measures duration of inbound RPC") + .setUnit("milliseconds") + .ofLongs().build(); + } + + /** + * Returns a {@link RequestMetrics} which can be used to enable recording of {@link + * RpcServerMetrics} on an {@link + * io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder} + * method addRequestMetrics(). + */ + @UnstableApi + public static RequestMetrics get() { + return RpcServerMetrics::new; + } + + @Override + public Context start(Context context, Attributes startAttributes, long startNanos) { + return context.with(RPC_SERVER_REQUEST_METRICS_STATE, + new AutoValue_RpcServerMetrics_State(startAttributes, startNanos)); + } + + @Override + public void end(Context context, Attributes endAttributes, long endNanos) { + State state = context.get(RPC_SERVER_REQUEST_METRICS_STATE); + if (state == null) { + logger.debug( + "No state present when ending context {}. Cannot reset RPC request metrics.", context); + } + serverDurationHistogram.record( + TimeUnit.MILLISECONDS.convert( + endNanos - state.startTimeNanos(), TimeUnit.NANOSECONDS), + MetricsView.applyRpcView(state.startAttributes(), endAttributes), context); + } + + @AutoValue + abstract static class State { + + abstract Attributes startAttributes(); + + abstract long startTimeNanos(); + + } +} + +