Skip to content

Commit

Permalink
add rpc metrics conventions implement
Browse files Browse the repository at this point in the history
add rpc metrics conventions implement
  • Loading branch information
yangtaoran committed Dec 7, 2021
1 parent 735c1b0 commit 4ae816f
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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 metrics unnecessary attributes
*/
public class MetricsView {

private static final Set<AttributeKey> recommended = buildRecommended();
private static final Set<AttributeKey> optional = buildOptional();

private static Set<AttributeKey> 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<AttributeKey> view = new HashSet<>();
view.add(SemanticAttributes.RPC_SYSTEM);
view.add(SemanticAttributes.RPC_SERVICE);
view.add(SemanticAttributes.RPC_METHOD);
return view;
}

private static Set<AttributeKey> 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<AttributeKey> 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<AttributeKey> view) {
attributes.forEach(
(BiConsumer<AttributeKey, Object>)
(key, value) -> {
if (view.contains(key)) {
filtered.put(key, value);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -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<RpcClientMetrics.State> 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
*/
public static 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();
}
}
Original file line number Diff line number Diff line change
@@ -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<RpcServerMetrics.State> 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 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();

}
}


0 comments on commit 4ae816f

Please sign in to comment.