Skip to content

Commit

Permalink
re-use logic for http client configuration (#11620)
Browse files Browse the repository at this point in the history
Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
  • Loading branch information
zeitlinger and trask authored Jun 21, 2024
1 parent 023091f commit 47ebb94
Show file tree
Hide file tree
Showing 31 changed files with 502 additions and 1,154 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.incubator.builder.internal;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.context.propagation.TextMapSetter;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceResolver;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nullable;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> {

private final String instrumentationName;
private final OpenTelemetry openTelemetry;

private final List<AttributesExtractor<? super REQUEST, ? super RESPONSE>> additionalExtractors =
new ArrayList<>();
private final HttpClientAttributesExtractorBuilder<REQUEST, RESPONSE>
httpAttributesExtractorBuilder;
private final HttpClientAttributesGetter<REQUEST, RESPONSE> attributesGetter;
private final HttpSpanNameExtractorBuilder<REQUEST> httpSpanNameExtractorBuilder;

@Nullable private TextMapSetter<REQUEST> headerSetter;
private Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
spanNameExtractorTransformer = Function.identity();
private boolean emitExperimentalHttpClientMetrics = false;
private Consumer<InstrumenterBuilder<REQUEST, RESPONSE>> builderCustomizer = b -> {};

public DefaultHttpClientInstrumenterBuilder(
String instrumentationName,
OpenTelemetry openTelemetry,
HttpClientAttributesGetter<REQUEST, RESPONSE> attributesGetter) {
this.instrumentationName = instrumentationName;
this.openTelemetry = openTelemetry;
httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(attributesGetter);
httpAttributesExtractorBuilder = HttpClientAttributesExtractor.builder(attributesGetter);
this.attributesGetter = attributesGetter;
}

/**
* Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
* items. The {@link AttributesExtractor} will be executed after all default extractors.
*/
@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> addAttributeExtractor(
AttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor) {
additionalExtractors.add(attributesExtractor);
return this;
}

/**
* Configures the HTTP request headers that will be captured as span attributes.
*
* @param requestHeaders A list of HTTP header names.
*/
@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setCapturedRequestHeaders(
List<String> requestHeaders) {
httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders);
return this;
}

/**
* Configures the HTTP response headers that will be captured as span attributes.
*
* @param responseHeaders A list of HTTP header names.
*/
@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setCapturedResponseHeaders(
List<String> responseHeaders) {
httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders);
return this;
}

/**
* Configures the instrumentation to recognize an alternative set of HTTP request methods.
*
* <p>By default, this instrumentation defines "known" methods as the ones listed in <a
* href="https://www.rfc-editor.org/rfc/rfc9110.html#name-methods">RFC9110</a> and the PATCH
* method defined in <a href="https://www.rfc-editor.org/rfc/rfc5789.html">RFC5789</a>.
*
* <p>Note: calling this method <b>overrides</b> the default known method sets completely; it does
* not supplement it.
*
* @param knownMethods A set of recognized HTTP request methods.
* @see HttpClientAttributesExtractorBuilder#setKnownMethods(Set)
*/
@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setKnownMethods(
Set<String> knownMethods) {
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
return this;
}

@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setHeaderSetter(
@Nullable TextMapSetter<REQUEST> headerSetter) {
this.headerSetter = headerSetter;
return this;
}

/**
* Configures the instrumentation to emit experimental HTTP client metrics.
*
* @param emitExperimentalHttpClientMetrics {@code true} if the experimental HTTP client metrics
* are to be emitted.
*/
@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE>
setEmitExperimentalHttpClientMetrics(boolean emitExperimentalHttpClientMetrics) {
this.emitExperimentalHttpClientMetrics = emitExperimentalHttpClientMetrics;
return this;
}

/** Sets custom {@link SpanNameExtractor} via transform function. */
@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setSpanNameExtractor(
Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
spanNameExtractorTransformer) {
this.spanNameExtractorTransformer = spanNameExtractorTransformer;
return this;
}

/** Sets custom {@link PeerServiceResolver}. */
@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setPeerServiceResolver(
PeerServiceResolver peerServiceResolver) {
return addAttributeExtractor(
HttpClientPeerServiceAttributesExtractor.create(attributesGetter, peerServiceResolver));
}

@CanIgnoreReturnValue
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setBuilderCustomizer(
Consumer<InstrumenterBuilder<REQUEST, RESPONSE>> builderCustomizer) {
this.builderCustomizer = builderCustomizer;
return this;
}

public Instrumenter<REQUEST, RESPONSE> build() {

SpanNameExtractor<? super REQUEST> spanNameExtractor =
spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build());

InstrumenterBuilder<REQUEST, RESPONSE> builder =
Instrumenter.<REQUEST, RESPONSE>builder(
openTelemetry, instrumentationName, spanNameExtractor)
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(attributesGetter))
.addAttributesExtractor(httpAttributesExtractorBuilder.build())
.addAttributesExtractors(additionalExtractors)
.addOperationMetrics(HttpClientMetrics.get());
if (emitExperimentalHttpClientMetrics) {
builder
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter))
.addOperationMetrics(HttpClientExperimentalMetrics.get());
}
builderCustomizer.accept(builder);

if (headerSetter != null) {
return builder.buildClientInstrumenter(headerSetter);
}
return builder.buildInstrumenter(SpanKindExtractor.alwaysClient());
}

public OpenTelemetry getOpenTelemetry() {
return openTelemetry;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,8 @@
import akka.http.scaladsl.model.HttpRequest;
import akka.http.scaladsl.model.HttpResponse;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters;
import io.opentelemetry.javaagent.instrumentation.akkahttp.AkkaHttpUtil;

public class AkkaHttpClientSingletons {
Expand All @@ -28,31 +19,9 @@ public class AkkaHttpClientSingletons {

static {
SETTER = new HttpHeaderSetter(GlobalOpenTelemetry.getPropagators());
AkkaHttpClientAttributesGetter httpAttributesGetter = new AkkaHttpClientAttributesGetter();
InstrumenterBuilder<HttpRequest, HttpResponse> builder =
Instrumenter.<HttpRequest, HttpResponse>builder(
GlobalOpenTelemetry.get(),
AkkaHttpUtil.instrumentationName(),
HttpSpanNameExtractor.builder(httpAttributesGetter)
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
.build())
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
.addAttributesExtractor(
HttpClientAttributesExtractor.builder(httpAttributesGetter)
.setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders())
.setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders())
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
.build())
.addAttributesExtractor(
HttpClientPeerServiceAttributesExtractor.create(
httpAttributesGetter, CommonConfig.get().getPeerServiceResolver()))
.addOperationMetrics(HttpClientMetrics.get());
if (CommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()) {
builder
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
.addOperationMetrics(HttpClientExperimentalMetrics.get());
}
INSTRUMENTER = builder.buildInstrumenter(SpanKindExtractor.alwaysClient());
INSTRUMENTER =
JavaagentHttpClientInstrumenters.create(
AkkaHttpUtil.instrumentationName(), new AkkaHttpClientAttributesGetter());
}

public static Instrumenter<HttpRequest, HttpResponse> instrumenter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,8 @@

package io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters;
import org.apache.http.HttpResponse;

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

static {
ApacheHttpAsyncClientHttpAttributesGetter httpAttributesGetter =
new ApacheHttpAsyncClientHttpAttributesGetter();

InstrumenterBuilder<ApacheHttpClientRequest, HttpResponse> builder =
Instrumenter.<ApacheHttpClientRequest, HttpResponse>builder(
GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME,
HttpSpanNameExtractor.builder(httpAttributesGetter)
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
.build())
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
.addAttributesExtractor(
HttpClientAttributesExtractor.builder(httpAttributesGetter)
.setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders())
.setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders())
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
.build())
.addAttributesExtractor(
HttpClientPeerServiceAttributesExtractor.create(
httpAttributesGetter, CommonConfig.get().getPeerServiceResolver()))
.addOperationMetrics(HttpClientMetrics.get());
if (CommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()) {
builder
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
.addOperationMetrics(HttpClientExperimentalMetrics.get());
}
INSTRUMENTER = builder.buildClientInstrumenter(HttpHeaderSetter.INSTANCE);
INSTRUMENTER =
JavaagentHttpClientInstrumenters.create(
INSTRUMENTATION_NAME,
new ApacheHttpAsyncClientHttpAttributesGetter(),
HttpHeaderSetter.INSTANCE);
}

public static Instrumenter<ApacheHttpClientRequest, HttpResponse> instrumenter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,8 @@

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

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor;
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters;
import org.apache.commons.httpclient.HttpMethod;

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

static {
ApacheHttpClientHttpAttributesGetter httpAttributesGetter =
new ApacheHttpClientHttpAttributesGetter();

InstrumenterBuilder<HttpMethod, HttpMethod> builder =
Instrumenter.<HttpMethod, HttpMethod>builder(
GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME,
HttpSpanNameExtractor.builder(httpAttributesGetter)
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
.build())
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
.addAttributesExtractor(
HttpClientAttributesExtractor.builder(httpAttributesGetter)
.setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders())
.setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders())
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
.build())
.addAttributesExtractor(
HttpClientPeerServiceAttributesExtractor.create(
httpAttributesGetter, CommonConfig.get().getPeerServiceResolver()))
.addOperationMetrics(HttpClientMetrics.get());
if (CommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()) {
builder
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter))
.addOperationMetrics(HttpClientExperimentalMetrics.get());
}
INSTRUMENTER = builder.buildClientInstrumenter(HttpHeaderSetter.INSTANCE);
INSTRUMENTER =
JavaagentHttpClientInstrumenters.create(
INSTRUMENTATION_NAME,
new ApacheHttpClientHttpAttributesGetter(),
HttpHeaderSetter.INSTANCE);
}

public static Instrumenter<HttpMethod, HttpMethod> instrumenter() {
Expand Down
Loading

0 comments on commit 47ebb94

Please sign in to comment.