Skip to content

Commit 47ebb94

Browse files
zeitlingertrask
andauthored
re-use logic for http client configuration (#11620)
Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 023091f commit 47ebb94

File tree

31 files changed

+502
-1154
lines changed

31 files changed

+502
-1154
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
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.TextMapSetter;
11+
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
12+
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
13+
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
14+
import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceResolver;
15+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
16+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
17+
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
18+
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
19+
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
20+
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
21+
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
22+
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter;
23+
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
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.Set;
30+
import java.util.function.Consumer;
31+
import java.util.function.Function;
32+
import javax.annotation.Nullable;
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 DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> {
39+
40+
private final String instrumentationName;
41+
private final OpenTelemetry openTelemetry;
42+
43+
private final List<AttributesExtractor<? super REQUEST, ? super RESPONSE>> additionalExtractors =
44+
new ArrayList<>();
45+
private final HttpClientAttributesExtractorBuilder<REQUEST, RESPONSE>
46+
httpAttributesExtractorBuilder;
47+
private final HttpClientAttributesGetter<REQUEST, RESPONSE> attributesGetter;
48+
private final HttpSpanNameExtractorBuilder<REQUEST> httpSpanNameExtractorBuilder;
49+
50+
@Nullable private TextMapSetter<REQUEST> headerSetter;
51+
private Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
52+
spanNameExtractorTransformer = Function.identity();
53+
private boolean emitExperimentalHttpClientMetrics = false;
54+
private Consumer<InstrumenterBuilder<REQUEST, RESPONSE>> builderCustomizer = b -> {};
55+
56+
public DefaultHttpClientInstrumenterBuilder(
57+
String instrumentationName,
58+
OpenTelemetry openTelemetry,
59+
HttpClientAttributesGetter<REQUEST, RESPONSE> attributesGetter) {
60+
this.instrumentationName = instrumentationName;
61+
this.openTelemetry = openTelemetry;
62+
httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(attributesGetter);
63+
httpAttributesExtractorBuilder = HttpClientAttributesExtractor.builder(attributesGetter);
64+
this.attributesGetter = attributesGetter;
65+
}
66+
67+
/**
68+
* Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
69+
* items. The {@link AttributesExtractor} will be executed after all default extractors.
70+
*/
71+
@CanIgnoreReturnValue
72+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> addAttributeExtractor(
73+
AttributesExtractor<? super REQUEST, ? super 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 DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setCapturedRequestHeaders(
85+
List<String> requestHeaders) {
86+
httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders);
87+
return this;
88+
}
89+
90+
/**
91+
* Configures the HTTP response headers that will be captured as span attributes.
92+
*
93+
* @param responseHeaders A list of HTTP header names.
94+
*/
95+
@CanIgnoreReturnValue
96+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setCapturedResponseHeaders(
97+
List<String> responseHeaders) {
98+
httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders);
99+
return this;
100+
}
101+
102+
/**
103+
* Configures the instrumentation to recognize an alternative set of HTTP request methods.
104+
*
105+
* <p>By default, this instrumentation defines "known" methods as the ones listed in <a
106+
* href="https://www.rfc-editor.org/rfc/rfc9110.html#name-methods">RFC9110</a> and the PATCH
107+
* method defined in <a href="https://www.rfc-editor.org/rfc/rfc5789.html">RFC5789</a>.
108+
*
109+
* <p>Note: calling this method <b>overrides</b> the default known method sets completely; it does
110+
* not supplement it.
111+
*
112+
* @param knownMethods A set of recognized HTTP request methods.
113+
* @see HttpClientAttributesExtractorBuilder#setKnownMethods(Set)
114+
*/
115+
@CanIgnoreReturnValue
116+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setKnownMethods(
117+
Set<String> knownMethods) {
118+
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
119+
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
120+
return this;
121+
}
122+
123+
@CanIgnoreReturnValue
124+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setHeaderSetter(
125+
@Nullable TextMapSetter<REQUEST> headerSetter) {
126+
this.headerSetter = headerSetter;
127+
return this;
128+
}
129+
130+
/**
131+
* Configures the instrumentation to emit experimental HTTP client metrics.
132+
*
133+
* @param emitExperimentalHttpClientMetrics {@code true} if the experimental HTTP client metrics
134+
* are to be emitted.
135+
*/
136+
@CanIgnoreReturnValue
137+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE>
138+
setEmitExperimentalHttpClientMetrics(boolean emitExperimentalHttpClientMetrics) {
139+
this.emitExperimentalHttpClientMetrics = emitExperimentalHttpClientMetrics;
140+
return this;
141+
}
142+
143+
/** Sets custom {@link SpanNameExtractor} via transform function. */
144+
@CanIgnoreReturnValue
145+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setSpanNameExtractor(
146+
Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
147+
spanNameExtractorTransformer) {
148+
this.spanNameExtractorTransformer = spanNameExtractorTransformer;
149+
return this;
150+
}
151+
152+
/** Sets custom {@link PeerServiceResolver}. */
153+
@CanIgnoreReturnValue
154+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setPeerServiceResolver(
155+
PeerServiceResolver peerServiceResolver) {
156+
return addAttributeExtractor(
157+
HttpClientPeerServiceAttributesExtractor.create(attributesGetter, peerServiceResolver));
158+
}
159+
160+
@CanIgnoreReturnValue
161+
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setBuilderCustomizer(
162+
Consumer<InstrumenterBuilder<REQUEST, RESPONSE>> builderCustomizer) {
163+
this.builderCustomizer = builderCustomizer;
164+
return this;
165+
}
166+
167+
public Instrumenter<REQUEST, RESPONSE> build() {
168+
169+
SpanNameExtractor<? super REQUEST> spanNameExtractor =
170+
spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build());
171+
172+
InstrumenterBuilder<REQUEST, RESPONSE> builder =
173+
Instrumenter.<REQUEST, RESPONSE>builder(
174+
openTelemetry, instrumentationName, spanNameExtractor)
175+
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(attributesGetter))
176+
.addAttributesExtractor(httpAttributesExtractorBuilder.build())
177+
.addAttributesExtractors(additionalExtractors)
178+
.addOperationMetrics(HttpClientMetrics.get());
179+
if (emitExperimentalHttpClientMetrics) {
180+
builder
181+
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter))
182+
.addOperationMetrics(HttpClientExperimentalMetrics.get());
183+
}
184+
builderCustomizer.accept(builder);
185+
186+
if (headerSetter != null) {
187+
return builder.buildClientInstrumenter(headerSetter);
188+
}
189+
return builder.buildInstrumenter(SpanKindExtractor.alwaysClient());
190+
}
191+
192+
public OpenTelemetry getOpenTelemetry() {
193+
return openTelemetry;
194+
}
195+
}

instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/client/AkkaHttpClientSingletons.java

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,8 @@
88
import akka.http.scaladsl.model.HttpRequest;
99
import akka.http.scaladsl.model.HttpResponse;
1010
import io.opentelemetry.api.GlobalOpenTelemetry;
11-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
12-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
13-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
1411
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.semconv.http.HttpClientAttributesExtractor;
18-
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
19-
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
20-
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
21-
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
12+
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters;
2213
import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil;
2314

2415
public class AkkaHttpClientSingletons {
@@ -28,31 +19,9 @@ public class AkkaHttpClientSingletons {
2819

2920
static {
3021
SETTER = new HttpHeaderSetter(GlobalOpenTelemetry.getPropagators());
31-
AkkaHttpClientAttributesGetter httpAttributesGetter = new AkkaHttpClientAttributesGetter();
32-
InstrumenterBuilder<HttpRequest, HttpResponse> builder =
33-
Instrumenter.<HttpRequest, HttpResponse>builder(
34-
GlobalOpenTelemetry.get(),
35-
AkkaHttpUtil.instrumentationName(),
36-
HttpSpanNameExtractor.builder(httpAttributesGetter)
37-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
38-
.build())
39-
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
40-
.addAttributesExtractor(
41-
HttpClientAttributesExtractor.builder(httpAttributesGetter)
42-
.setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders())
43-
.setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders())
44-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
45-
.build())
46-
.addAttributesExtractor(
47-
HttpClientPeerServiceAttributesExtractor.create(
48-
httpAttributesGetter, CommonConfig.get().getPeerServiceResolver()))
49-
.addOperationMetrics(HttpClientMetrics.get());
50-
if (CommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()) {
51-
builder
52-
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
53-
.addOperationMetrics(HttpClientExperimentalMetrics.get());
54-
}
55-
INSTRUMENTER = builder.buildInstrumenter(SpanKindExtractor.alwaysClient());
22+
INSTRUMENTER =
23+
JavaagentHttpClientInstrumenters.create(
24+
AkkaHttpUtil.instrumentationName(), new AkkaHttpClientAttributesGetter());
5625
}
5726

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

instrumentation/apache-httpasyncclient-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientSingletons.java

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,8 @@
55

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

8-
import io.opentelemetry.api.GlobalOpenTelemetry;
9-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
10-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
11-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
128
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
13-
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
14-
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
15-
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
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.JavaagentHttpClientInstrumenters;
1910
import org.apache.http.HttpResponse;
2011

2112
public final class ApacheHttpAsyncClientSingletons {
@@ -24,33 +15,11 @@ public final class ApacheHttpAsyncClientSingletons {
2415
private static final Instrumenter<ApacheHttpClientRequest, HttpResponse> INSTRUMENTER;
2516

2617
static {
27-
ApacheHttpAsyncClientHttpAttributesGetter httpAttributesGetter =
28-
new ApacheHttpAsyncClientHttpAttributesGetter();
29-
30-
InstrumenterBuilder<ApacheHttpClientRequest, HttpResponse> builder =
31-
Instrumenter.<ApacheHttpClientRequest, HttpResponse>builder(
32-
GlobalOpenTelemetry.get(),
33-
INSTRUMENTATION_NAME,
34-
HttpSpanNameExtractor.builder(httpAttributesGetter)
35-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
36-
.build())
37-
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
38-
.addAttributesExtractor(
39-
HttpClientAttributesExtractor.builder(httpAttributesGetter)
40-
.setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders())
41-
.setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders())
42-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
43-
.build())
44-
.addAttributesExtractor(
45-
HttpClientPeerServiceAttributesExtractor.create(
46-
httpAttributesGetter, CommonConfig.get().getPeerServiceResolver()))
47-
.addOperationMetrics(HttpClientMetrics.get());
48-
if (CommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()) {
49-
builder
50-
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
51-
.addOperationMetrics(HttpClientExperimentalMetrics.get());
52-
}
53-
INSTRUMENTER = builder.buildClientInstrumenter(HttpHeaderSetter.INSTANCE);
18+
INSTRUMENTER =
19+
JavaagentHttpClientInstrumenters.create(
20+
INSTRUMENTATION_NAME,
21+
new ApacheHttpAsyncClientHttpAttributesGetter(),
22+
HttpHeaderSetter.INSTANCE);
5423
}
5524

5625
public static Instrumenter<ApacheHttpClientRequest, HttpResponse> instrumenter() {

instrumentation/apache-httpclient/apache-httpclient-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/apachehttpclient/v2_0/ApacheHttpClientSingletons.java

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,8 @@
55

66
package io.opentelemetry.javaagent.instrumentation.apachehttpclient.v2_0;
77

8-
import io.opentelemetry.api.GlobalOpenTelemetry;
9-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
10-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
11-
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
128
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
13-
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
14-
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
15-
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
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.JavaagentHttpClientInstrumenters;
1910
import org.apache.commons.httpclient.HttpMethod;
2011

2112
public final class ApacheHttpClientSingletons {
@@ -24,33 +15,11 @@ public final class ApacheHttpClientSingletons {
2415
private static final Instrumenter<HttpMethod, HttpMethod> INSTRUMENTER;
2516

2617
static {
27-
ApacheHttpClientHttpAttributesGetter httpAttributesGetter =
28-
new ApacheHttpClientHttpAttributesGetter();
29-
30-
InstrumenterBuilder<HttpMethod, HttpMethod> builder =
31-
Instrumenter.<HttpMethod, HttpMethod>builder(
32-
GlobalOpenTelemetry.get(),
33-
INSTRUMENTATION_NAME,
34-
HttpSpanNameExtractor.builder(httpAttributesGetter)
35-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
36-
.build())
37-
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
38-
.addAttributesExtractor(
39-
HttpClientAttributesExtractor.builder(httpAttributesGetter)
40-
.setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders())
41-
.setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders())
42-
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
43-
.build())
44-
.addAttributesExtractor(
45-
HttpClientPeerServiceAttributesExtractor.create(
46-
httpAttributesGetter, CommonConfig.get().getPeerServiceResolver()))
47-
.addOperationMetrics(HttpClientMetrics.get());
48-
if (CommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()) {
49-
builder
50-
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
51-
.addOperationMetrics(HttpClientExperimentalMetrics.get());
52-
}
53-
INSTRUMENTER = builder.buildClientInstrumenter(HttpHeaderSetter.INSTANCE);
18+
INSTRUMENTER =
19+
JavaagentHttpClientInstrumenters.create(
20+
INSTRUMENTATION_NAME,
21+
new ApacheHttpClientHttpAttributesGetter(),
22+
HttpHeaderSetter.INSTANCE);
5423
}
5524

5625
public static Instrumenter<HttpMethod, HttpMethod> instrumenter() {

0 commit comments

Comments
 (0)