Spring web instrumenter 2 (#3731)
* Refactor spring-web library instrumentation to Instrumenter API * errorprone * fix typo
This commit is contained in:
parent
78a41261d9
commit
cbd8bb29fd
|
@ -6,7 +6,7 @@
|
|||
package io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.resttemplate;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.spring.httpclients.RestTemplateInterceptor;
|
||||
import io.opentelemetry.instrumentation.spring.web.SpringWebTracing;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
|
@ -29,17 +29,20 @@ final class RestTemplateBeanPostProcessor implements BeanPostProcessor {
|
|||
RestTemplate restTemplate = (RestTemplate) bean;
|
||||
OpenTelemetry openTelemetry = openTelemetryProvider.getIfUnique();
|
||||
if (openTelemetry != null) {
|
||||
addRestTemplateInterceptorIfNotPresent(restTemplate, openTelemetry);
|
||||
ClientHttpRequestInterceptor interceptor =
|
||||
SpringWebTracing.create(openTelemetry).newInterceptor();
|
||||
addRestTemplateInterceptorIfNotPresent(restTemplate, interceptor);
|
||||
}
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
private static void addRestTemplateInterceptorIfNotPresent(
|
||||
RestTemplate restTemplate, OpenTelemetry openTelemetry) {
|
||||
RestTemplate restTemplate, ClientHttpRequestInterceptor instrumentationInterceptor) {
|
||||
List<ClientHttpRequestInterceptor> restTemplateInterceptors = restTemplate.getInterceptors();
|
||||
if (restTemplateInterceptors.stream()
|
||||
.noneMatch(interceptor -> interceptor instanceof RestTemplateInterceptor)) {
|
||||
restTemplateInterceptors.add(0, new RestTemplateInterceptor(openTelemetry));
|
||||
.noneMatch(
|
||||
interceptor -> interceptor.getClass() == instrumentationInterceptor.getClass())) {
|
||||
restTemplateInterceptors.add(0, instrumentationInterceptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.spring.httpclients.RestTemplateInterceptor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -20,6 +19,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
|
@ -71,7 +71,7 @@ class RestTemplateBeanPostProcessorTest {
|
|||
|
||||
assertThat(
|
||||
restTemplate.getInterceptors().stream()
|
||||
.filter(rti -> rti instanceof RestTemplateInterceptor)
|
||||
.filter(RestTemplateBeanPostProcessorTest::isOtelInstrumentationInterceptor)
|
||||
.count())
|
||||
.isEqualTo(1);
|
||||
|
||||
|
@ -90,10 +90,14 @@ class RestTemplateBeanPostProcessorTest {
|
|||
|
||||
assertThat(
|
||||
restTemplate.getInterceptors().stream()
|
||||
.filter(rti -> rti instanceof RestTemplateInterceptor)
|
||||
.filter(RestTemplateBeanPostProcessorTest::isOtelInstrumentationInterceptor)
|
||||
.count())
|
||||
.isEqualTo(0);
|
||||
|
||||
verify(openTelemetryProvider, times(3)).getIfUnique();
|
||||
}
|
||||
|
||||
private static boolean isOtelInstrumentationInterceptor(ClientHttpRequestInterceptor rti) {
|
||||
return rti.getClass().getName().startsWith("io.opentelemetry.instrumentation");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,14 @@ Provides OpenTelemetry instrumentation for Spring's RestTemplate.
|
|||
Replace `SPRING_VERSION` with the version of spring you're using.
|
||||
`Minimum version: 3.1`
|
||||
|
||||
Replace `OPENTELEMETRY_VERSION` with the latest stable [release](https://mvnrepository.com/artifact/io.opentelemetry).
|
||||
`Minimum version: 0.17.0`
|
||||
Replace `OPENTELEMETRY_VERSION` with the latest
|
||||
stable [release](https://mvnrepository.com/artifact/io.opentelemetry).
|
||||
`Minimum version: 1.4.0`
|
||||
|
||||
For Maven add to your `pom.xml`:
|
||||
|
||||
```xml
|
||||
|
||||
<dependencies>
|
||||
<!-- opentelemetry -->
|
||||
<dependency>
|
||||
|
@ -22,8 +25,8 @@ For Maven add to your `pom.xml`:
|
|||
<version>OPENTELEMETRY_VERSION</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provides opentelemetry-sdk -->
|
||||
<dependency>
|
||||
<!-- provides opentelemetry-sdk -->
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporters-logging</artifactId>
|
||||
<version>OPENTELEMETRY_VERSION</version>
|
||||
|
@ -41,6 +44,7 @@ For Maven add to your `pom.xml`:
|
|||
```
|
||||
|
||||
For Gradle add to your dependencies:
|
||||
|
||||
```groovy
|
||||
implementation("io.opentelemetry.instrumentation:opentelemetry-spring-web-3.1:OPENTELEMETRY_VERSION")
|
||||
implementation("io.opentelemetry:opentelemetry-exporters-logging:OPENTELEMETRY_VERSION")
|
||||
|
@ -51,16 +55,17 @@ implementation("org.springframework:spring-web:SPRING_VERSION")
|
|||
|
||||
### Features
|
||||
|
||||
#### RestTemplateInterceptor
|
||||
#### Telemetry-producing `ClientHttpRequestInterceptor` implementation
|
||||
|
||||
RestTemplateInterceptor adds OpenTelemetry client spans to requests sent using RestTemplate by implementing the [ClientHttpRequestInterceptor](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/client/ClientHttpRequestInterceptor.html)
|
||||
interface. An example is shown below:
|
||||
`SpringWebTracing` allows creating a
|
||||
custom [ClientHttpRequestInterceptor](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/client/ClientHttpRequestInterceptor.html)
|
||||
that produces telemetry for HTTP requests sent using a `RestTemplate`. Example:
|
||||
|
||||
##### Usage
|
||||
|
||||
```java
|
||||
|
||||
import io.opentelemetry.instrumentation.spring.httpclients.RestTemplateInterceptor;
|
||||
import io.opentelemetry.instrumentation.spring.web.SpringWebTracing;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -72,18 +77,20 @@ import org.springframework.web.client.RestTemplate;
|
|||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(OpenTelemetry openTelemetry) {
|
||||
@Bean
|
||||
public RestTemplate restTemplate(OpenTelemetry openTelemetry) {
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
RestTemplateInterceptor restTemplateInterceptor = new RestTemplateInterceptor(openTelemetry);
|
||||
restTemplate.getInterceptors().add(restTemplateInterceptor);
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
SpringWebTracing springWebTracing = SpringWebTracing.create(openTelemetry);
|
||||
restTemplate.getInterceptors().add(springWebTracing.newInterceptor());
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
return restTemplate;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Starter Guide
|
||||
|
||||
Check out the opentelemetry [quick start](https://github.com/open-telemetry/opentelemetry-java/blob/master/QUICKSTART.md) to learn more about OpenTelemetry instrumentation.
|
||||
Check out the
|
||||
OpenTelemetry [quick start](https://github.com/open-telemetry/opentelemetry-java/blob/master/QUICKSTART.md)
|
||||
to learn more about OpenTelemetry instrumentation.
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.httpclients;
|
||||
|
||||
import io.opentelemetry.context.propagation.TextMapSetter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
class HttpHeadersInjectAdapter implements TextMapSetter<HttpHeaders> {
|
||||
|
||||
public static final HttpHeadersInjectAdapter SETTER = new HttpHeadersInjectAdapter();
|
||||
|
||||
@Override
|
||||
public void set(HttpHeaders carrier, String key, String value) {
|
||||
carrier.set(key, value);
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.httpclients;
|
||||
|
||||
import static io.opentelemetry.instrumentation.spring.httpclients.HttpHeadersInjectAdapter.SETTER;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.context.propagation.TextMapSetter;
|
||||
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
|
||||
import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
class RestTemplateTracer extends HttpClientTracer<HttpRequest, HttpHeaders, ClientHttpResponse> {
|
||||
RestTemplateTracer(OpenTelemetry openTelemetry) {
|
||||
super(openTelemetry, new NetPeerAttributes());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String method(HttpRequest httpRequest) {
|
||||
return httpRequest.getMethod().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI url(HttpRequest request) {
|
||||
return request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer status(ClientHttpResponse response) {
|
||||
try {
|
||||
return response.getStatusCode().value();
|
||||
} catch (IOException e) {
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR.value();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String requestHeader(HttpRequest request, String name) {
|
||||
return request.getHeaders().getFirst(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String responseHeader(ClientHttpResponse response, String name) {
|
||||
return response.getHeaders().getFirst(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextMapSetter<HttpHeaders> getSetter() {
|
||||
return SETTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInstrumentationName() {
|
||||
return "io.opentelemetry.spring-web-3.1";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.web;
|
||||
|
||||
import io.opentelemetry.context.propagation.TextMapSetter;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
final class HttpRequestSetter implements TextMapSetter<HttpRequest> {
|
||||
@Override
|
||||
public void set(HttpRequest httpRequest, String key, String value) {
|
||||
httpRequest.getHeaders().set(key, value);
|
||||
}
|
||||
}
|
|
@ -3,42 +3,40 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.httpclients;
|
||||
package io.opentelemetry.instrumentation.spring.web;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import java.io.IOException;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/** Wraps RestTemplate requests in a span. Adds the current span context to request headers. */
|
||||
public final class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
|
||||
final class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private final RestTemplateTracer tracer;
|
||||
private final Instrumenter<HttpRequest, ClientHttpResponse> instrumenter;
|
||||
|
||||
// TODO: create a SpringWebTracing class that follows the new library instrumentation pattern
|
||||
public RestTemplateInterceptor(OpenTelemetry openTelemetry) {
|
||||
this.tracer = new RestTemplateTracer(openTelemetry);
|
||||
RestTemplateInterceptor(Instrumenter<HttpRequest, ClientHttpResponse> instrumenter) {
|
||||
this.instrumenter = instrumenter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(
|
||||
HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||
Context parentContext = Context.current();
|
||||
if (!tracer.shouldStartSpan(parentContext)) {
|
||||
if (!instrumenter.shouldStart(parentContext, request)) {
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
|
||||
Context context = tracer.startSpan(parentContext, request, request.getHeaders());
|
||||
Context context = instrumenter.start(parentContext, request);
|
||||
try (Scope ignored = context.makeCurrent()) {
|
||||
ClientHttpResponse response = execution.execute(request, body);
|
||||
tracer.end(context, response);
|
||||
instrumenter.end(context, request, response, null);
|
||||
return response;
|
||||
} catch (Throwable t) {
|
||||
tracer.endExceptionally(context, t);
|
||||
instrumenter.end(context, request, null, t);
|
||||
throw t;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.web;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpAttributesExtractor;
|
||||
import java.io.IOException;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
final class SpringWebHttpAttributesExtractor
|
||||
extends HttpAttributesExtractor<HttpRequest, ClientHttpResponse> {
|
||||
@Override
|
||||
protected String method(HttpRequest httpRequest) {
|
||||
return httpRequest.getMethod().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String url(HttpRequest httpRequest) {
|
||||
return httpRequest.getURI().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String target(HttpRequest httpRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String host(HttpRequest httpRequest) {
|
||||
return httpRequest.getHeaders().getFirst("host");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String route(HttpRequest httpRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String scheme(HttpRequest httpRequest) {
|
||||
return httpRequest.getURI().getScheme();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String userAgent(HttpRequest httpRequest) {
|
||||
// using lowercase header name intentionally to ensure extraction is not case-sensitive
|
||||
return httpRequest.getHeaders().getFirst("user-agent");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Long requestContentLength(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Long requestContentLengthUncompressed(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String flavor(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String serverName(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String clientIp(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer statusCode(HttpRequest httpRequest, ClientHttpResponse clientHttpResponse) {
|
||||
try {
|
||||
return clientHttpResponse.getStatusCode().value();
|
||||
} catch (IOException e) {
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR.value();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Long responseContentLength(
|
||||
HttpRequest httpRequest, ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Long responseContentLengthUncompressed(
|
||||
HttpRequest httpRequest, ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.web;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
final class SpringWebNetAttributesExtractor
|
||||
extends NetAttributesExtractor<HttpRequest, ClientHttpResponse> {
|
||||
@Override
|
||||
public String transport(HttpRequest httpRequest) {
|
||||
return SemanticAttributes.NetTransportValues.IP_TCP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String peerName(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return httpRequest.getURI().getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer peerPort(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return httpRequest.getURI().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String peerIp(
|
||||
HttpRequest httpRequest, @Nullable ClientHttpResponse clientHttpResponse) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.web;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/** Entrypoint for tracing Spring {@link org.springframework.web.client.RestTemplate}. */
|
||||
public final class SpringWebTracing {
|
||||
|
||||
/** Returns a new {@link SpringWebTracing} configured with the given {@link OpenTelemetry}. */
|
||||
public static SpringWebTracing create(OpenTelemetry openTelemetry) {
|
||||
return newBuilder(openTelemetry).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link SpringWebTracingBuilder} configured with the given {@link OpenTelemetry}.
|
||||
*/
|
||||
public static SpringWebTracingBuilder newBuilder(OpenTelemetry openTelemetry) {
|
||||
return new SpringWebTracingBuilder(openTelemetry);
|
||||
}
|
||||
|
||||
private final Instrumenter<HttpRequest, ClientHttpResponse> instrumenter;
|
||||
|
||||
SpringWebTracing(Instrumenter<HttpRequest, ClientHttpResponse> instrumenter) {
|
||||
this.instrumenter = instrumenter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link ClientHttpRequestInterceptor} that can be used with {@link
|
||||
* RestTemplate#getInterceptors()}. For example:
|
||||
*
|
||||
* <pre>{@code
|
||||
* restTemplate.getInterceptors().add(SpringWebTracing.create(openTelemetry).newInterceptor());
|
||||
* }</pre>
|
||||
*/
|
||||
public ClientHttpRequestInterceptor newInterceptor() {
|
||||
return new RestTemplateInterceptor(instrumenter);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.web;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/** A builder of {@link SpringWebTracing}. */
|
||||
public final class SpringWebTracingBuilder {
|
||||
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-web-3.1";
|
||||
|
||||
private final OpenTelemetry openTelemetry;
|
||||
private final List<AttributesExtractor<HttpRequest, ClientHttpResponse>> additionalExtractors =
|
||||
new ArrayList<>();
|
||||
|
||||
SpringWebTracingBuilder(OpenTelemetry openTelemetry) {
|
||||
this.openTelemetry = openTelemetry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
|
||||
* items.
|
||||
*/
|
||||
public SpringWebTracingBuilder addAttributesExtractor(
|
||||
AttributesExtractor<HttpRequest, ClientHttpResponse> attributesExtractor) {
|
||||
additionalExtractors.add(attributesExtractor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link SpringWebTracing} with the settings of this {@link
|
||||
* SpringWebTracingBuilder}.
|
||||
*/
|
||||
public SpringWebTracing build() {
|
||||
SpringWebHttpAttributesExtractor httpAttributesExtractor =
|
||||
new SpringWebHttpAttributesExtractor();
|
||||
SpringWebNetAttributesExtractor netAttributesExtractor = new SpringWebNetAttributesExtractor();
|
||||
|
||||
Instrumenter<HttpRequest, ClientHttpResponse> instrumenter =
|
||||
Instrumenter.<HttpRequest, ClientHttpResponse>newBuilder(
|
||||
openTelemetry,
|
||||
INSTRUMENTATION_NAME,
|
||||
HttpSpanNameExtractor.create(httpAttributesExtractor))
|
||||
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesExtractor))
|
||||
.addAttributesExtractor(httpAttributesExtractor)
|
||||
.addAttributesExtractor(netAttributesExtractor)
|
||||
.addAttributesExtractors(additionalExtractors)
|
||||
.addRequestMetrics(HttpClientMetrics.get())
|
||||
.newClientInstrumenter(new HttpRequestSetter());
|
||||
|
||||
return new SpringWebTracing(instrumenter);
|
||||
}
|
||||
}
|
|
@ -3,10 +3,12 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.spring.httpclients.RestTemplateInterceptor
|
||||
import io.opentelemetry.api.common.AttributeKey
|
||||
import io.opentelemetry.instrumentation.spring.web.SpringWebTracing
|
||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||
import io.opentelemetry.instrumentation.test.base.HttpClientTest
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||
import org.springframework.http.HttpEntity
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpMethod
|
||||
|
@ -14,14 +16,14 @@ import org.springframework.web.client.ResourceAccessException
|
|||
import org.springframework.web.client.RestTemplate
|
||||
import spock.lang.Shared
|
||||
|
||||
class RestTemplateInstrumentationTest extends HttpClientTest<HttpEntity<String>> implements LibraryTestTrait {
|
||||
class SpringWebInstrumentationTest extends HttpClientTest<HttpEntity<String>> implements LibraryTestTrait {
|
||||
@Shared
|
||||
RestTemplate restTemplate
|
||||
|
||||
def setupSpec() {
|
||||
if (restTemplate == null) {
|
||||
restTemplate = new RestTemplate()
|
||||
restTemplate.getInterceptors().add(new RestTemplateInterceptor(getOpenTelemetry()))
|
||||
restTemplate.getInterceptors().add(SpringWebTracing.create(getOpenTelemetry()).newInterceptor())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,4 +68,12 @@ class RestTemplateInstrumentationTest extends HttpClientTest<HttpEntity<String>>
|
|||
boolean testWithClientParent() {
|
||||
false
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<AttributeKey<?>> httpAttributes(URI uri) {
|
||||
def attributes = super.httpAttributes(uri)
|
||||
attributes.remove(SemanticAttributes.HTTP_FLAVOR)
|
||||
attributes.add(SemanticAttributes.HTTP_SCHEME)
|
||||
attributes
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.httpclients;
|
||||
package io.opentelemetry.instrumentation.spring.web;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
|
@ -17,9 +17,10 @@ import org.mockito.Mock;
|
|||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RestTemplateInterceptorTest {
|
||||
class SpringWebTracingTest {
|
||||
@RegisterExtension
|
||||
static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
|
@ -30,7 +31,8 @@ class RestTemplateInterceptorTest {
|
|||
@Test
|
||||
void shouldSkipWhenContextHasClientSpan() throws Exception {
|
||||
// given
|
||||
RestTemplateInterceptor interceptor = new RestTemplateInterceptor(testing.getOpenTelemetry());
|
||||
ClientHttpRequestInterceptor interceptor =
|
||||
SpringWebTracing.create(testing.getOpenTelemetry()).newInterceptor();
|
||||
|
||||
// when
|
||||
testing.runWithClientSpan(
|
Loading…
Reference in New Issue