Skip to content

Commit

Permalink
Split ArmeriaTelemetry into client and server (#12851)
Browse files Browse the repository at this point in the history
  • Loading branch information
trask authored Dec 11, 2024
1 parent de9c891 commit 1381f16
Show file tree
Hide file tree
Showing 13 changed files with 602 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import com.linecorp.armeria.server.HttpService;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig;
import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaTelemetry;
import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaTelemetryBuilder;
import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaClientTelemetry;
import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaClientTelemetryBuilder;
import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaServerTelemetry;
import io.opentelemetry.instrumentation.armeria.v1_3.ArmeriaServerTelemetryBuilder;
import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderUtil;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import java.util.function.Function;
Expand All @@ -23,15 +25,25 @@ public final class ArmeriaSingletons {
public static final Function<? super HttpService, ? extends HttpService> SERVER_DECORATOR;

static {
ArmeriaTelemetryBuilder builder = ArmeriaTelemetry.builder(GlobalOpenTelemetry.get());
CommonConfig config = AgentCommonConfig.get();
ArmeriaInstrumenterBuilderUtil.getClientBuilderExtractor().apply(builder).configure(config);
ArmeriaInstrumenterBuilderUtil.getServerBuilderExtractor().apply(builder).configure(config);
ArmeriaTelemetry telemetry = builder.build();

CLIENT_DECORATOR = telemetry.newClientDecorator();
ArmeriaClientTelemetryBuilder clientBuilder =
ArmeriaClientTelemetry.builder(GlobalOpenTelemetry.get());
ArmeriaInstrumenterBuilderUtil.getClientBuilderExtractor()
.apply(clientBuilder)
.configure(config);
ArmeriaClientTelemetry clientTelemetry = clientBuilder.build();

ArmeriaServerTelemetryBuilder serverBuilder =
ArmeriaServerTelemetry.builder(GlobalOpenTelemetry.get());
ArmeriaInstrumenterBuilderUtil.getServerBuilderExtractor()
.apply(serverBuilder)
.configure(config);
ArmeriaServerTelemetry serverTelemetry = serverBuilder.build();

CLIENT_DECORATOR = clientTelemetry.newDecorator();
Function<? super HttpService, ? extends HttpService> libraryDecorator =
telemetry.newServiceDecorator().compose(ResponseCustomizingDecorator::new);
serverTelemetry.newDecorator().compose(ResponseCustomizingDecorator::new);
SERVER_DECORATOR = service -> new ServerDecorator(service, libraryDecorator.apply(service));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.armeria.v1_3;

import com.linecorp.armeria.client.ClientRequestContext;
import com.linecorp.armeria.client.HttpClient;
import com.linecorp.armeria.common.logging.RequestLog;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import java.util.function.Function;

/** Entrypoint for instrumenting Armeria clients. */
public final class ArmeriaClientTelemetry {

/**
* Returns a new {@link ArmeriaClientTelemetry} configured with the given {@link OpenTelemetry}.
*/
public static ArmeriaClientTelemetry create(OpenTelemetry openTelemetry) {
return builder(openTelemetry).build();
}

public static ArmeriaClientTelemetryBuilder builder(OpenTelemetry openTelemetry) {
return new ArmeriaClientTelemetryBuilder(openTelemetry);
}

private final Instrumenter<ClientRequestContext, RequestLog> instrumenter;

ArmeriaClientTelemetry(Instrumenter<ClientRequestContext, RequestLog> instrumenter) {
this.instrumenter = instrumenter;
}

/**
* Returns a new {@link HttpClient} decorator for use with methods like {@link
* com.linecorp.armeria.client.ClientBuilder#decorator(Function)}.
*/
public Function<? super HttpClient, ? extends HttpClient> newDecorator() {
return client -> new OpenTelemetryClient(client, instrumenter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.armeria.v1_3;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.linecorp.armeria.client.ClientRequestContext;
import com.linecorp.armeria.common.logging.RequestLog;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderFactory;
import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderUtil;
import io.opentelemetry.instrumentation.armeria.v1_3.internal.Experimental;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

public final class ArmeriaClientTelemetryBuilder {

private final DefaultHttpClientInstrumenterBuilder<ClientRequestContext, RequestLog> builder;

static {
ArmeriaInstrumenterBuilderUtil.setClientBuilderExtractor(builder -> builder.builder);
Experimental.setSetEmitExperimentalClientTelemetry(
(builder, emit) -> builder.builder.setEmitExperimentalHttpClientMetrics(emit));
Experimental.setSetClientPeerService(
(builder, peerService) -> builder.builder.setPeerService(peerService));
}

ArmeriaClientTelemetryBuilder(OpenTelemetry openTelemetry) {
builder = ArmeriaInstrumenterBuilderFactory.getClientBuilder(openTelemetry);
}

/** Sets the status extractor for client spans. */
@CanIgnoreReturnValue
public ArmeriaClientTelemetryBuilder setStatusExtractor(
Function<
SpanStatusExtractor<? super ClientRequestContext, ? super RequestLog>,
? extends SpanStatusExtractor<? super ClientRequestContext, ? super RequestLog>>
statusExtractor) {
builder.setStatusExtractor(statusExtractor);
return this;
}

/**
* Adds an extra {@link AttributesExtractor} to invoke to set attributes to instrumented items.
* The {@link AttributesExtractor} will be executed after all default extractors.
*/
@CanIgnoreReturnValue
public ArmeriaClientTelemetryBuilder addAttributesExtractor(
AttributesExtractor<? super ClientRequestContext, ? super RequestLog> attributesExtractor) {
builder.addAttributesExtractor(attributesExtractor);
return this;
}

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

/**
* Configures the HTTP client response headers that will be captured as span attributes.
*
* @param responseHeaders A list of HTTP header names.
*/
@CanIgnoreReturnValue
public ArmeriaClientTelemetryBuilder setCapturedResponseHeaders(List<String> responseHeaders) {
builder.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 ArmeriaClientTelemetryBuilder setKnownMethods(Set<String> knownMethods) {
builder.setKnownMethods(knownMethods);
return this;
}

/** Sets custom client {@link SpanNameExtractor} via transform function. */
@CanIgnoreReturnValue
public ArmeriaClientTelemetryBuilder setSpanNameExtractor(
Function<
SpanNameExtractor<? super ClientRequestContext>,
? extends SpanNameExtractor<? super ClientRequestContext>>
clientSpanNameExtractor) {
builder.setSpanNameExtractor(clientSpanNameExtractor);
return this;
}

public ArmeriaClientTelemetry build() {
return new ArmeriaClientTelemetry(builder.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.armeria.v1_3;

import com.linecorp.armeria.common.logging.RequestLog;
import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.ServiceRequestContext;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import java.util.function.Function;

/** Entrypoint for instrumenting Armeria services. */
public final class ArmeriaServerTelemetry {

/**
* Returns a new {@link ArmeriaServerTelemetry} configured with the given {@link OpenTelemetry}.
*/
public static ArmeriaServerTelemetry create(OpenTelemetry openTelemetry) {
return builder(openTelemetry).build();
}

public static ArmeriaServerTelemetryBuilder builder(OpenTelemetry openTelemetry) {
return new ArmeriaServerTelemetryBuilder(openTelemetry);
}

private final Instrumenter<ServiceRequestContext, RequestLog> instrumenter;

ArmeriaServerTelemetry(Instrumenter<ServiceRequestContext, RequestLog> instrumenter) {
this.instrumenter = instrumenter;
}

/**
* Returns a new {@link HttpService} decorator for use with methods like {@link
* HttpService#decorate(Function)}.
*/
public Function<? super HttpService, ? extends HttpService> newDecorator() {
return service -> new OpenTelemetryService(service, instrumenter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.armeria.v1_3;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.linecorp.armeria.common.logging.RequestLog;
import com.linecorp.armeria.server.ServiceRequestContext;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder;
import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderFactory;
import io.opentelemetry.instrumentation.armeria.v1_3.internal.ArmeriaInstrumenterBuilderUtil;
import io.opentelemetry.instrumentation.armeria.v1_3.internal.Experimental;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

public final class ArmeriaServerTelemetryBuilder {

private final DefaultHttpServerInstrumenterBuilder<ServiceRequestContext, RequestLog> builder;

static {
ArmeriaInstrumenterBuilderUtil.setServerBuilderExtractor(builder -> builder.builder);
Experimental.setSetEmitExperimentalServerTelemetry(
(builder, emit) -> builder.builder.setEmitExperimentalHttpServerMetrics(emit));
}

ArmeriaServerTelemetryBuilder(OpenTelemetry openTelemetry) {
builder = ArmeriaInstrumenterBuilderFactory.getServerBuilder(openTelemetry);
}

/** Sets the status extractor for server spans. */
@CanIgnoreReturnValue
public ArmeriaServerTelemetryBuilder setStatusExtractor(
Function<
SpanStatusExtractor<? super ServiceRequestContext, ? super RequestLog>,
? extends SpanStatusExtractor<? super ServiceRequestContext, ? super RequestLog>>
statusExtractor) {
builder.setStatusExtractor(statusExtractor);
return this;
}

/**
* Adds an extra {@link AttributesExtractor} to invoke to set attributes to instrumented items.
* The {@link AttributesExtractor} will be executed after all default extractors.
*/
@CanIgnoreReturnValue
public ArmeriaServerTelemetryBuilder addAttributesExtractor(
AttributesExtractor<? super ServiceRequestContext, ? super RequestLog> attributesExtractor) {
builder.addAttributesExtractor(attributesExtractor);
return this;
}

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

/**
* Configures the HTTP server response headers that will be captured as span attributes.
*
* @param responseHeaders A list of HTTP header names.
*/
@CanIgnoreReturnValue
public ArmeriaServerTelemetryBuilder setCapturedResponseHeaders(List<String> responseHeaders) {
builder.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 HttpServerAttributesExtractorBuilder#setKnownMethods(Set)
*/
@CanIgnoreReturnValue
public ArmeriaServerTelemetryBuilder setKnownMethods(Set<String> knownMethods) {
builder.setKnownMethods(knownMethods);
return this;
}

/** Sets custom server {@link SpanNameExtractor} via transform function. */
@CanIgnoreReturnValue
public ArmeriaServerTelemetryBuilder setSpanNameExtractor(
Function<
SpanNameExtractor<? super ServiceRequestContext>,
? extends SpanNameExtractor<? super ServiceRequestContext>>
serverSpanNameExtractor) {
builder.setSpanNameExtractor(serverSpanNameExtractor);
return this;
}

public ArmeriaServerTelemetry build() {
return new ArmeriaServerTelemetry(builder.build());
}
}
Loading

0 comments on commit 1381f16

Please sign in to comment.