Skip to content

Commit

Permalink
Extract HTTP server experimental metrics to a separate class (#9259)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Rzeszutek committed Aug 30, 2023
1 parent bcd693f commit 133de24
Show file tree
Hide file tree
Showing 30 changed files with 956 additions and 447 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

### Migration notes

- Experimental HTTP server metrics have been split out from `HttpServerMetrics` into a separate
class `HttpServerExperimentalMetrics`
([#9259](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/9259))

## Version 1.29.0 (2023-08-17)

### Migration notes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/http-metrics.md#http-client">non-stable
* HTTP client metrics</a>: <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/http-metrics.md#metric-httpclientrequestsize">the
* request size </a> and the <a
* request size </a> and <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/http-metrics.md#metric-httpclientresponsesize">
* the response size</a>.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.instrumenter.http;

import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpRequestBodySize;
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpResponseBodySize;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView;
import static java.util.logging.Level.FINE;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
import java.util.logging.Logger;

/**
* {@link OperationListener} which keeps track of <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/http-metrics.md#http-server">non-stable
* HTTP server metrics</a>: <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-metrics.md#metric-httpserveractive_requests">the
* number of in-flight request</a>, <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-metrics.md#metric-httpserverrequestsize">the
* request size</a> and <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-metrics.md#metric-httpserverresponsesize">the
* response size</a>.
*/
public final class HttpServerExperimentalMetrics implements OperationListener {

private static final ContextKey<Attributes> HTTP_SERVER_EXPERIMENTAL_METRICS_START_ATTRIBUTES =
ContextKey.named("http-server-experimental-metrics-start-attributes");

private static final Logger logger =
Logger.getLogger(HttpServerExperimentalMetrics.class.getName());

/**
* Returns a {@link OperationMetrics} which can be used to enable recording of {@link
* HttpServerExperimentalMetrics} on an {@link
* io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder}.
*/
public static OperationMetrics get() {
return HttpServerExperimentalMetrics::new;
}

private final LongUpDownCounter activeRequests;
private final LongHistogram requestSize;
private final LongHistogram responseSize;

private HttpServerExperimentalMetrics(Meter meter) {
activeRequests =
meter
.upDownCounterBuilder("http.server.active_requests")
.setUnit("{requests}")
.setDescription("The number of concurrent HTTP requests that are currently in-flight")
.build();
requestSize =
meter
.histogramBuilder("http.server.request.size")
.setUnit("By")
.setDescription("The size of HTTP request messages")
.ofLongs()
.build();
responseSize =
meter
.histogramBuilder("http.server.response.size")
.setUnit("By")
.setDescription("The size of HTTP response messages")
.ofLongs()
.build();
}

@Override
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
activeRequests.add(1, applyActiveRequestsView(startAttributes), context);

return context.with(HTTP_SERVER_EXPERIMENTAL_METRICS_START_ATTRIBUTES, startAttributes);
}

@Override
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
Attributes startAttributes = context.get(HTTP_SERVER_EXPERIMENTAL_METRICS_START_ATTRIBUTES);
if (startAttributes == null) {
logger.log(
FINE,
"No state present when ending context {0}. Cannot record HTTP request metrics.",
context);
return;
}
// it's important to use exactly the same attributes that were used when incrementing the active
// request count (otherwise it will split the timeseries)
activeRequests.add(-1, applyActiveRequestsView(startAttributes), context);

Attributes sizeAttributes = applyServerDurationAndSizeView(startAttributes, endAttributes);

Long requestBodySize = getHttpRequestBodySize(endAttributes, startAttributes);
if (requestBodySize != null) {
requestSize.record(requestBodySize, sizeAttributes, context);
}

Long responseBodySize = getHttpResponseBodySize(endAttributes, startAttributes);
if (responseBodySize != null) {
responseSize.record(responseBodySize, sizeAttributes, context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,14 @@

package io.opentelemetry.instrumentation.api.instrumenter.http;

import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpRequestBodySize;
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpResponseBodySize;
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMetricsUtil.createDurationHistogram;
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMetricsUtil.nanosToUnit;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView;
import static java.util.logging.Level.FINE;

import com.google.auto.value.AutoValue;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
Expand All @@ -32,8 +27,8 @@
*/
public final class HttpServerMetrics implements OperationListener {

private static final ContextKey<State> HTTP_SERVER_REQUEST_METRICS_STATE =
ContextKey.named("http-server-request-metrics-state");
private static final ContextKey<State> HTTP_SERVER_METRICS_STATE =
ContextKey.named("http-server-metrics-state");

private static final Logger logger = Logger.getLogger(HttpServerMetrics.class.getName());

Expand All @@ -46,78 +41,39 @@ public static OperationMetrics get() {
return HttpServerMetrics::new;
}

private final LongUpDownCounter activeRequests;
private final DoubleHistogram duration;
private final LongHistogram requestSize;
private final LongHistogram responseSize;

private HttpServerMetrics(Meter meter) {
activeRequests =
meter
.upDownCounterBuilder("http.server.active_requests")
.setUnit("{requests}")
.setDescription("The number of concurrent HTTP requests that are currently in-flight")
.build();
String durationInstrumentName =
HttpMetricsUtil.emitNewSemconvMetrics
? "http.server.request.duration"
: "http.server.duration";
duration =
createDurationHistogram(
meter, durationInstrumentName, "The duration of the inbound HTTP request");
requestSize =
meter
.histogramBuilder("http.server.request.size")
.setUnit("By")
.setDescription("The size of HTTP request messages")
.ofLongs()
.build();
responseSize =
meter
.histogramBuilder("http.server.response.size")
.setUnit("By")
.setDescription("The size of HTTP response messages")
.ofLongs()
.build();
}

@Override
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
activeRequests.add(1, applyActiveRequestsView(startAttributes), context);

return context.with(
HTTP_SERVER_REQUEST_METRICS_STATE,
HTTP_SERVER_METRICS_STATE,
new AutoValue_HttpServerMetrics_State(startAttributes, startNanos));
}

@Override
public void onEnd(Context context, Attributes endAttributes, long endNanos) {
State state = context.get(HTTP_SERVER_REQUEST_METRICS_STATE);
State state = context.get(HTTP_SERVER_METRICS_STATE);
if (state == null) {
logger.log(
FINE,
"No state present when ending context {0}. Cannot record HTTP request metrics.",
context);
return;
}
// it's important to use exactly the same attributes that were used when incrementing the active
// request count (otherwise it will split the timeseries)
activeRequests.add(-1, applyActiveRequestsView(state.startAttributes()), context);

Attributes durationAndSizeAttributes =
applyServerDurationAndSizeView(state.startAttributes(), endAttributes);
duration.record(
nanosToUnit(endNanos - state.startTimeNanos()), durationAndSizeAttributes, context);

Long requestBodySize = getHttpRequestBodySize(endAttributes, state.startAttributes());
if (requestBodySize != null) {
requestSize.record(requestBodySize, durationAndSizeAttributes, context);
}

Long responseBodySize = getHttpResponseBodySize(endAttributes, state.startAttributes());
if (responseBodySize != null) {
responseSize.record(responseBodySize, durationAndSizeAttributes, context);
}
}

@AutoValue
Expand Down
Loading

0 comments on commit 133de24

Please sign in to comment.