Skip to content

Commit 712dc6c

Browse files
committed
add http server builder
1 parent b92a5bd commit 712dc6c

File tree

22 files changed

+425
-560
lines changed

22 files changed

+425
-560
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.builder.internal;
7+
8+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
9+
import io.opentelemetry.api.OpenTelemetry;
10+
import io.opentelemetry.context.propagation.TextMapGetter;
11+
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
12+
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics;
13+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
14+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
15+
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
16+
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
17+
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
18+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor;
19+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder;
20+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesGetter;
21+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics;
22+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
23+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteBuilder;
24+
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
25+
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder;
26+
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
27+
import java.util.ArrayList;
28+
import java.util.List;
29+
import java.util.Optional;
30+
import java.util.Set;
31+
import java.util.function.Consumer;
32+
import java.util.function.Function;
33+
34+
/**
35+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
36+
* any time.
37+
*/
38+
public final class DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> {
39+
40+
private final String instrumentationName;
41+
private final OpenTelemetry openTelemetry;
42+
43+
private final List<AttributesExtractor<REQUEST, RESPONSE>> additionalExtractors =
44+
new ArrayList<>();
45+
private final HttpServerAttributesExtractorBuilder<REQUEST, RESPONSE>
46+
httpAttributesExtractorBuilder;
47+
private final HttpSpanNameExtractorBuilder<REQUEST> httpSpanNameExtractorBuilder;
48+
private Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
49+
spanNameExtractorTransformer = Function.identity();
50+
private final HttpServerRouteBuilder<REQUEST> httpServerRouteBuilder;
51+
private final HttpServerAttributesGetter<REQUEST, RESPONSE> attributesGetter;
52+
private final Optional<TextMapGetter<REQUEST>> headerSetter;
53+
private boolean emitExperimentalHttpServerMetrics = false;
54+
55+
public DefaultHttpServerTelemetryBuilder(
56+
String instrumentationName, OpenTelemetry openTelemetry, HttpServerAttributesGetter<REQUEST, RESPONSE> attributesGetter,
57+
Optional<TextMapGetter<REQUEST>> headerSetter) {
58+
this.instrumentationName = instrumentationName;
59+
this.openTelemetry = openTelemetry;
60+
httpAttributesExtractorBuilder = HttpServerAttributesExtractor.builder(attributesGetter);
61+
httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(attributesGetter);
62+
httpServerRouteBuilder = HttpServerRoute.builder(attributesGetter);
63+
this.attributesGetter = attributesGetter;
64+
this.headerSetter = headerSetter;
65+
}
66+
67+
/**
68+
* Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
69+
* items.
70+
*/
71+
@CanIgnoreReturnValue
72+
public DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> addAttributesExtractor(
73+
AttributesExtractor<REQUEST, RESPONSE> attributesExtractor) {
74+
additionalExtractors.add(attributesExtractor);
75+
return this;
76+
}
77+
78+
/**
79+
* Configures the HTTP request headers that will be captured as span attributes.
80+
*
81+
* @param requestHeaders A list of HTTP header names.
82+
*/
83+
@CanIgnoreReturnValue
84+
public DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> setCapturedRequestHeaders(List<String> requestHeaders) {
85+
httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders);
86+
return this;
87+
}
88+
89+
/**
90+
* Configures the HTTP response headers that will be captured as span attributes.
91+
*
92+
* @param responseHeaders A list of HTTP header names.
93+
*/
94+
@CanIgnoreReturnValue
95+
public DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> setCapturedResponseHeaders(List<String> responseHeaders) {
96+
httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders);
97+
return this;
98+
}
99+
100+
/**
101+
* Configures the instrumentation to recognize an alternative set of HTTP request methods.
102+
*
103+
* <p>By default, this instrumentation defines "known" methods as the ones listed in <a
104+
* href="https://www.rfc-editor.org/rfc/rfc9110.html#name-methods">RFC9110</a> and the PATCH
105+
* method defined in <a href="https://www.rfc-editor.org/rfc/rfc5789.html">RFC5789</a>.
106+
*
107+
* <p>Note: calling this method <b>overrides</b> the default known method sets completely; it does
108+
* not supplement it.
109+
*
110+
* @param knownMethods A set of recognized HTTP request methods.
111+
* @see HttpServerAttributesExtractorBuilder#setKnownMethods(Set)
112+
*/
113+
@CanIgnoreReturnValue
114+
public DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> setKnownMethods(Set<String> knownMethods) {
115+
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
116+
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
117+
httpServerRouteBuilder.setKnownMethods(knownMethods);
118+
return this;
119+
}
120+
121+
/**
122+
* Configures the instrumentation to emit experimental HTTP server metrics.
123+
*
124+
* @param emitExperimentalHttpServerMetrics {@code true} if the experimental HTTP server metrics
125+
* are to be emitted.
126+
*/
127+
@CanIgnoreReturnValue
128+
public DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> setEmitExperimentalHttpServerMetrics(
129+
boolean emitExperimentalHttpServerMetrics) {
130+
this.emitExperimentalHttpServerMetrics = emitExperimentalHttpServerMetrics;
131+
return this;
132+
}
133+
134+
/** Sets custom {@link SpanNameExtractor} via transform function. */
135+
@CanIgnoreReturnValue
136+
public DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> setSpanNameExtractor(
137+
Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
138+
spanNameExtractorTransformer) {
139+
this.spanNameExtractorTransformer = spanNameExtractorTransformer;
140+
return this;
141+
}
142+
143+
public Instrumenter<REQUEST, RESPONSE> instrumenter() {
144+
return instrumenter(b -> {});
145+
}
146+
147+
public Instrumenter<REQUEST, RESPONSE> instrumenter(
148+
Consumer<InstrumenterBuilder<REQUEST, RESPONSE>> instrumenterBuilderConsumer) {
149+
150+
SpanNameExtractor<? super REQUEST> spanNameExtractor =
151+
spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build());
152+
153+
InstrumenterBuilder<REQUEST, RESPONSE> builder =
154+
Instrumenter.<REQUEST, RESPONSE>builder(
155+
openTelemetry, instrumentationName, spanNameExtractor)
156+
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(attributesGetter))
157+
.addAttributesExtractor(httpAttributesExtractorBuilder.build())
158+
.addAttributesExtractors(additionalExtractors)
159+
.addContextCustomizer(httpServerRouteBuilder.build())
160+
.addOperationMetrics(HttpServerMetrics.get());
161+
if (emitExperimentalHttpServerMetrics) {
162+
builder
163+
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter))
164+
.addOperationMetrics(HttpServerExperimentalMetrics.get());
165+
}
166+
167+
instrumenterBuilderConsumer.accept(builder);
168+
169+
if (headerSetter.isPresent()) {
170+
return builder.buildServerInstrumenter(headerSetter.get());
171+
}
172+
return builder.buildInstrumenter(SpanKindExtractor.alwaysServer());
173+
}
174+
175+
public OpenTelemetry getOpenTelemetry() {
176+
return openTelemetry;
177+
}
178+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.builder.internal;
7+
8+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
9+
import io.opentelemetry.instrumentation.api.incubator.config.internal.CoreCommonConfig;
10+
import java.lang.reflect.Field;
11+
import java.util.function.Consumer;
12+
import java.util.function.Supplier;
13+
14+
/**
15+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
16+
* any time.
17+
*/
18+
public class HttpServerInstrumenterBuilder {
19+
private HttpServerInstrumenterBuilder() {}
20+
21+
@CanIgnoreReturnValue
22+
public static <REQUEST, RESPONSE> DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> configure(
23+
CoreCommonConfig config, Object builder) {
24+
DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> defaultBuilder = unwrapBuilder(builder);
25+
set(config::getKnownHttpRequestMethods, defaultBuilder::setKnownMethods);
26+
set(config::getServerRequestHeaders, defaultBuilder::setCapturedRequestHeaders);
27+
set(config::getServerResponseHeaders, defaultBuilder::setCapturedResponseHeaders);
28+
set(
29+
config::shouldEmitExperimentalHttpServerTelemetry,
30+
defaultBuilder::setEmitExperimentalHttpServerMetrics);
31+
return defaultBuilder;
32+
}
33+
34+
private static <T> void set(Supplier<T> supplier, Consumer<T> consumer) {
35+
T t = supplier.get();
36+
if (t != null) {
37+
consumer.accept(t);
38+
}
39+
}
40+
41+
/**
42+
* This method is used to access the builder field of the builder object.
43+
*
44+
* <p>This approach allows us to re-use the existing builder classes from the library modules
45+
*/
46+
@SuppressWarnings("unchecked")
47+
private static <REQUEST, RESPONSE>
48+
DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE> unwrapBuilder(Object builder) {
49+
if (builder instanceof DefaultHttpServerTelemetryBuilder<?, ?>) {
50+
return (DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE>) builder;
51+
}
52+
try {
53+
Field field = builder.getClass().getDeclaredField("serverBuilder");
54+
field.setAccessible(true);
55+
return (DefaultHttpServerTelemetryBuilder<REQUEST, RESPONSE>) field.get(builder);
56+
} catch (Exception e) {
57+
throw new IllegalStateException("Could not access builder field", e);
58+
}
59+
}
60+
}

instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,50 +7,20 @@
77

88
import akka.http.scaladsl.model.HttpRequest;
99
import akka.http.scaladsl.model.HttpResponse;
10-
import io.opentelemetry.api.GlobalOpenTelemetry;
11-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
12-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics;
1310
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
14-
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
15-
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor;
16-
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics;
17-
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
18-
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
19-
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
20-
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
11+
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpServerInstrumenterBuilder;
2112
import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil;
13+
import java.util.Optional;
2214

2315
public final class AkkaHttpServerSingletons {
2416

2517
private static final Instrumenter<HttpRequest, HttpResponse> INSTRUMENTER;
2618

2719
static {
28-
AkkaHttpServerAttributesGetter httpAttributesGetter = new AkkaHttpServerAttributesGetter();
29-
InstrumenterBuilder<HttpRequest, HttpResponse> builder =
30-
Instrumenter.<HttpRequest, HttpResponse>builder(
31-
GlobalOpenTelemetry.get(),
32-
AkkaHttpUtil.instrumentationName(),
33-
HttpSpanNameExtractor.builder(httpAttributesGetter)
34-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
35-
.build())
36-
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
37-
.addAttributesExtractor(
38-
HttpServerAttributesExtractor.builder(httpAttributesGetter)
39-
.setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders())
40-
.setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders())
41-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
42-
.build())
43-
.addOperationMetrics(HttpServerMetrics.get())
44-
.addContextCustomizer(
45-
HttpServerRoute.builder(httpAttributesGetter)
46-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
47-
.build());
48-
if (CommonConfig.get().shouldEmitExperimentalHttpServerTelemetry()) {
49-
builder
50-
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
51-
.addOperationMetrics(HttpServerExperimentalMetrics.get());
52-
}
53-
INSTRUMENTER = builder.buildServerInstrumenter(AkkaHttpServerHeaders.INSTANCE);
20+
INSTRUMENTER = JavaagentHttpServerInstrumenterBuilder.create(
21+
AkkaHttpUtil.instrumentationName(),
22+
new AkkaHttpServerAttributesGetter(),
23+
Optional.of(AkkaHttpServerHeaders.INSTANCE));
5424
}
5525

5626
public static Instrumenter<HttpRequest, HttpResponse> instrumenter() {

instrumentation/grizzly-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,10 @@
55

66
package io.opentelemetry.javaagent.instrumentation.grizzly;
77

8-
import io.opentelemetry.api.GlobalOpenTelemetry;
9-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
10-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics;
118
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
12-
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
13-
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor;
14-
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics;
15-
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
16-
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
17-
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
18-
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
9+
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpServerInstrumenterBuilder;
1910
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
11+
import java.util.Optional;
2012
import org.glassfish.grizzly.http.HttpRequestPacket;
2113
import org.glassfish.grizzly.http.HttpResponsePacket;
2214

@@ -25,43 +17,21 @@ public final class GrizzlySingletons {
2517
private static final Instrumenter<HttpRequestPacket, HttpResponsePacket> INSTRUMENTER;
2618

2719
static {
28-
GrizzlyHttpAttributesGetter httpAttributesGetter = new GrizzlyHttpAttributesGetter();
29-
30-
InstrumenterBuilder<HttpRequestPacket, HttpResponsePacket> builder =
31-
Instrumenter.<HttpRequestPacket, HttpResponsePacket>builder(
32-
GlobalOpenTelemetry.get(),
33-
"io.opentelemetry.grizzly-2.3",
34-
HttpSpanNameExtractor.builder(httpAttributesGetter)
35-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
36-
.build())
37-
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
38-
.addAttributesExtractor(
39-
HttpServerAttributesExtractor.builder(httpAttributesGetter)
40-
.setCapturedRequestHeaders(CommonConfig.get().getServerRequestHeaders())
41-
.setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders())
42-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
43-
.build())
44-
.addOperationMetrics(HttpServerMetrics.get());
45-
if (CommonConfig.get().shouldEmitExperimentalHttpServerTelemetry()) {
46-
builder
47-
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
48-
.addOperationMetrics(HttpServerExperimentalMetrics.get());
49-
}
50-
INSTRUMENTER =
51-
builder
52-
.addContextCustomizer(
53-
(context, request, attributes) ->
54-
new AppServerBridge.Builder()
55-
.captureServletAttributes()
56-
.recordException()
57-
.init(context))
58-
.addContextCustomizer(
59-
(context, httpRequestPacket, startAttributes) -> GrizzlyErrorHolder.init(context))
60-
.addContextCustomizer(
61-
HttpServerRoute.builder(httpAttributesGetter)
62-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
63-
.build())
64-
.buildServerInstrumenter(HttpRequestHeadersGetter.INSTANCE);
20+
INSTRUMENTER = JavaagentHttpServerInstrumenterBuilder.createWithCustomizer(
21+
"io.opentelemetry.grizzly-2.3",
22+
new GrizzlyHttpAttributesGetter(),
23+
Optional.of(HttpRequestHeadersGetter.INSTANCE),
24+
builder ->
25+
builder
26+
.addContextCustomizer(
27+
(context, request, attributes) ->
28+
new AppServerBridge.Builder()
29+
.captureServletAttributes()
30+
.recordException()
31+
.init(context))
32+
.addContextCustomizer(
33+
(context, httpRequestPacket, startAttributes) -> GrizzlyErrorHolder.init(
34+
context)));
6535
}
6636

6737
public static Instrumenter<HttpRequestPacket, HttpResponsePacket> instrumenter() {

0 commit comments

Comments
 (0)