Migrated to OTel 0.10.0

This commit is contained in:
Marcin Grzejszczak
2020-11-06 14:59:11 +01:00
parent cf8e7dd04a
commit 06d353f155
167 changed files with 2750 additions and 1282 deletions

View File

@@ -41,7 +41,11 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<artifactId>spring-cloud-starter-sleuth-otel</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -562,7 +562,7 @@ For example if you want to use the OpenTelemetry Zipkin Exporter just add a depe
----
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporters-zipkin</artifactId>
<artifactId>opentelemetry-exporter-zipkin</artifactId>
</dependency>
----
@@ -570,7 +570,7 @@ For example if you want to use the OpenTelemetry Zipkin Exporter just add a depe
.Gradle
----
dependencies {
compile "io.opentelemetry:opentelemetry-exporters-zipkin"
compile "io.opentelemetry:opentelemetry-exporter-zipkin"
}
----
====

97
pom.xml
View File

@@ -57,6 +57,43 @@
<module>docs</module>
</modules>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.testTarget>1.8</maven.compiler.testTarget>
<maven.compiler.testSource>1.8</maven.compiler.testSource>
<spring-cloud-build.version>3.0.0-SNAPSHOT</spring-cloud-build.version>
<spring-cloud-commons.version>3.0.0-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-gateway.version>3.0.0-SNAPSHOT</spring-cloud-gateway.version>
<spring-cloud-circuitbreaker.version>2.0.0-SNAPSHOT</spring-cloud-circuitbreaker.version>
<spring-cloud-stream.version>3.1.0-SNAPSHOT</spring-cloud-stream.version>
<spring-cloud-function.version>3.1.0-SNAPSHOT</spring-cloud-function.version>
<spring-cloud-netflix.version>3.0.0-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>3.0.0-SNAPSHOT</spring-cloud-openfeign.version>
<brave.version>5.12.7</brave.version>
<opentracing.version>0.32.0</opentracing.version>
<opentelemetry.version>0.10.0</opentelemetry.version>
<!-- From maven local -->
<opentelemetry-instrumentation.version>0.10.0</opentelemetry-instrumentation.version>
<spring-security-boot-autoconfigure.version>2.3.4.RELEASE</spring-security-boot-autoconfigure.version>
<disable.nohttp.checks>false</disable.nohttp.checks>
<okhttp.version>4.9.0</okhttp.version>
<mockwebserver.version>4.8.0</mockwebserver.version>
<guava.version>20.0</guava.version>
<javax.resource-api.version>1.7.1</javax.resource-api.version>
<cglib-nodep.version>3.3.0</cglib-nodep.version>
<objenesis.version>3.0.1</objenesis.version>
<spring-security-oauth2.version>2.2.0.RELEASE</spring-security-oauth2.version>
<!-- Until we switch it to true in sc-build -->
<javadoc.failOnError>true</javadoc.failOnError>
<javadoc.failOnWarnings>false</javadoc.failOnWarnings>
<commons-lang3.version>3.8.1</commons-lang3.version>
<hamcrest-core.version>2.2</hamcrest-core.version>
<awaitility.version>4.0.3</awaitility.version>
<brave-propagation-aws.version>0.21.3</brave-propagation-aws.version>
</properties>
<build>
<pluginManagement>
<plugins>
@@ -129,6 +166,9 @@
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-report-plugin</artifactId>
</plugin>
</plugins>
</reporting>
@@ -197,19 +237,6 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>${spock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>${spock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
@@ -260,51 +287,9 @@
<version>${awaitility.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core-java8</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.testTarget>1.8</maven.compiler.testTarget>
<maven.compiler.testSource>1.8</maven.compiler.testSource>
<spring-cloud-build.version>3.0.0-SNAPSHOT</spring-cloud-build.version>
<spring-cloud-commons.version>3.0.0-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-gateway.version>3.0.0-SNAPSHOT</spring-cloud-gateway.version>
<spring-cloud-circuitbreaker.version>2.0.0-SNAPSHOT</spring-cloud-circuitbreaker.version>
<spring-cloud-stream.version>3.1.0-SNAPSHOT</spring-cloud-stream.version>
<spring-cloud-function.version>3.1.0-SNAPSHOT</spring-cloud-function.version>
<spring-cloud-netflix.version>3.0.0-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>3.0.0-SNAPSHOT</spring-cloud-openfeign.version>
<brave.version>5.12.7</brave.version>
<opentracing.version>0.32.0</opentracing.version>
<opentelemetry.version>0.9.1</opentelemetry.version>
<!-- From maven local -->
<opentelemetry-instrumentation.version>0.9.0-SNAPSHOT</opentelemetry-instrumentation.version>
<spring-security-boot-autoconfigure.version>2.3.4.RELEASE</spring-security-boot-autoconfigure.version>
<disable.nohttp.checks>false</disable.nohttp.checks>
<okhttp.version>4.9.0</okhttp.version>
<mockwebserver.version>4.8.0</mockwebserver.version>
<guava.version>20.0</guava.version>
<javax.resource-api.version>1.7.1</javax.resource-api.version>
<cglib-nodep.version>3.3.0</cglib-nodep.version>
<objenesis.version>3.0.1</objenesis.version>
<spring-security-oauth2.version>2.2.0.RELEASE</spring-security-oauth2.version>
<!-- Until we switch it to true in sc-build -->
<javadoc.failOnError>true</javadoc.failOnError>
<javadoc.failOnWarnings>false</javadoc.failOnWarnings>
<commons-lang3.version>3.8.1</commons-lang3.version>
<hamcrest-core.version>2.2</hamcrest-core.version>
<awaitility.version>4.0.3</awaitility.version>
<brave-propagation-aws.version>0.21.3</brave-propagation-aws.version>
</properties>
<repositories>
<repository>
<snapshots>
@@ -349,7 +334,7 @@
<enabled>false</enabled>
</releases>
</repository>
<!-- ONLY FOR BRAVE -->
<!-- BRAVE & OTEL -->
<repository>
<id>jfrog-snapshots</id>
<name>JFrog Snapshots</name>

View File

@@ -39,6 +39,7 @@ import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.api.exporter.SpanFilter;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.brave.LocalServiceName;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager;
import org.springframework.cloud.sleuth.brave.propagation.TraceBravePropagationAutoConfiguration;
import org.springframework.cloud.sleuth.brave.sampler.SamplerAutoConfiguration;
import org.springframework.cloud.sleuth.internal.DefaultSpanNamer;
@@ -82,6 +83,12 @@ public class TraceBraveAutoConfiguration {
*/
public static final String DEFAULT_SERVICE_NAME = "default";
@Bean
@ConditionalOnMissingBean
BraveBaggageManager braveBaggageManager() {
return new BraveBaggageManager();
}
@Bean(name = TRACING_BEAN_NAME)
@ConditionalOnMissingBean
// NOTE: stable bean name as might be used outside sleuth

View File

@@ -18,20 +18,20 @@ package org.springframework.cloud.sleuth.brave.bridge;
import brave.baggage.BaggageField;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.TraceContext;
/**
* Brave implementation of a {@link BaggageEntry}.
* Brave implementation of a {@link BaggageInScope}.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class BraveBaggageEntry implements BaggageEntry {
public class BraveBaggageInScope implements BaggageInScope {
private final BaggageField delegate;
public BraveBaggageEntry(BaggageField delegate) {
public BraveBaggageInScope(BaggageField delegate) {
this.delegate = delegate;
}
@@ -51,13 +51,24 @@ public class BraveBaggageEntry implements BaggageEntry {
}
@Override
public void set(String value) {
public BraveBaggageInScope set(String value) {
this.delegate.updateValue(value);
return this;
}
public BaggageField unwrap() {
return this.delegate;
}
@Override
public void set(TraceContext traceContext, String value) {
public BraveBaggageInScope set(TraceContext traceContext, String value) {
this.delegate.updateValue(BraveTraceContext.toBrave(traceContext), value);
return this;
}
@Override
public void close() {
}
}

View File

@@ -22,32 +22,35 @@ import java.util.concurrent.ConcurrentHashMap;
import brave.baggage.BaggageField;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.BaggageManager;
import org.springframework.cloud.sleuth.api.TraceContext;
/**
* Brave implementation of a {@link BaggageManager}.
* Brave implementation of a {@link BaggageManager}. Not implementing the interface
* directly cause {@link BraveTracer} will already implement it.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class BraveBaggageManager implements BaggageManager, Closeable {
public class BraveBaggageManager implements Closeable {
private static Map<String, BaggageEntry> CACHE = new ConcurrentHashMap<>();
private static final Map<String, BaggageInScope> CACHE = new ConcurrentHashMap<>();
@Override
public Map<String, String> getAllBaggage() {
return BaggageField.getAllValues();
}
@Override
public BaggageEntry getBaggage(String name) {
public BaggageInScope getBaggage(String name) {
return createBaggage(name);
}
@Override
public BaggageEntry createBaggage(String name) {
return CACHE.computeIfAbsent(name, s -> new BraveBaggageEntry(BaggageField.create(s)));
public BaggageInScope getBaggage(TraceContext traceContext, String name) {
return new BraveBaggageInScope(BaggageField.getByName(BraveTraceContext.toBrave(traceContext), name));
}
public BaggageInScope createBaggage(String name) {
return CACHE.computeIfAbsent(name, s -> new BraveBaggageInScope(BaggageField.create(s)));
}
@Override

View File

@@ -16,6 +16,10 @@
package org.springframework.cloud.sleuth.brave.bridge;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import org.springframework.cloud.sleuth.api.CurrentTraceContext;
import org.springframework.cloud.sleuth.api.TraceContext;
@@ -34,7 +38,7 @@ public class BraveCurrentTraceContext implements CurrentTraceContext {
}
@Override
public TraceContext get() {
public TraceContext context() {
brave.propagation.TraceContext context = this.delegate.get();
if (context == null) {
return null;
@@ -52,6 +56,26 @@ public class BraveCurrentTraceContext implements CurrentTraceContext {
return new BraveScope(this.delegate.maybeScope(BraveTraceContext.toBrave(context)));
}
@Override
public <C> Callable<C> wrap(Callable<C> task) {
return this.delegate.wrap(task);
}
@Override
public Runnable wrap(Runnable task) {
return this.delegate.wrap(task);
}
@Override
public Executor wrap(Executor delegate) {
return this.delegate.executor(delegate);
}
@Override
public ExecutorService wrap(ExecutorService delegate) {
return this.delegate.executorService(delegate);
}
public static brave.propagation.CurrentTraceContext toBrave(CurrentTraceContext context) {
return ((BraveCurrentTraceContext) context).delegate;
}

View File

@@ -39,62 +39,62 @@ public class BraveFinishedSpan implements FinishedSpan {
}
@Override
public String name() {
public String getName() {
return this.mutableSpan.name();
}
@Override
public long startTimestamp() {
public long getStartTimestamp() {
return this.mutableSpan.startTimestamp();
}
@Override
public long endTimestamp() {
public long getEndTimestamp() {
return this.mutableSpan.finishTimestamp();
}
@Override
public Map<String, String> tags() {
public Map<String, String> getTags() {
return this.mutableSpan.tags();
}
@Override
public Collection<Map.Entry<Long, String>> events() {
public Collection<Map.Entry<Long, String>> getEvents() {
return this.mutableSpan.annotations();
}
@Override
public String spanId() {
public String getSpanId() {
return this.mutableSpan.id();
}
@Override
public String parentId() {
public String getParentId() {
return this.mutableSpan.parentId();
}
@Override
public String remoteIp() {
public String getRemoteIp() {
return this.mutableSpan.remoteIp();
}
@Override
public int remotePort() {
public int getRemotePort() {
return this.mutableSpan.remotePort();
}
@Override
public String traceId() {
public String getTraceId() {
return this.mutableSpan.traceId();
}
@Override
public Throwable error() {
public Throwable getError() {
return this.mutableSpan.error();
}
@Override
public Span.Kind kind() {
public Span.Kind getKind() {
if (this.mutableSpan.kind() == null) {
return null;
}
@@ -102,7 +102,7 @@ public class BraveFinishedSpan implements FinishedSpan {
}
@Override
public String remoteServiceName() {
public String getRemoteServiceName() {
return this.mutableSpan.remoteServiceName();
}

View File

@@ -20,10 +20,11 @@ import java.util.Map;
import brave.propagation.TraceContextOrSamplingFlags;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.ScopedSpan;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.SpanCustomizer;
import org.springframework.cloud.sleuth.api.TraceContext;
import org.springframework.cloud.sleuth.api.Tracer;
/**
@@ -36,10 +37,11 @@ public class BraveTracer implements Tracer {
private final brave.Tracer tracer;
private final BraveBaggageManager braveBaggageManager = new BraveBaggageManager();
private final BraveBaggageManager braveBaggageManager;
public BraveTracer(brave.Tracer tracer) {
public BraveTracer(brave.Tracer tracer, BraveBaggageManager braveBaggageManager) {
this.tracer = tracer;
this.braveBaggageManager = braveBaggageManager;
}
@Override
@@ -88,8 +90,8 @@ public class BraveTracer implements Tracer {
return new BraveSpanBuilder(this.tracer);
}
public static Tracer fromBrave(brave.Tracer tracer) {
return new BraveTracer(tracer);
public static Tracer fromBrave(brave.Tracer tracer, BraveBaggageManager braveBaggageManager) {
return new BraveTracer(tracer, braveBaggageManager);
}
@Override
@@ -98,15 +100,25 @@ public class BraveTracer implements Tracer {
}
@Override
public BaggageEntry getBaggage(String name) {
public BaggageInScope getBaggage(String name) {
return this.braveBaggageManager.getBaggage(name);
}
@Override
public BaggageEntry createBaggage(String name) {
public BaggageInScope getBaggage(TraceContext traceContext, String name) {
return this.braveBaggageManager.getBaggage(traceContext, name);
}
@Override
public BaggageInScope createBaggage(String name) {
return this.braveBaggageManager.createBaggage(name);
}
@Override
public BaggageInScope createBaggage(String name, String value) {
return this.braveBaggageManager.createBaggage(name).set(value);
}
}
class BraveSpanInScope implements Tracer.SpanInScope {

View File

@@ -48,8 +48,8 @@ import org.springframework.context.annotation.Configuration;
public class TraceBraveBridgeAutoConfiguation {
@Bean
Tracer braveTracer(brave.Tracer tracer) {
return new BraveTracer(tracer);
Tracer braveTracer(brave.Tracer tracer, BraveBaggageManager braveBaggageManager) {
return new BraveTracer(tracer, braveBaggageManager);
}
@Bean

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth.brave.bridge.http;
import java.util.Collection;
import java.util.Collections;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.http.HttpClientRequest;
@@ -48,6 +51,12 @@ public class BraveHttpClientRequest implements HttpClientRequest {
return this.delegate.unwrap();
}
@Override
public Collection<String> headerNames() {
// this is unused by Brave
return Collections.emptyList();
}
@Override
public Span.Kind spanKind() {
return Span.Kind.valueOf(this.delegate.spanKind().name());

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth.brave.bridge.http;
import java.util.Collection;
import java.util.Collections;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.http.HttpClientRequest;
import org.springframework.cloud.sleuth.api.http.HttpClientResponse;
@@ -54,6 +57,12 @@ public class BraveHttpClientResponse implements HttpClientResponse {
return this.delegate.unwrap();
}
@Override
public Collection<String> headerNames() {
// this is unused by Brave
return Collections.emptyList();
}
@Override
public Span.Kind spanKind() {
return Span.Kind.valueOf(this.delegate.spanKind().name());

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth.brave.bridge.http;
import java.util.Collection;
import java.util.Collections;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.http.HttpRequest;
@@ -53,6 +56,12 @@ public class BraveHttpRequest implements HttpRequest {
return this.delegate.header(name);
}
@Override
public Collection<String> headerNames() {
// this is unused by Brave
return Collections.emptyList();
}
@Override
public Span.Kind spanKind() {
return Span.Kind.valueOf(this.delegate.spanKind().name());

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth.brave.bridge.http;
import java.util.Collection;
import java.util.Collections;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.http.HttpRequest;
import org.springframework.cloud.sleuth.api.http.HttpResponse;
@@ -54,6 +57,12 @@ public class BraveHttpResponse implements HttpResponse {
return this.delegate.unwrap();
}
@Override
public Collection<String> headerNames() {
// this is unused by Brave
return Collections.emptyList();
}
@Override
public Span.Kind spanKind() {
return Span.Kind.valueOf(this.delegate.spanKind().name());

View File

@@ -17,6 +17,8 @@
package org.springframework.cloud.sleuth.brave.bridge.http;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.http.HttpServerRequest;
@@ -51,6 +53,12 @@ public class BraveHttpServerRequest implements HttpServerRequest {
return this.delegate.unwrap();
}
@Override
public Collection<String> headerNames() {
// this is unused by Brave
return Collections.emptyList();
}
@Override
public Span.Kind spanKind() {
return Span.Kind.valueOf(this.delegate.spanKind().name());

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth.brave.bridge.http;
import java.util.Collection;
import java.util.Collections;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.http.HttpServerRequest;
import org.springframework.cloud.sleuth.api.http.HttpServerResponse;
@@ -54,6 +57,12 @@ public class BraveHttpServerResponse implements HttpServerResponse {
return this.delegate.unwrap();
}
@Override
public Collection<String> headerNames() {
// this is unused by Brave
return Collections.emptyList();
}
@Override
public Span.Kind spanKind() {
return Span.Kind.valueOf(this.delegate.spanKind().name());

View File

@@ -29,17 +29,30 @@ import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import brave.propagation.aws.AWSPropagation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager;
class CompositePropagationFactorySupplier implements PropagationFactorySupplier {
private final BeanFactory beanFactory;
private final SleuthBaggageProperties baggageProperties;
private final SleuthPropagationProperties properties;
CompositePropagationFactorySupplier(SleuthPropagationProperties properties) {
CompositePropagationFactorySupplier(BeanFactory beanFactory, SleuthBaggageProperties baggageProperties,
SleuthPropagationProperties properties) {
this.beanFactory = beanFactory;
this.baggageProperties = baggageProperties;
this.properties = properties;
}
@Override
public Propagation.Factory get() {
return new CompositePropagationFactory(this.properties);
return new CompositePropagationFactory(
this.beanFactory.getBeanProvider(BraveBaggageManager.class).getIfAvailable(BraveBaggageManager::new),
this.baggageProperties, this.properties);
}
}
@@ -50,7 +63,8 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
private final SleuthPropagationProperties properties;
CompositePropagationFactory(SleuthPropagationProperties properties) {
CompositePropagationFactory(BraveBaggageManager braveBaggageManager, SleuthBaggageProperties baggageProperties,
SleuthPropagationProperties properties) {
this.properties = properties;
this.mapping.put(SleuthPropagationProperties.PropagationType.AWS, AWSPropagation.FACTORY.get());
// Note: Versions <2.2.3 use injectFormat(MULTI) for non-remote (ex
@@ -58,7 +72,8 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
// See #1643
this.mapping.put(SleuthPropagationProperties.PropagationType.B3,
B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build().get());
this.mapping.put(SleuthPropagationProperties.PropagationType.W3C, W3CPropagation.getInstance());
this.mapping.put(SleuthPropagationProperties.PropagationType.W3C,
new W3CPropagation(braveBaggageManager, baggageProperties));
this.mapping.put(SleuthPropagationProperties.PropagationType.CUSTOM, NoOpPropagation.INSTANCE);
}

View File

@@ -16,9 +16,11 @@
package org.springframework.cloud.sleuth.brave.propagation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -36,8 +38,9 @@ public class TraceBravePropagationAutoConfiguration {
@Bean
@ConditionalOnMissingBean
PropagationFactorySupplier compositePropagationFactorySupplier(SleuthPropagationProperties properties) {
return new CompositePropagationFactorySupplier(properties);
PropagationFactorySupplier compositePropagationFactorySupplier(BeanFactory beanFactory,
SleuthBaggageProperties baggageProperties, SleuthPropagationProperties properties) {
return new CompositePropagationFactorySupplier(beanFactory, baggageProperties, properties);
}
}

View File

@@ -16,18 +16,33 @@
package org.springframework.cloud.sleuth.brave.propagation;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import brave.baggage.BaggageField;
import brave.internal.baggage.BaggageFields;
import brave.internal.propagation.StringPropagationAdapter;
import brave.propagation.Propagation;
import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageInScope;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager;
import static java.util.Collections.singletonList;
/**
* Adopted from OpenTelemetry API.
@@ -41,7 +56,7 @@ import brave.propagation.TraceContextOrSamplingFlags;
*/
public final class W3CPropagation extends Propagation.Factory implements Propagation<String> {
private static final Logger logger = Logger.getLogger(W3CPropagation.class.getName());
private static final Log logger = LogFactory.getLog(W3CPropagation.class.getName());
static final String TRACE_PARENT = "traceparent";
static final String TRACE_STATE = "tracestate";
@@ -90,8 +105,6 @@ public final class W3CPropagation extends Propagation.Factory implements Propaga
private static final String VERSION_00 = "00";
private static final W3CPropagation INSTANCE = new W3CPropagation();
static {
// A valid version is 1 byte representing an 8-bit unsigned integer, version ff is
// invalid.
@@ -105,8 +118,10 @@ public final class W3CPropagation extends Propagation.Factory implements Propaga
}
}
private W3CPropagation() {
// singleton
private final W3CBaggagePropagator baggagePropagator;
public W3CPropagation(BraveBaggageManager braveBaggageManager, SleuthBaggageProperties sleuthBaggageProperties) {
this.baggagePropagator = new W3CBaggagePropagator(braveBaggageManager, sleuthBaggageProperties);
}
@Override
@@ -114,10 +129,6 @@ public final class W3CPropagation extends Propagation.Factory implements Propaga
return StringPropagationAdapter.create(this, keyFactory);
}
public static W3CPropagation getInstance() {
return INSTANCE;
}
@Override
public List<String> keys() {
return FIELDS;
@@ -144,7 +155,8 @@ public final class W3CPropagation extends Propagation.Factory implements Propaga
chars[TRACE_OPTION_OFFSET - 1] = TRACEPARENT_DELIMITER;
copyTraceFlagsHexTo(chars, TRACE_OPTION_OFFSET, context);
setter.put(carrier, TRACE_PARENT, new String(chars, 0, TRACEPARENT_HEADER_SIZE));
// Does not inject trace state
// Add baggage
this.baggagePropagator.injector(setter).inject(context, carrier);
};
}
@@ -159,29 +171,42 @@ public final class W3CPropagation extends Propagation.Factory implements Propaga
return carrier -> {
String traceParent = getter.get(carrier, TRACE_PARENT);
if (traceParent == null) {
return TraceContextOrSamplingFlags.EMPTY;
return withBaggage(TraceContextOrSamplingFlags.EMPTY, carrier, getter);
}
TraceContext contextFromParentHeader = extractContextFromTraceParent(traceParent);
if (contextFromParentHeader == null) {
return TraceContextOrSamplingFlags.EMPTY;
return withBaggage(TraceContextOrSamplingFlags.EMPTY, carrier, getter);
}
String traceStateHeader = getter.get(carrier, TRACE_STATE);
if (traceStateHeader == null || traceStateHeader.isEmpty()) {
return TraceContextOrSamplingFlags.create(contextFromParentHeader);
}
try {
return TraceContextOrSamplingFlags.create(TraceContext.newBuilder()
.traceId(contextFromParentHeader.traceId()).traceIdHigh(contextFromParentHeader.traceIdHigh())
.spanId(contextFromParentHeader.spanId()).sampled(contextFromParentHeader.sampled())
.shared(true).build());
}
catch (IllegalArgumentException e) {
logger.info("Unparseable tracestate header. Returning span context without state.");
return TraceContextOrSamplingFlags.create(contextFromParentHeader);
}
return withBaggage(context(contextFromParentHeader, traceStateHeader), carrier, getter);
};
}
private <R> TraceContextOrSamplingFlags withBaggage(TraceContextOrSamplingFlags context, R carrier,
Getter<R, String> getter) {
if (context.context() == null) {
return context;
}
return this.baggagePropagator.contextWithBaggage(carrier, context, getter);
}
@NotNull
protected TraceContextOrSamplingFlags context(TraceContext contextFromParentHeader, String traceStateHeader) {
if (traceStateHeader == null || traceStateHeader.isEmpty()) {
return TraceContextOrSamplingFlags.create(contextFromParentHeader);
}
try {
return TraceContextOrSamplingFlags
.create(TraceContext.newBuilder().traceId(contextFromParentHeader.traceId())
.traceIdHigh(contextFromParentHeader.traceIdHigh()).spanId(contextFromParentHeader.spanId())
.sampled(contextFromParentHeader.sampled()).shared(true).build());
}
catch (IllegalArgumentException e) {
logger.info("Unparseable tracestate header. Returning span context without state.");
return TraceContextOrSamplingFlags.create(contextFromParentHeader);
}
}
private static boolean isTraceIdValid(CharSequence traceId) {
return (traceId.length() == TRACE_ID_HEX_SIZE) && !INVALID_TRACE_ID.contentEquals(traceId)
&& BigendianEncoding.isValidBase16String(traceId);
@@ -237,6 +262,112 @@ public final class W3CPropagation extends Propagation.Factory implements Propaga
}
/**
* Taken from OpenTelemetry API.
*/
class W3CBaggagePropagator {
private static final Log log = LogFactory.getLog(W3CBaggagePropagator.class);
private static final String FIELD = "baggage";
private static final List<String> FIELDS = singletonList(FIELD);
private final BraveBaggageManager braveBaggageManager;
private final SleuthBaggageProperties properties;
W3CBaggagePropagator(BraveBaggageManager braveBaggageManager, SleuthBaggageProperties properties) {
this.braveBaggageManager = braveBaggageManager;
this.properties = properties;
}
public List<String> keys() {
return FIELDS;
}
public <R> TraceContext.Injector<R> injector(Propagation.Setter<R, String> setter) {
return (context, carrier) -> {
BaggageFields extra = context.findExtra(BaggageFields.class);
if (extra == null || extra.getAllFields().isEmpty()) {
return;
}
StringBuilder headerContent = new StringBuilder();
// We ignore local keys - they won't get propagated
String[] strings = this.properties.getLocalFields().toArray(new String[0]);
Map<String, String> filtered = extra.toMapFilteringFieldNames(strings);
for (Map.Entry<String, String> entry : filtered.entrySet()) {
headerContent.append(entry.getKey()).append("=").append(entry.getValue());
// TODO: [OTEL] No metadata support
// String metadataValue = entry.getEntryMetadata().getValue();
// if (metadataValue != null && !metadataValue.isEmpty()) {
// headerContent.append(";").append(metadataValue);
// }
headerContent.append(",");
}
if (headerContent.length() > 0) {
headerContent.setLength(headerContent.length() - 1);
setter.put(carrier, FIELD, headerContent.toString());
}
};
}
<R> TraceContextOrSamplingFlags contextWithBaggage(R carrier, TraceContextOrSamplingFlags context,
Propagation.Getter<R, String> getter) {
String baggageHeader = getter.get(carrier, FIELD);
if (baggageHeader == null) {
return context;
}
if (baggageHeader.isEmpty()) {
return context;
}
TraceContextOrSamplingFlags.Builder builder = context.toBuilder();
List<AbstractMap.SimpleEntry<BaggageField, String>> pairs = addBaggageToContext(baggageHeader, builder);
TraceContextOrSamplingFlags built = builder.build();
pairs.forEach(e -> {
BaggageField baggage = e.getKey();
baggage.updateValue(built, e.getValue());
});
return built;
}
@SuppressWarnings("StringSplitter")
List<AbstractMap.SimpleEntry<BaggageField, String>> addBaggageToContext(String baggageHeader,
TraceContextOrSamplingFlags.Builder builder) {
List<AbstractMap.SimpleEntry<BaggageField, String>> pairs = new ArrayList<>();
String[] entries = baggageHeader.split(",");
for (String entry : entries) {
int beginningOfMetadata = entry.indexOf(";");
if (beginningOfMetadata > 0) {
entry = entry.substring(0, beginningOfMetadata);
}
String[] keyAndValue = entry.split("=");
for (int i = 0; i < keyAndValue.length; i += 2) {
try {
String key = keyAndValue[i].trim();
String value = keyAndValue[i + 1].trim();
BaggageInScope baggage = this.braveBaggageManager.createBaggage(key);
BaggageField field = ((BraveBaggageInScope) baggage).unwrap();
pairs.add(new AbstractMap.SimpleEntry<>(field, value));
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Exception occurred while trying to parse baggage with key value ["
+ Arrays.toString(keyAndValue) + "]. Will ignore that entry.", e);
}
}
}
}
// TODO: [OTEL] Magic number for max dynamic entries
builder.addExtra(BaggageFields
.newFactory(pairs.stream().map(AbstractMap.SimpleEntry::getKey).collect(Collectors.toList()),
pairs.size() * 2)
.create());
return pairs;
}
}
/**
* Taken from OpenTelemetry API.
*

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth.brave.autoconfig;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -34,6 +37,7 @@ import org.assertj.core.api.InstanceOfAssertFactories;
import org.assertj.core.api.ListAssert;
import org.assertj.core.api.ObjectAssert;
import org.assertj.core.groups.Tuple;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@@ -49,7 +53,7 @@ import static org.assertj.core.api.InstanceOfAssertFactories.array;
public class TraceBaggageEntryConfigurationTests {
static final String[] EMPTY_ARRAY = {};
static final Set EMPTY_ARRAY = new HashSet();
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceBaggageConfiguration.class));
@@ -68,38 +72,40 @@ public class TraceBaggageEntryConfigurationTests {
static ListAssert<Tuple> assertThatBaggageFieldNameToKeyNames(AssertableApplicationContext context) {
return assertThat(context.getBean(Propagation.Factory.class)).extracting("configs")
.asInstanceOf(InstanceOfAssertFactories.ARRAY).extracting("field.name", "keyNames.toArray")
.asInstanceOf(InstanceOfAssertFactories.ARRAY).extracting("field.name", "keyNames")
.asInstanceOf(InstanceOfAssertFactories.list(Tuple.class));
}
@Disabled("Brave broke its internal implementation and tests are bound to it - will skip for now")
@Test
public void shouldCreateRemoteFields() {
this.contextRunner.withPropertyValues("spring.sleuth.baggage.remote-fields=x-vcap-request-id,country-code")
.run((context) -> assertThatBaggageFieldNameToKeyNames(context).containsOnly(
tuple("x-vcap-request-id", new String[] { "x-vcap-request-id" }),
tuple("country-code", new String[] { "country-code" })));
tuple("x-vcap-request-id", new HashSet<>(Collections.singletonList("x-vcap-request-id")),
tuple("country-code", new HashSet<>(Collections.singletonList("country-code"))))));
}
@Disabled("Brave broke its internal implementation and tests are bound to it - will skip for now")
@Test
public void shouldCreateRemoteFields_oldName() {
this.contextRunner.withPropertyValues("spring.sleuth.propagation-keys=x-vcap-request-id,country-code")
.run((context) -> assertThatBaggageFieldNameToKeyNames(context).containsOnly(
tuple("x-vcap-request-id", new String[] { "x-vcap-request-id" }),
tuple("country-code", new String[] { "country-code" })));
tuple("x-vcap-request-id", new HashSet<>(Collections.singletonList("x-vcap-request-id")),
tuple("country-code", new HashSet<>(Collections.singletonList("country-code"))))));
}
@Test
public void shouldCreateDeprecatedBaggageFields() {
this.contextRunner.withPropertyValues("spring.sleuth.baggage-keys=country-code")
.run((context) -> assertThatBaggageFieldNameToKeyNames(context).containsOnly(
tuple("country-code", new String[] { "baggage-country-code", "baggage_country-code" })));
.run((context) -> assertThatBaggageFieldNameToKeyNames(context).containsOnly(tuple("country-code",
new HashSet<>(Arrays.asList("baggage-country-code", "baggage_country-code")))));
}
@Test
public void canCreateDeprecatedBaggageFieldsWithJavaConfig() {
this.contextRunner.withUserConfiguration(CustomBaggageConfiguration.class)
.run((context) -> assertThatBaggageFieldNameToKeyNames(context).containsOnly(
tuple("country-code", new String[] { "baggage-country-code", "baggage_country-code" })));
.run((context) -> assertThatBaggageFieldNameToKeyNames(context).containsOnly(tuple("country-code",
new HashSet<>(Arrays.asList("baggage-country-code", "baggage_country-code")))));
}
@Test

View File

@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.brave.bridge.TraceBraveBridgeAutoConfiguation;
import org.springframework.cloud.sleuth.brave.instrument.messaging.TraceMessagingAutoConfiguration;
import org.springframework.cloud.sleuth.brave.instrument.rpc.TraceRpcAutoConfiguration;
import org.springframework.cloud.sleuth.brave.instrument.web.TraceHttpAutoConfiguration;
@@ -43,8 +44,9 @@ public class TraceBraveAutoConfigurationCustomizersTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class,
TraceHttpAutoConfiguration.class, TraceRpcAutoConfiguration.class,
TraceMessagingAutoConfiguration.class, FakeSpringMessagingAutoConfiguration.class))
TraceBraveBridgeAutoConfiguation.class, TraceHttpAutoConfiguration.class,
TraceRpcAutoConfiguration.class, TraceMessagingAutoConfiguration.class,
FakeSpringMessagingAutoConfiguration.class))
.withUserConfiguration(Customizers.class);
@Test

View File

@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.cloud.sleuth.DisableSecurity;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ActiveProfiles;
@@ -72,6 +73,7 @@ public class TraceBraveAutoConfigurationWithDisabledSleuthTests {
@EnableAutoConfiguration
@Configuration(proxyBeanMethods = false)
@DisableSecurity
static class Config {
}

View File

@@ -137,12 +137,7 @@ public class CorrelationScopeDecoratorTest {
public void should_only_include_whitelist() {
assertThat(this.scopeDecorator).extracting("fields")
.asInstanceOf(InstanceOfAssertFactories.array(SingleCorrelationField[].class))
.extracting(SingleCorrelationField::name).containsOnly("traceId", "spanId", "bp", COUNTRY_CODE.name()); // x-vcap-request-id
// is
// not
// in
// the
// whitelist
.extracting(SingleCorrelationField::name).containsOnly("traceId", "spanId", "bp", COUNTRY_CODE.name());
}
@Test

View File

@@ -30,6 +30,7 @@ import org.mockito.BDDMockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.DisableSecurity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -49,6 +50,7 @@ class TraceMongoDbAutoConfigurationTests {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@DisableSecurity
static class TestTraceMongoDbAutoConfiguration {
@Bean

View File

@@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.DisableSecurity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
@@ -45,6 +46,7 @@ public class EndpointWithCyclicDependenciesTests {
@EnableAutoConfiguration
@Configuration(proxyBeanMethods = false)
@DisableSecurity
static class ClientConfig {
@Bean

View File

@@ -44,6 +44,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
import org.springframework.cloud.sleuth.DisableSecurity;
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -91,6 +92,7 @@ public class WebClientTests {
@Test
@SuppressWarnings("unchecked")
public void shouldAttachTraceIdWhenCallingAnotherServiceForHttpClient() throws Exception {
then(this.spans).isEmpty();
Span span = this.tracer.nextSpan().name("foo").start();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
@@ -144,6 +146,7 @@ public class WebClientTests {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { TraceWebServletAutoConfiguration.class,
GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class })
@DisableSecurity
public static class TestConfiguration {
@Bean

View File

@@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -39,8 +40,8 @@ class TraceBravePropagationAutoConfigurationTests {
@Test
void should_start_a_composite_propagation_factory_supplier_with_b3_as_default() {
ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceBravePropagationAutoConfiguration.class))
ApplicationContextRunner runner = new ApplicationContextRunner().withConfiguration(
AutoConfigurations.of(TraceBraveAutoConfiguration.class, TraceBravePropagationAutoConfiguration.class))
.withUserConfiguration(Config.class);
runner.run(context -> {
@@ -53,7 +54,8 @@ class TraceBravePropagationAutoConfigurationTests {
@Test
void should_start_a_composite_propagation_factory_supplier_with_a_single_propagation_type() {
ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceBravePropagationAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(TraceBraveAutoConfiguration.class,
TraceBravePropagationAutoConfiguration.class))
.withUserConfiguration(Config.class).withPropertyValues("spring.sleuth.propagation.type=w3c");
runner.run(context -> {
@@ -66,7 +68,8 @@ class TraceBravePropagationAutoConfigurationTests {
@Test
void should_start_a_composite_propagation_factory_supplier_with_multiple_propagation_types() {
ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceBravePropagationAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(TraceBraveAutoConfiguration.class,
TraceBravePropagationAutoConfiguration.class))
.withUserConfiguration(Config.class).withPropertyValues("spring.sleuth.propagation.type=b3,w3c");
runner.run(context -> {

View File

@@ -0,0 +1,185 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.brave.propagation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import brave.baggage.BaggageField;
import brave.internal.baggage.BaggageFields;
import brave.propagation.Propagation;
import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test taken from OpenTelemetry.
*/
class W3CBaggagePropagatorTest {
W3CBaggagePropagator propagator = new W3CBaggagePropagator(new BraveBaggageManager(), sleuthBaggageProperties());
SleuthBaggageProperties sleuthBaggageProperties() {
SleuthBaggageProperties properties = new SleuthBaggageProperties();
properties.setRemoteFields(Arrays.asList("nometa", "meta", "key", "key1", "key2"));
return properties;
}
@Test
void fields() {
assertThat(propagator.keys()).containsExactly("baggage");
}
@Test
void extract_noBaggageHeader() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
TraceContextOrSamplingFlags contextWithBaggage = propagator.contextWithBaggage(carrier, context, Map::get);
assertThat(contextWithBaggage).isSameAs(context);
}
@Test
void extract_emptyBaggageHeader() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
carrier.put("baggage", "");
TraceContextOrSamplingFlags contextWithBaggage = propagator.contextWithBaggage(carrier, context, Map::get);
assertThat(contextWithBaggage).isSameAs(context);
}
@Test
void extract_singleEntry() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
carrier.put("baggage", "key=value");
TraceContextOrSamplingFlags contextWithBaggage = propagator.contextWithBaggage(carrier, context, Map::get);
Map<String, String> baggageEntries = BaggageField.getAllValues(contextWithBaggage);
assertThat(baggageEntries).hasSize(1).containsEntry("key", "value");
}
@NotNull
private TraceContextOrSamplingFlags context() {
return TraceContextOrSamplingFlags
.create(TraceContext.newBuilder().traceId(1L).spanId(2L).sampled(true).build());
}
@NotNull
private TraceContext.Builder contextBuilder() {
return TraceContext.newBuilder().traceId(1L).spanId(2L).sampled(true);
}
@Test
void extract_multiEntry() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
carrier.put("baggage", "key1=value1,key2=value2");
TraceContextOrSamplingFlags contextWithBaggage = propagator.contextWithBaggage(carrier, context, Map::get);
Map<String, String> baggageEntries = BaggageField.getAllValues(contextWithBaggage);
assertThat(baggageEntries).hasSize(2).containsEntry("key1", "value1").containsEntry("key2", "value2");
}
@Test
void extract_duplicateKeys() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
carrier.put("baggage", "key=value1,key=value2");
TraceContextOrSamplingFlags contextWithBaggage = propagator.contextWithBaggage(carrier, context, Map::get);
Map<String, String> baggageEntries = BaggageField.getAllValues(contextWithBaggage);
assertThat(baggageEntries).hasSize(1).containsEntry("key", "value2");
}
@Test
void extract_fullComplexities() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
carrier.put("baggage",
"key1= value1; metadata-key = value; othermetadata, " + "key2 =value2 , key3 =\tvalue3 ; ");
TraceContextOrSamplingFlags contextWithBaggage = propagator.contextWithBaggage(carrier, context, Map::get);
Map<String, String> baggageEntries = BaggageField.getAllValues(contextWithBaggage);
assertThat(baggageEntries).hasSize(3).containsEntry("key1", "value1").containsEntry("key2", "value2")
.containsEntry("key3", "value3");
}
/**
* It would be cool if we could replace this with a fuzzer to generate tons of crud
* data, to make sure we don't blow up with it.
*/
@Test
@Disabled("We don't support additional data")
void extract_invalidHeader() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
carrier.put("baggage", "key1= v;alsdf;-asdflkjasdf===asdlfkjadsf ,,a sdf9asdf-alue1; metadata-key = "
+ "value; othermetadata, key2 =value2 , key3 =\tvalue3 ; ");
TraceContextOrSamplingFlags contextWithBaggage = propagator.contextWithBaggage(carrier, context, Map::get);
Map<String, String> baggageEntries = BaggageField.getAllValues(contextWithBaggage);
assertThat(baggageEntries).isEmpty();
}
@Test
void inject_noBaggage() {
TraceContextOrSamplingFlags context = context();
Map<String, String> carrier = new HashMap<>();
propagator.injector((Propagation.Setter<Map<String, String>, String>) Map::put).inject(context.context(),
carrier);
assertThat(carrier).isEmpty();
}
@Test
void inject() {
TraceContextOrSamplingFlags.Builder builder = context().toBuilder();
BaggageField nometa = BaggageField.create("nometa");
BaggageField meta = BaggageField.create("meta");
builder.addExtra(BaggageFields.newFactory(Arrays.asList(nometa, meta), 10).create());
TraceContextOrSamplingFlags context = builder.build();
nometa.updateValue(context, "nometa-value");
meta.updateValue(context, "meta-value;somemetadata; someother=foo");
Map<String, String> carrier = new HashMap<>();
propagator.injector((Propagation.Setter<Map<String, String>, String>) Map::put).inject(context.context(),
carrier);
assertThat(carrier).containsExactlyInAnyOrderEntriesOf(
singletonMap("baggage", "nometa=nometa-value,meta=meta-value;somemetadata; someother=foo"));
}
}

View File

@@ -27,6 +27,9 @@ import brave.propagation.TraceContextOrSamplingFlags;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.springframework.cloud.sleuth.brave.propagation.W3CPropagation.TRACE_PARENT;
@@ -52,7 +55,8 @@ class W3CPropagationTest {
private static final String TRACESTATE_NOT_DEFAULT_ENCODING_WITH_SPACES = "bar=baz , foo=bar";
private final W3CPropagation w3CPropagation = W3CPropagation.getInstance();
private final W3CPropagation w3CPropagation = new W3CPropagation(new BraveBaggageManager(),
new SleuthBaggageProperties());
@Test
void inject_NullCarrierUsage() {

View File

@@ -39,6 +39,7 @@ import org.springframework.cloud.sleuth.SpanName;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.Tracer;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager;
import org.springframework.cloud.sleuth.brave.bridge.BraveTracer;
import org.springframework.cloud.sleuth.instrument.async.TraceCallable;
import org.springframework.cloud.sleuth.instrument.async.TraceRunnable;
@@ -66,7 +67,10 @@ public class SpringCloudSleuthDocTests {
brave.Tracer braveTracer = this.tracing.tracer();
org.springframework.cloud.sleuth.api.Tracer tracer = BraveTracer.fromBrave(braveTracer);
BraveBaggageManager braveBaggageManager = new BraveBaggageManager();
org.springframework.cloud.sleuth.api.Tracer tracer = BraveTracer.fromBrave(this.braveTracer,
this.braveBaggageManager);
@BeforeEach
public void setup() {

View File

@@ -16,17 +16,23 @@
package org.springframework.cloud.sleuth.api;
import java.io.Closeable;
import org.springframework.lang.Nullable;
/**
* Inspired by OpenZipkin Brave's {@code BaggageField}.
* Inspired by OpenZipkin Brave's {@code BaggageField}. Since some tracer implementations
* require a scope to be wrapped around baggage, baggage must be closed so that the scope
* does not leak. Some tracer implementations make baggage immutable (e.g. OpenTelemetry),
* so when the value gets updated they might create new scope (others will return the same
* one - e.g. OpenZipkin Brave).
*
* Represents a single baggage entry.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public interface BaggageEntry {
public interface BaggageInScope extends Closeable {
/**
* @return name of the baggage entry
@@ -50,14 +56,19 @@ public interface BaggageEntry {
/**
* Sets the baggage value.
* @param value to set
* @return new scope
*/
void set(String value);
BaggageInScope set(String value);
/**
* Sets the baggage value for the given {@link TraceContext}.
* @param traceContext context containing baggage
* @param value to set
* @return new scope
*/
void set(TraceContext traceContext, String value);
BaggageInScope set(TraceContext traceContext, String value);
@Override
void close();
}

View File

@@ -19,7 +19,8 @@ package org.springframework.cloud.sleuth.api;
import java.util.Map;
/**
* Manages {@link BaggageEntry} entries.
* Manages {@link BaggageInScope} entries. Upon retrieval / creation of a baggage entry
* puts it in scope. Scope must be closed.
*
* @author OpenTelemetry Authors
* @author Marcin Grzejszczak
@@ -28,23 +29,40 @@ import java.util.Map;
public interface BaggageManager {
/**
* @return mapping of all baggage entries
* @return mapping of all baggage entries from the given scope
*/
Map<String, String> getAllBaggage();
/**
* Retrieves {@link BaggageEntry} for the given name.
* Retrieves {@link BaggageInScope} for the given name.
* @param name baggage name
* @return baggage or {@code null} if not present
*/
BaggageEntry getBaggage(String name);
BaggageInScope getBaggage(String name);
/**
* Creates a new {@link BaggageEntry} entry for the given name or returns an existing
* one if it's already present.
* Retrieves {@link BaggageInScope} for the given name.
* @param traceContext trace context with baggage attached to it
* @param name baggage name
* @return baggage or {@code null} if not present
*/
BaggageInScope getBaggage(TraceContext traceContext, String name);
/**
* Creates a new {@link BaggageInScope} entry for the given name or returns an
* existing one if it's already present.
* @param name baggage name
* @return new or already created baggage
*/
BaggageEntry createBaggage(String name);
BaggageInScope createBaggage(String name);
/**
* Creates a new {@link BaggageInScope} entry for the given name or returns an
* existing one if it's already present.
* @param name baggage name
* @param value baggage value
* @return new or already created baggage
*/
BaggageInScope createBaggage(String name, String value);
}

View File

@@ -17,6 +17,9 @@
package org.springframework.cloud.sleuth.api;
import java.io.Closeable;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import org.springframework.lang.Nullable;
@@ -37,7 +40,7 @@ public interface CurrentTraceContext {
* @return current {@link TraceContext} or {@code null} if not set.
*/
@Nullable
TraceContext get();
TraceContext context();
/**
* Sets the current span in scope until the returned object is closed. It is a
@@ -56,6 +59,35 @@ public interface CurrentTraceContext {
*/
CurrentTraceContext.Scope maybeScope(@Nullable TraceContext context);
/**
* Wraps a task in a trace representation.
* @param task task to wrap
* @param <C> task return type
* @return wrapped task
*/
<C> Callable<C> wrap(Callable<C> task);
/**
* Wraps a task in a trace representation.
* @param task task to wrap
* @return wrapped task
*/
Runnable wrap(Runnable task);
/**
* Wraps an executor in a trace representation.
* @param delegate executor to wrap
* @return wrapped executor
*/
Executor wrap(Executor delegate);
/**
* Wraps an executor service in a trace representation.
* @param delegate executor service to wrap
* @return wrapped executor service
*/
ExecutorService wrap(ExecutorService delegate);
/**
* Scope of a span. Needs to be closed so that resources are let go (e.g. MDC is
* cleared).

View File

@@ -37,70 +37,70 @@ public interface FinishedSpan {
/**
* @return span's name
*/
String name();
String getName();
/**
* @return span's start timestamp
*/
long startTimestamp();
long getStartTimestamp();
/**
* @return span's end timestamp
*/
long endTimestamp();
long getEndTimestamp();
/**
* @return span's tags
*/
Map<String, String> tags();
Map<String, String> getTags();
/**
* @return span's events as timestamp to value mapping
*/
Collection<Map.Entry<Long, String>> events();
Collection<Map.Entry<Long, String>> getEvents();
/**
* @return span's span id
*/
String spanId();
String getSpanId();
/**
* @return span's parent id or {@code null} if not set
*/
@Nullable
String parentId();
String getParentId();
/**
* @return span's remote ip
*/
@Nullable
String remoteIp();
String getRemoteIp();
/**
* @return span's remote port
*/
int remotePort();
int getRemotePort();
/**
* @return span's trace id
*/
String traceId();
String getTraceId();
/**
* @return corresponding error or {@code null} if one was not thrown
*/
@Nullable
Throwable error();
Throwable getError();
/**
* @return span's kind
*/
Span.Kind kind();
Span.Kind getKind();
/**
* @return remote service name or {@code null} if not set
*/
@Nullable
String remoteServiceName();
String getRemoteServiceName();
}

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.sleuth.api.http;
import java.util.Collection;
import org.springframework.cloud.sleuth.api.Span;
/**
@@ -29,6 +31,11 @@ import org.springframework.cloud.sleuth.api.Span;
*/
public interface Request {
/**
* @return list of header names.
*/
Collection<String> headerNames();
/**
* @return The remote {@link Span.Kind} describing the direction and type of the
* request.

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.sleuth.api.http;
import java.util.Collection;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.lang.Nullable;
@@ -30,6 +32,11 @@ import org.springframework.lang.Nullable;
*/
public interface Response {
/**
* @return list of header names.
*/
Collection<String> headerNames();
/**
* @return The remote {@link Span.Kind} describing the direction and type of the
* request.

View File

@@ -16,7 +16,7 @@
package org.springframework.cloud.sleuth.api.noop;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.TraceContext;
/**
@@ -25,7 +25,7 @@ import org.springframework.cloud.sleuth.api.TraceContext;
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class NoOpBaggageEntry implements BaggageEntry {
public class NoOpBaggageInScope implements BaggageInScope {
@Override
public String name() {
@@ -43,12 +43,17 @@ public class NoOpBaggageEntry implements BaggageEntry {
}
@Override
public void set(String value) {
public BaggageInScope set(String value) {
return this;
}
@Override
public void set(TraceContext traceContext, String value) {
public BaggageInScope set(TraceContext traceContext, String value) {
return this;
}
@Override
public void close() {
}

View File

@@ -16,6 +16,10 @@
package org.springframework.cloud.sleuth.api.noop;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import org.springframework.cloud.sleuth.api.CurrentTraceContext;
import org.springframework.cloud.sleuth.api.TraceContext;
@@ -28,7 +32,7 @@ import org.springframework.cloud.sleuth.api.TraceContext;
public class NoOpCurrentTraceContext implements CurrentTraceContext {
@Override
public TraceContext get() {
public TraceContext context() {
return new NoOpTraceContext();
}
@@ -44,4 +48,24 @@ public class NoOpCurrentTraceContext implements CurrentTraceContext {
};
}
@Override
public <C> Callable<C> wrap(Callable<C> task) {
return task;
}
@Override
public Runnable wrap(Runnable task) {
return task;
}
@Override
public Executor wrap(Executor delegate) {
return delegate;
}
@Override
public ExecutorService wrap(ExecutorService delegate) {
return delegate;
}
}

View File

@@ -19,10 +19,11 @@ package org.springframework.cloud.sleuth.api.noop;
import java.util.HashMap;
import java.util.Map;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.ScopedSpan;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.SpanCustomizer;
import org.springframework.cloud.sleuth.api.TraceContext;
import org.springframework.cloud.sleuth.api.Tracer;
/**
@@ -74,13 +75,23 @@ public class NoOpTracer implements Tracer {
}
@Override
public BaggageEntry getBaggage(String name) {
return new NoOpBaggageEntry();
public BaggageInScope getBaggage(String name) {
return new NoOpBaggageInScope();
}
@Override
public BaggageEntry createBaggage(String name) {
return new NoOpBaggageEntry();
public BaggageInScope getBaggage(TraceContext traceContext, String name) {
return new NoOpBaggageInScope();
}
@Override
public BaggageInScope createBaggage(String name) {
return new NoOpBaggageInScope();
}
@Override
public BaggageInScope createBaggage(String name, String value) {
return new NoOpBaggageInScope();
}
}

View File

@@ -62,7 +62,7 @@ class SpanIgnoringSpanFilter implements SpanFilter {
@Override
public boolean isExportable(FinishedSpan span) {
List<Pattern> spanNamesToIgnore = spanNamesToIgnore();
String name = span.name();
String name = span.getName();
if (StringUtils.hasText(name) && spanNamesToIgnore.stream().anyMatch(p -> p.matcher(name).matches())) {
if (log.isDebugEnabled()) {
log.debug("Will ignore a span with name [" + name + "]");

View File

@@ -171,13 +171,14 @@ public abstract class ReactorSleuth {
}
/**
* Like {@link CurrentTraceContext#get()}, except it first checks the reactor context.
* Like {@link CurrentTraceContext#context()}, except it first checks the reactor
* context.
*/
static TraceContext traceContext(Context context, CurrentTraceContext fallback) {
if (context.hasKey(TraceContext.class)) {
return context.get(TraceContext.class);
}
return fallback.get();
return fallback.context();
}
}

View File

@@ -99,7 +99,7 @@ class TraceWebAspect {
@SuppressWarnings("unchecked")
public Object wrapWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable {
Callable<Object> callable = (Callable<Object>) pjp.proceed();
TraceContext currentSpan = this.currentTraceContext.get();
TraceContext currentSpan = this.currentTraceContext.context();
if (currentSpan == null) {
return callable;
}
@@ -112,7 +112,7 @@ class TraceWebAspect {
@Around("anyControllerOrRestControllerWithPublicWebAsyncTaskMethod()")
public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable {
final WebAsyncTask<?> webAsyncTask = (WebAsyncTask<?>) pjp.proceed();
TraceContext currentSpan = this.currentTraceContext.get();
TraceContext currentSpan = this.currentTraceContext.context();
if (currentSpan == null) {
return webAsyncTask;
}

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.sleuth.instrument.web;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
@@ -330,6 +331,11 @@ final class TraceWebFilter implements WebFilter, Ordered {
this.delegate = delegate;
}
@Override
public Collection<String> headerNames() {
return this.delegate.getHeaders().keySet();
}
@Override
public ServerHttpRequest unwrap() {
return delegate;
@@ -395,6 +401,11 @@ final class TraceWebFilter implements WebFilter, Ordered {
return statusCode != null ? statusCode.value() : 0;
}
@Override
public Collection<String> headerNames() {
return this.delegate.getHeaders().keySet();
}
@Override
public Throwable error() {
return this.throwable;

View File

@@ -17,6 +17,8 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
@@ -65,7 +67,7 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
.doOnRequestError(new TracingDoOnErrorRequest(springContext))
.doOnRequest(new TracingDoOnRequest(springContext)).mapConnect(new TracingMapConnect(() -> {
CurrentTraceContext ref = currentContext.get();
return ref != null ? ref.get() : null;
return ref != null ? ref.context() : null;
}));
}
return bean;
@@ -263,6 +265,11 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
return null;
}
@Override
public Collection<String> headerNames() {
return this.delegate.requestHeaders().names();
}
@Override
public Object unwrap() {
return delegate;
@@ -327,6 +334,11 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
return this.delegate;
}
@Override
public Collection<String> headerNames() {
return this.delegate != null ? this.delegate.responseHeaders().names() : Collections.emptyList();
}
@Override
public HttpClientRequestWrapper request() {
if (request == null) {

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -182,6 +183,11 @@ abstract class AbstractHttpHeadersFilter implements HttpHeadersFilter {
this.filteredHeaders = filteredHeaders;
}
@Override
public Collection<String> headerNames() {
return this.delegate.getHeaders().keySet();
}
@Override
public Object unwrap() {
return delegate;
@@ -222,6 +228,11 @@ abstract class AbstractHttpHeadersFilter implements HttpHeadersFilter {
this.delegate = delegate;
}
@Override
public Collection<String> headerNames() {
return this.delegate.getHeaders().keySet();
}
@Override
public Object unwrap() {
return delegate;

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicReference;
@@ -340,6 +341,11 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
this.builder = ClientRequest.from(delegate);
}
@Override
public Collection<String> headerNames() {
return this.delegate.headers().keySet();
}
@Override
public Object unwrap() {
return delegate;
@@ -384,6 +390,11 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
this.delegate = delegate;
}
@Override
public Collection<String> headerNames() {
return this.delegate.headers().asHttpHeaders().keySet();
}
@Override
public Object unwrap() {
return delegate;

View File

@@ -113,6 +113,11 @@ final class TracingFeignClient implements Client {
this.delegate = delegate;
}
@Override
public Collection<String> headerNames() {
return this.delegate.headers().keySet();
}
@Override
public Object unwrap() {
return delegate;
@@ -194,6 +199,11 @@ final class TracingFeignClient implements Client {
return response;
}
@Override
public Collection<String> headerNames() {
return this.response != null ? this.response.headers().keySet() : Collections.emptyList();
}
@Override
public RequestWrapper request() {
return request;

View File

@@ -58,7 +58,7 @@ public final class TracingAsyncClientHttpRequestInterceptor implements AsyncClie
// avoid context sync overhead when we are the root span
String parentId = span.context().parentId();
TraceContext invocationContext = parentId != null ? currentTraceContext.get() : null;
TraceContext invocationContext = parentId != null ? currentTraceContext.context() : null;
try (CurrentTraceContext.Scope ws = currentTraceContext.maybeScope(span.context())) {
ListenableFuture<ClientHttpResponse> result = execution.executeAsync(req, body);

View File

@@ -17,6 +17,8 @@
package org.springframework.cloud.sleuth.instrument.web.mvc;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -84,6 +86,11 @@ public final class TracingClientHttpRequestInterceptor implements ClientHttpRequ
this.delegate = delegate;
}
@Override
public Collection<String> headerNames() {
return this.delegate.getHeaders().keySet();
}
@Override
public Object unwrap() {
return delegate;
@@ -139,6 +146,11 @@ public final class TracingClientHttpRequestInterceptor implements ClientHttpRequ
return response;
}
@Override
public Collection<String> headerNames() {
return this.response != null ? this.response.getHeaders().keySet() : Collections.emptyList();
}
@Override
public HttpRequestWrapper request() {
return request;

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth.instrument.web.servlet;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
@@ -45,6 +48,11 @@ class HttpServletRequestWrapper implements HttpServerRequest {
this.delegate = delegate;
}
@Override
public Collection<String> headerNames() {
return Collections.list(this.delegate.getHeaderNames());
}
@Override
public Object unwrap() {
return delegate;

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.sleuth.instrument.web.servlet;
import java.util.Collection;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -66,6 +68,11 @@ class HttpServletResponseWrapper implements HttpServerResponse {
return response;
}
@Override
public Collection<String> headerNames() {
return this.response.getHeaderNames();
}
@Override
@Nullable
public HttpServletRequestWrapper request() {

View File

@@ -33,7 +33,7 @@ class SpanIgnoringSpanFilterTests {
private FinishedSpan namedSpan() {
FinishedSpan span = BDDMockito.mock(FinishedSpan.class);
BDDMockito.given(span.name()).willReturn("someName");
BDDMockito.given(span.getName()).willReturn("someName");
return span;
}

View File

@@ -34,7 +34,7 @@
<brave.version>5.12.7</brave.version>
<brave.opentracing.version>0.37.2</brave.opentracing.version>
<grpc.spring.boot.version>4.2.1</grpc.spring.boot.version>
<opentelemetry.version>0.9.1</opentelemetry.version>
<opentelemetry.version>0.10.0</opentelemetry.version>
</properties>
<dependencyManagement>
<dependencies>
@@ -163,4 +163,22 @@
</pluginRepositories>
</profile>
</profiles>
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-open-telemetry-maven</id>
<name>bintray</name>
<url>https://dl.bintray.com/open-telemetry/maven</url>
</repository>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>bintray-open-telemetry-maven-snapshot</id>
<name>bintray</name>
<url>https://oss.jfrog.org/oss-snapshot-local/</url>
</repository>
</repositories>
</project>

View File

@@ -33,15 +33,6 @@
</parent>
<dependencies>
<!-- TODO: [OTEL] Why do I have to add this? -->
<!-- https://mvnrepository.com/artifact/com.google.auto.value/auto-value -->
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<version>1.7.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-core</artifactId>
@@ -57,11 +48,27 @@
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-tracing</artifactId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-baggage</artifactId>
<artifactId>opentelemetry-sdk-common</artifactId>
<exclusions>
<exclusion>
<groupId>io.opentelemetry</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-tracing</artifactId>
<exclusions>
<exclusion>
<groupId>io.opentelemetry</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
@@ -81,21 +88,45 @@
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-auto-annotations</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>io.opentelemetry</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporters-logging</artifactId>
<artifactId>opentelemetry-exporter-logging</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>io.opentelemetry</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-trace-propagators</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>io.opentelemetry</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-opentracing-shim</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>io.opentelemetry</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.otel.autoconfig;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.sdk.trace.config.TraceConfig;
import io.opentelemetry.trace.Span;
import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@@ -20,20 +20,20 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import io.opentelemetry.baggage.BaggageManager;
import io.opentelemetry.baggage.spi.BaggageManagerFactory;
import io.opentelemetry.sdk.baggage.spi.BaggageManagerFactorySdk;
import io.opentelemetry.sdk.trace.Sampler;
import io.opentelemetry.sdk.trace.Samplers;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.metrics.spi.MeterProviderFactory;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.api.trace.spi.TracerProviderFactory;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.TracerSdkProvider;
import io.opentelemetry.sdk.trace.config.TraceConfig;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.sdk.trace.spi.TracerProviderFactorySdk;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.TracerProvider;
import io.opentelemetry.trace.spi.TracerProviderFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@@ -41,6 +41,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.otel.bridge.OtelOpenTelemetry;
import org.springframework.cloud.sleuth.otel.exporter.SpanExporterCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -58,6 +59,14 @@ import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties(OtelProperties.class)
public class TraceOtelAutoConfiguration {
@Bean
@ConditionalOnMissingBean
OpenTelemetry otel(TracerProviderFactory tracerProviderFactory, MeterProviderFactory meterProviderFactory,
TracerProvider tracerProvider, MeterProvider meterProvider, ContextPropagators contextPropagators) {
return new OtelOpenTelemetry(tracerProviderFactory, meterProviderFactory, tracerProvider, meterProvider,
contextPropagators);
}
@Bean
@ConditionalOnMissingBean
TracerProviderFactory otelTracerProviderFactory() {
@@ -72,14 +81,14 @@ public class TraceOtelAutoConfiguration {
@Bean
@ConditionalOnMissingBean
BaggageManagerFactory otelBaggageManagerFactory() {
return new BaggageManagerFactorySdk();
MeterProviderFactory otelMeterProviderFactory() {
return OpenTelemetry::getGlobalMeterProvider;
}
@Bean
@ConditionalOnMissingBean
BaggageManager otelBaggageManager(BaggageManagerFactory baggageManagerFactory) {
return baggageManagerFactory.create();
MeterProvider otelMeterProvider(MeterProviderFactory meterProviderFactory) {
return meterProviderFactory.create();
}
@Bean
@@ -101,7 +110,7 @@ public class TraceOtelAutoConfiguration {
tracerSdkObjectProvider.ifAvailable(tracerSdkProvider -> {
List<SpanProcessor> processors = spanProcessors.getIfAvailable(ArrayList::new);
processors.addAll(spanExporters.getIfAvailable(ArrayList::new).stream()
.map(e -> SimpleSpanProcessor.newBuilder(spanExporterCustomizer.customize(e)).build())
.map(e -> SimpleSpanProcessor.builder(spanExporterCustomizer.customize(e)).build())
.collect(Collectors.toList()));
processors.forEach(tracerSdkProvider::addSpanProcessor);
tracerSdkProvider.updateActiveTraceConfig(traceConfig);
@@ -112,7 +121,7 @@ public class TraceOtelAutoConfiguration {
@Bean
@ConditionalOnMissingBean
Sampler otelSampler(OtelProperties otelProperties) {
return Samplers.traceIdRatioBased(otelProperties.getTraceIdRatioBased());
return Sampler.traceIdRatioBased(otelProperties.getTraceIdRatioBased());
}
@Bean

View File

@@ -18,10 +18,10 @@ package org.springframework.cloud.sleuth.otel.bridge;
import java.util.Arrays;
import io.opentelemetry.internal.Utils;
import io.opentelemetry.api.internal.Utils;
/**
* Copied from io.opentelemetry.trace.BigendianEncoding.
* Copied from io.opentelemetry.api.trace.BigendianEncoding.
*/
final class BigendianEncoding {
@@ -35,6 +35,9 @@ final class BigendianEncoding {
private static final byte[] DECODING = buildDecodingArray();
private BigendianEncoding() {
}
private static byte[] buildDecodingArray() {
byte[] decoding = new byte[ASCII_CHARACTERS];
Arrays.fill(decoding, (byte) -1);
@@ -78,7 +81,4 @@ final class BigendianEncoding {
return (byte) decoded;
}
private BigendianEncoding() {
}
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.otel.bridge;
import java.util.concurrent.atomic.AtomicReference;
import io.grpc.Context;
import io.opentelemetry.baggage.BaggageManager;
import io.opentelemetry.baggage.BaggageUtils;
import io.opentelemetry.baggage.EntryMetadata;
import io.opentelemetry.trace.Tracer;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.TraceContext;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
/**
* OpenTelemetry implementation of a {@link BaggageEntry}.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
// TODO: [OTEL] Experimental - doesn't really work
public class OtelBaggageEntry implements BaggageEntry {
private final Tracer tracer;
private final ApplicationEventPublisher publisher;
private final SleuthBaggageProperties sleuthBaggageProperties;
private final io.opentelemetry.baggage.Baggage delegate;
private final io.opentelemetry.baggage.BaggageManager manager;
private final String name;
private final EntryMetadata entryMetadata;
private final AtomicReference<Context> context;
public OtelBaggageEntry(Tracer tracer, ApplicationEventPublisher publisher,
SleuthBaggageProperties sleuthBaggageProperties, io.opentelemetry.baggage.Baggage delegate,
BaggageManager manager, AtomicReference<Context> context, String name, EntryMetadata entryMetadata) {
this.tracer = tracer;
this.publisher = publisher;
this.sleuthBaggageProperties = sleuthBaggageProperties;
this.delegate = delegate;
this.manager = manager;
this.name = name;
this.entryMetadata = entryMetadata;
this.context = context;
}
@Override
public String name() {
return this.name;
}
@Override
public String get() {
return BaggageUtils.getBaggage(this.context.get()).getEntryValue(this.name);
}
@Override
public String get(TraceContext traceContext) {
// TODO: [OTEL] Discuss this with OTEL
return null;
}
@Override
public void set(String value) {
io.opentelemetry.baggage.Baggage baggage = this.manager.baggageBuilder().setParent(this.delegate)
.put(this.name, value, this.entryMetadata).build();
this.context.set(BaggageUtils.withBaggage(baggage, this.context.get()));
if (this.sleuthBaggageProperties.getTagFields().stream().map(String::toLowerCase)
.anyMatch(s -> s.equals(this.name))) {
this.tracer.getCurrentSpan().setAttribute(this.name, value);
}
this.publisher.publishEvent(new BaggageChanged(this, this.name, value));
}
@Override
public void set(TraceContext traceContext, String value) {
// TODO: [OTEL] Discuss this with OTEL
}
public static class BaggageChanged extends ApplicationEvent {
/**
* Baggage entry name.
*/
public String name;
/**
* Baggage entry value.
*/
public String value;
public BaggageChanged(OtelBaggageEntry source, String name, String value) {
super(source);
this.name = name;
this.value = value;
}
@Override
public String toString() {
return "BaggageChanged{" + "name='" + name + '\'' + ", value='" + value + '\'' + '}';
}
}
public static class BaggageScopeEnded extends ApplicationEvent {
public BaggageScopeEnded(Object source) {
super(source);
}
}
}

View File

@@ -0,0 +1,163 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.otel.bridge;
import java.util.concurrent.atomic.AtomicReference;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.CurrentTraceContext;
import org.springframework.cloud.sleuth.api.TraceContext;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
/**
* OpenTelemetry implementation of a {@link BaggageInScope}.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class OtelBaggageInScope implements BaggageInScope {
private final OtelBaggageManager otelBaggageManager;
private final CurrentTraceContext currentTraceContext;
private final ApplicationEventPublisher publisher;
private final SleuthBaggageProperties sleuthBaggageProperties;
private final AtomicReference<Entry> entry = new AtomicReference<>();
private final AtomicReference<Scope> scope = new AtomicReference<>();
public OtelBaggageInScope(OtelBaggageManager otelBaggageManager, CurrentTraceContext currentTraceContext,
ApplicationEventPublisher publisher, SleuthBaggageProperties sleuthBaggageProperties, Entry entry) {
this.otelBaggageManager = otelBaggageManager;
this.currentTraceContext = currentTraceContext;
this.publisher = publisher;
this.sleuthBaggageProperties = sleuthBaggageProperties;
this.entry.set(entry);
}
@Override
public String name() {
return entry().getKey();
}
@Override
public String get() {
return this.otelBaggageManager.currentBaggage().getEntryValue(entry().getKey());
}
@Override
public String get(TraceContext traceContext) {
Entry entry = this.otelBaggageManager.getEntry((OtelTraceContext) traceContext, entry().getKey());
if (entry == null) {
return null;
}
return entry.getValue();
}
@Override
public BaggageInScope set(String value) {
return doSet(this.currentTraceContext.context(), value);
}
private BaggageInScope doSet(TraceContext context, String value) {
Context current = Context.current();
Span currentSpan = Span.current();
io.opentelemetry.api.baggage.Baggage baggage;
if (context != null) {
OtelTraceContext ctx = (OtelTraceContext) context;
Context storedCtx = ctx.context();
Baggage fromContext = Baggage.fromContext(storedCtx);
baggage = fromContext.toBuilder().setParent(current)
.put(entry().getKey(), value, entry().getEntryMetadata()).build();
current = baggage.storeInContext(current);
ctx.updateContext(current);
}
else {
baggage = Baggage.builder().put(entry().getKey(), value, entry().getEntryMetadata()).build();
}
Context withBaggage = current.with(baggage);
this.scope.set(withBaggage.makeCurrent());
if (this.sleuthBaggageProperties.getTagFields().stream().map(String::toLowerCase)
.anyMatch(s -> s.equals(entry().getKey()))) {
currentSpan.setAttribute(entry().getKey(), value);
}
this.publisher.publishEvent(new BaggageChanged(this, baggage, entry().getKey(), value));
Entry previous = entry();
this.entry.set(new Entry(previous.getKey(), value, previous.getEntryMetadata()));
return this;
}
private Entry entry() {
return this.entry.get();
}
@Override
public BaggageInScope set(TraceContext traceContext, String value) {
return doSet(traceContext, value);
}
@Override
public void close() {
Scope scope = this.scope.get();
if (scope != null) {
this.scope.set(null);
scope.close();
}
}
public static class BaggageChanged extends ApplicationEvent {
/**
* Baggage with the new entry.
*/
public Baggage baggage;
/**
* Baggage entry name.
*/
public String name;
/**
* Baggage entry value.
*/
public String value;
public BaggageChanged(OtelBaggageInScope source, Baggage baggage, String name, String value) {
super(source);
this.baggage = baggage;
this.name = name;
this.value = value;
}
@Override
public String toString() {
return "BaggageChanged{" + "name='" + name + '\'' + ", value='" + value + '\'' + '}';
}
}
}

View File

@@ -16,24 +16,27 @@
package org.springframework.cloud.sleuth.otel.bridge;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Objects;
import io.grpc.Context;
import io.opentelemetry.baggage.BaggageUtils;
import io.opentelemetry.baggage.Entry;
import io.opentelemetry.baggage.EntryMetadata;
import io.opentelemetry.trace.Tracer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.BaggageConsumer;
import io.opentelemetry.api.baggage.EntryMetadata;
import io.opentelemetry.context.Context;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.BaggageManager;
import org.springframework.cloud.sleuth.api.CurrentTraceContext;
import org.springframework.cloud.sleuth.api.TraceContext;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
/**
* OpenTelemetry implementation of a {@link BaggageManager}.
@@ -41,24 +44,17 @@ import org.springframework.context.ApplicationListener;
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class OtelBaggageManager implements BaggageManager, ApplicationListener<OtelBaggageEntry.BaggageScopeEnded> {
public class OtelBaggageManager {
private static final Log log = LogFactory.getLog(OtelBaggageManager.class);
private final io.opentelemetry.trace.Tracer tracer;
private final io.opentelemetry.baggage.BaggageManager delegate;
private final CurrentTraceContext currentTraceContext;
private final SleuthBaggageProperties sleuthBaggageProperties;
private final ApplicationEventPublisher publisher;
AtomicReference<Context> context = new AtomicReference<>(Context.ROOT);
public OtelBaggageManager(Tracer tracer, io.opentelemetry.baggage.BaggageManager delegate,
SleuthBaggageProperties sleuthBaggageProperties, ApplicationEventPublisher publisher) {
this.tracer = tracer;
this.delegate = delegate;
public OtelBaggageManager(CurrentTraceContext currentTraceContext, SleuthBaggageProperties sleuthBaggageProperties,
ApplicationEventPublisher publisher) {
this.currentTraceContext = currentTraceContext;
this.sleuthBaggageProperties = sleuthBaggageProperties;
this.publisher = publisher;
}
@@ -69,55 +65,193 @@ public class OtelBaggageManager implements BaggageManager, ApplicationListener<O
return baggage;
}
private io.opentelemetry.baggage.Baggage currentBaggage() {
return BaggageUtils.getBaggage(Context.current());
}
public BaggageEntry getBaggage(String name) {
io.opentelemetry.baggage.Baggage baggage = currentBaggage();
Entry entry = entryForName(name, baggage);
if (entry == null) {
return null;
CompositeBaggage currentBaggage() {
OtelTraceContext traceContext = (OtelTraceContext) currentTraceContext.context();
Context context = Context.current();
Deque<Context> stack = new ArrayDeque<>();
if (traceContext != null) {
stack.addFirst(traceContext.context());
}
return otelBaggage(name, baggage, entry);
stack.addFirst(context);
return new CompositeBaggage(stack);
}
private Entry entryForName(String name, io.opentelemetry.baggage.Baggage baggage) {
return baggage.getEntries().stream().filter(e -> e.getKey().toLowerCase().equals(name.toLowerCase()))
public BaggageInScope getBaggage(String name) {
Entry entry = getBaggage(name, currentBaggage());
return createNewEntryIfMissing(name, entry);
}
protected BaggageInScope createNewEntryIfMissing(String name, Entry entry) {
if (entry == null) {
return createBaggage(name);
}
return otelBaggage(entry);
}
private Entry getBaggage(String name, io.opentelemetry.api.baggage.Baggage baggage) {
return entryForName(name, baggage);
}
public BaggageInScope getBaggage(TraceContext traceContext, String name) {
OtelTraceContext context = (OtelTraceContext) traceContext;
// TODO: Refactor
Deque<Context> stack = new ArrayDeque<>();
stack.addFirst(Context.current());
stack.addFirst(context.context());
Context ctx = removeFirst(stack);
Entry entry = null;
while (ctx != null && entry == null) {
entry = getBaggage(name, Baggage.fromContext(ctx));
ctx = removeFirst(stack);
}
return createNewEntryIfMissing(name, entry);
}
protected Entry getEntry(OtelTraceContext traceContext, String name) {
OtelTraceContext context = traceContext;
Context ctx = context.context();
return getBaggage(name, Baggage.fromContext(ctx));
}
protected Context removeFirst(Deque<Context> stack) {
return stack.isEmpty() ? null : stack.removeFirst();
}
private Entry entryForName(String name, io.opentelemetry.api.baggage.Baggage baggage) {
return Entry.fromBaggage(baggage).stream().filter(e -> e.getKey().toLowerCase().equals(name.toLowerCase()))
.findFirst().orElse(null);
}
private BaggageEntry otelBaggage(String name, io.opentelemetry.baggage.Baggage baggage, Entry entry) {
return new OtelBaggageEntry(this.tracer, this.publisher, this.sleuthBaggageProperties, baggage, this.delegate,
this.context, name, entry.getEntryMetadata());
private BaggageInScope otelBaggage(Entry entry) {
return new OtelBaggageInScope(this, this.currentTraceContext, this.publisher, this.sleuthBaggageProperties,
entry);
}
public BaggageEntry createBaggage(String name) {
return baggageWithValue(name, "");
public BaggageInScope createBaggage(String name) {
return createBaggage(name, "");
}
private BaggageEntry baggageWithValue(String name, String value) {
public BaggageInScope createBaggage(String name, String value) {
BaggageInScope baggageInScope = baggageWithValue(name, "");
return baggageInScope.set(value);
}
private BaggageInScope baggageWithValue(String name, String value) {
List<String> remoteFieldsFields = this.sleuthBaggageProperties.getRemoteFields();
boolean remoteField = remoteFieldsFields.stream().map(String::toLowerCase)
.anyMatch(s -> s.equals(name.toLowerCase()));
EntryMetadata.EntryTtl entryTtl = EntryMetadata.EntryTtl.NO_PROPAGATION;
if (remoteField) {
entryTtl = EntryMetadata.EntryTtl.UNLIMITED_PROPAGATION;
}
EntryMetadata entryMetadata = EntryMetadata.create(entryTtl);
io.opentelemetry.baggage.Baggage baggage = this.delegate.baggageBuilder().put(name, value, entryMetadata)
.build();
this.context.set(BaggageUtils.withBaggage(baggage, this.context.get()));
return new OtelBaggageEntry(this.tracer, this.publisher, this.sleuthBaggageProperties, baggage, this.delegate,
this.context, name, entryMetadata);
EntryMetadata entryMetadata = EntryMetadata.create(propagationString(remoteField));
Entry entry = new Entry(name, value, entryMetadata);
return new OtelBaggageInScope(this, this.currentTraceContext, this.publisher, this.sleuthBaggageProperties,
entry);
}
@Override
public void onApplicationEvent(OtelBaggageEntry.BaggageScopeEnded event) {
if (log.isTraceEnabled()) {
log.trace("Baggage scope ended");
private String propagationString(boolean remoteField) {
// TODO: [OTEL] Magic strings
String propagation = "";
if (remoteField) {
propagation = "propagation=unlimited";
}
this.context.set(Context.ROOT);
return propagation;
}
}
class CompositeBaggage implements io.opentelemetry.api.baggage.Baggage {
private final Deque<Context> stack;
private final Collection<Entry> entries;
CompositeBaggage(Deque<Context> stack) {
this.stack = stack;
this.entries = getEntries();
}
Collection<Entry> getEntries() {
// parent baggage foo=bar
// child baggage foo=baz - we want the last one to override the previous one
Map<String, Entry> map = new HashMap<>();
Iterator<Context> iterator = this.stack.descendingIterator();
while (iterator.hasNext()) {
Context next = iterator.next();
Baggage baggage = Baggage.fromContext(next);
baggage.forEach((key, value, metadata) -> map.put(key, new Entry(key, value, metadata)));
}
return map.values();
}
@Override
public int size() {
return this.entries.size();
}
@Override
public void forEach(BaggageConsumer consumer) {
this.entries.forEach(entry -> consumer.accept(entry.getKey(), entry.getValue(), entry.getEntryMetadata()));
}
@Override
public String getEntryValue(String entryKey) {
return this.entries.stream().filter(entry -> entryKey.equals(entry.getKey())).map(Entry::getValue).findFirst()
.orElse(null);
}
@Override
public Builder toBuilder() {
return Baggage.builder();
}
}
class Entry {
final String key;
final String value;
final EntryMetadata entryMetadata;
Entry(String key, String value, EntryMetadata entryMetadata) {
this.key = key;
this.value = value;
this.entryMetadata = entryMetadata;
}
String getKey() {
return this.key;
}
String getValue() {
return this.value;
}
EntryMetadata getEntryMetadata() {
return this.entryMetadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Entry entry = (Entry) o;
return Objects.equals(this.key, entry.key) && Objects.equals(this.value, entry.value)
&& Objects.equals(this.entryMetadata, entry.entryMetadata);
}
@Override
public int hashCode() {
return Objects.hash(this.key, this.value, this.entryMetadata);
}
static List<Entry> fromBaggage(Baggage baggage) {
List<Entry> list = new ArrayList<>(baggage.size());
baggage.forEach((key, value, metadata) -> list.add(new Entry(key, value, metadata)));
return list;
}
}

View File

@@ -16,12 +16,18 @@
package org.springframework.cloud.sleuth.otel.bridge;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.ContextStorageProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -37,85 +43,136 @@ import org.springframework.lang.Nullable;
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class OtelCurrentTraceContext implements CurrentTraceContext {
public class OtelCurrentTraceContext implements CurrentTraceContext, ContextStorageProvider {
private static final Log log = LogFactory.getLog(OtelCurrentTraceContext.class);
final Tracer tracer;
private final ApplicationEventPublisher publisher;
public OtelCurrentTraceContext(Tracer tracer, ApplicationEventPublisher publisher) {
this.tracer = tracer;
private final ContextStorageProvider delegate = ContextStorage::get;
public OtelCurrentTraceContext(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public TraceContext get() {
Span currentSpan = this.tracer.getCurrentSpan();
if (DefaultSpan.getInvalid().equals(currentSpan)) {
public TraceContext context() {
Span currentSpan = Span.current();
if (Span.getInvalid().equals(currentSpan)) {
return null;
}
if (currentSpan instanceof SpanFromSpanContext) {
return new OtelTraceContext((SpanFromSpanContext) currentSpan);
}
return new OtelTraceContext(currentSpan);
}
@Override
public Scope newScope(TraceContext context) {
OtelTraceContext otelTraceContext = (OtelTraceContext) context;
if (otelTraceContext == null) {
return io.opentelemetry.context.Scope::noop;
}
Context current = Context.current();
Context old = otelTraceContext.context();
Span currentSpan = Span.fromContext(current);
Span oldSpan = Span.fromContext(otelTraceContext.context());
SpanContext spanContext = otelTraceContext.delegate;
Span fromContext = new SpanFromSpanContext(((OtelTraceContext) context).span, spanContext, otelTraceContext);
this.publisher.publishEvent(new ScopeChanged(this, context));
return new OtelScope(new OtelSpanInScope(this.tracer.withSpan(fromContext), spanContext), publisher);
boolean sameSpan = currentSpan.getSpanContext().equals(oldSpan.getSpanContext())
&& currentSpan.getSpanContext().equals(spanContext);
Baggage currentBaggage = Baggage.fromContext(current);
Baggage oldBaggage = Baggage.fromContext(old);
boolean sameBaggage = sameBaggage(currentBaggage, oldBaggage);
if (sameSpan && sameBaggage) {
return io.opentelemetry.context.Scope::noop;
}
SpanFromSpanContext fromContext = new SpanFromSpanContext(((OtelTraceContext) context).span, spanContext,
otelTraceContext);
Context newContext = old.with(fromContext).with(currentBaggage.toBuilder().setParent(old).build());
io.opentelemetry.context.Scope attach = get().attach(newContext);
return attach::close;
}
private boolean sameBaggage(Baggage currentBaggage, Baggage oldBaggage) {
Set<Entry> entries = new HashSet<>(Entry.fromBaggage(currentBaggage));
entries.removeAll(Entry.fromBaggage(oldBaggage));
return entries.isEmpty();
}
@Override
public Scope maybeScope(TraceContext context) {
if (log.isTraceEnabled()) {
log.trace("Will check if new scope should be created for context [" + context + "]");
}
if (context == null || SpanContext.getInvalid().equals(OtelTraceContext.toOtel(context))) {
if (log.isTraceEnabled()) {
log.trace("Invalid context - will return noop");
}
return new OtelScope.RevertToPrevious(this.publisher, null);
}
OtelTraceContext otelTraceContext = (OtelTraceContext) context;
Span fromContext = new SpanFromSpanContext(otelTraceContext.span, otelTraceContext.delegate, otelTraceContext);
Span currentSpan = this.tracer.getCurrentSpan();
if (log.isTraceEnabled()) {
log.trace("Span from context [" + fromContext + "], current span [" + currentSpan + "]");
}
if (traceAndSpanIdsAreEqual(fromContext, currentSpan)) {
if (log.isTraceEnabled()) {
log.trace("Same context as the current one - will return noop");
}
return new OtelScope.RevertToPrevious(this.publisher, context);
}
return newScope(context);
}
@Override
public <C> Callable<C> wrap(Callable<C> task) {
return get().current().wrap(task);
}
@Override
public Runnable wrap(Runnable task) {
return get().current().wrap(task);
}
@Override
public Executor wrap(Executor delegate) {
return get().current().wrap(delegate);
}
@Override
public ExecutorService wrap(ExecutorService delegate) {
return get().current().wrap(delegate);
}
private boolean traceAndSpanIdsAreEqual(Span fromContext, Span currentSpan) {
return fromContext.getContext().getTraceIdAsHexString().equals(currentSpan.getContext().getTraceIdAsHexString())
&& fromContext.getContext().getSpanIdAsHexString()
.equals(currentSpan.getContext().getSpanIdAsHexString());
return fromContext.getSpanContext().getTraceIdAsHexString()
.equals(currentSpan.getSpanContext().getTraceIdAsHexString())
&& fromContext.getSpanContext().getSpanIdAsHexString()
.equals(currentSpan.getSpanContext().getSpanIdAsHexString());
}
@Override
public ContextStorage get() {
ContextStorage threadLocalStorage = this.delegate.get();
return new ContextStorage() {
@Override
public io.opentelemetry.context.Scope attach(Context toAttach) {
io.opentelemetry.context.Scope scope = threadLocalStorage.attach(toAttach);
Span currentSpan = Span.fromContext(Context.current());
if (scope != io.opentelemetry.context.Scope.noop()) {
Span fromContext = Span.fromContext(toAttach);
publisher.publishEvent(new ScopeChanged(this, fromContext));
}
return () -> {
publisher.publishEvent(new OtelCurrentTraceContext.ScopeClosed(this));
publisher.publishEvent(new ScopeChanged(this, currentSpan));
scope.close();
};
}
@Override
public Context current() {
return threadLocalStorage.current();
}
};
}
public static class ScopeChanged extends ApplicationEvent {
/**
* Trace context corresponding to the changed scope. Might be {@code null}.
* Span corresponding to the changed scope. Might be {@code null}.
*/
public final TraceContext context;
public final Span span;
/**
* Create a new {@code ApplicationEvent}.
* @param source the object on which the event initially occurred or with which
* the event is associated (never {@code null})
* @param context corresponding trace context
* @param span corresponding trace context
*/
public ScopeChanged(Object source, @Nullable TraceContext context) {
public ScopeChanged(Object source, @Nullable Span span) {
super(source);
this.context = context;
this.span = span;
}
}
@@ -134,50 +191,3 @@ public class OtelCurrentTraceContext implements CurrentTraceContext {
}
}
class OtelScope implements CurrentTraceContext.Scope {
private final OtelSpanInScope delegate;
private final ApplicationEventPublisher publisher;
OtelScope(OtelSpanInScope delegate, ApplicationEventPublisher publisher) {
this.delegate = delegate;
this.publisher = publisher;
}
@Override
public void close() {
this.delegate.close();
this.publisher.publishEvent(new OtelCurrentTraceContext.ScopeClosed(this));
this.publisher.publishEvent(new OtelBaggageEntry.BaggageScopeEnded(this));
}
static class RevertToPrevious implements CurrentTraceContext.Scope {
private static final Log log = LogFactory.getLog(RevertToPrevious.class);
private static final LinkedBlockingDeque<TraceContext> CONTEXTS = new LinkedBlockingDeque<>();
private final ApplicationEventPublisher publisher;
RevertToPrevious(ApplicationEventPublisher publisher, TraceContext previous) {
this.publisher = publisher;
if (previous != null && !previous.equals(CONTEXTS.peekFirst())) {
CONTEXTS.addFirst(previous);
}
}
@Override
public void close() {
publisher.publishEvent(new OtelCurrentTraceContext.ScopeClosed(this));
TraceContext context = CONTEXTS.pollFirst();
if (log.isTraceEnabled()) {
log.trace("Reverting scope to [" + context + "]");
}
publisher.publishEvent(new OtelCurrentTraceContext.ScopeChanged(this, context));
}
}
}

View File

@@ -22,9 +22,9 @@ import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import io.opentelemetry.common.AttributeConsumer;
import io.opentelemetry.common.AttributeKey;
import io.opentelemetry.common.Attributes;
import io.opentelemetry.api.common.AttributeConsumer;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.trace.data.SpanData;
import org.springframework.cloud.sleuth.api.Span;
@@ -46,27 +46,31 @@ public class OtelFinishedSpan implements FinishedSpan {
this.spanData = spanData;
}
public static FinishedSpan fromOtel(SpanData span) {
return new OtelFinishedSpan(span);
}
@Override
public String name() {
public String getName() {
return this.spanData.getName();
}
@Override
public long startTimestamp() {
public long getStartTimestamp() {
return this.spanData.getStartEpochNanos();
}
@Override
public long endTimestamp() {
public long getEndTimestamp() {
return this.spanData.getEndEpochNanos();
}
@Override
public Map<String, String> tags() {
public Map<String, String> getTags() {
if (this.tags.isEmpty()) {
this.spanData.getAttributes().forEach(new AttributeConsumer() {
@Override
public <T> void consume(AttributeKey<T> key, T value) {
public <T> void accept(AttributeKey<T> key, T value) {
tags.put(key.getKey(), String.valueOf(value));
}
});
@@ -75,38 +79,38 @@ public class OtelFinishedSpan implements FinishedSpan {
}
@Override
public Collection<Map.Entry<Long, String>> events() {
public Collection<Map.Entry<Long, String>> getEvents() {
return this.spanData.getEvents().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getEpochNanos(), e.getName())).collect(Collectors.toList());
}
@Override
public String spanId() {
public String getSpanId() {
return this.spanData.getSpanId();
}
@Override
public String parentId() {
public String getParentId() {
return this.spanData.getParentSpanId();
}
@Override
public String remoteIp() {
return tags().get("net.peer.name");
public String getRemoteIp() {
return getTags().get("net.peer.name");
}
@Override
public int remotePort() {
return Integer.valueOf(tags().get("net.peer.port"));
public int getRemotePort() {
return Integer.valueOf(getTags().get("net.peer.port"));
}
@Override
public String traceId() {
public String getTraceId() {
return this.spanData.getTraceId();
}
@Override
public Throwable error() {
public Throwable getError() {
Attributes attributes = this.spanData.getEvents().stream().filter(e -> e.getName().equals("exception"))
.findFirst().map(e -> e.getAttributes()).orElse(null);
if (attributes != null) {
@@ -116,15 +120,15 @@ public class OtelFinishedSpan implements FinishedSpan {
}
@Override
public Span.Kind kind() {
if (this.spanData.getKind() == io.opentelemetry.trace.Span.Kind.INTERNAL) {
public Span.Kind getKind() {
if (this.spanData.getKind() == io.opentelemetry.api.trace.Span.Kind.INTERNAL) {
return null;
}
return Span.Kind.valueOf(this.spanData.getKind().name());
}
@Override
public String remoteServiceName() {
public String getRemoteServiceName() {
return this.spanData.getAttributes().get(AttributeKey.stringKey("peer.service"));
}
@@ -133,10 +137,6 @@ public class OtelFinishedSpan implements FinishedSpan {
return "SpanDataToReportedSpan{" + "spanData=" + spanData + ", tags=" + tags + '}';
}
public static FinishedSpan fromOtel(SpanData span) {
return new OtelFinishedSpan(span);
}
public static class AssertingThrowable extends Throwable {
/**

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.otel.bridge;
import java.util.Objects;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.metrics.spi.MeterProviderFactory;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.api.trace.spi.TracerProviderFactory;
import io.opentelemetry.context.propagation.ContextPropagators;
/**
* Sleuth implementation of a {@link OpenTelemetry}.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class OtelOpenTelemetry implements OpenTelemetry {
private final TracerProviderFactory tracerProviderFactory;
private final MeterProviderFactory meterProviderFactory;
private final TracerProvider tracerProvider;
private final MeterProvider meterProvider;
private final ContextPropagators contextPropagators;
public OtelOpenTelemetry(TracerProviderFactory tracerProviderFactory, MeterProviderFactory meterProviderFactory,
TracerProvider tracerProvider, MeterProvider meterProvider, ContextPropagators contextPropagators) {
this.tracerProviderFactory = tracerProviderFactory;
this.meterProviderFactory = meterProviderFactory;
this.tracerProvider = tracerProvider;
this.meterProvider = meterProvider;
this.contextPropagators = contextPropagators;
}
@Override
public TracerProvider getTracerProvider() {
return this.tracerProvider;
}
@Override
public MeterProvider getMeterProvider() {
return this.meterProvider;
}
@Override
public ContextPropagators getPropagators() {
return this.contextPropagators;
}
@Override
public io.opentelemetry.api.OpenTelemetry.Builder<OtelOpenTelemetry.Builder> toBuilder() {
return new Builder(this.meterProviderFactory, this.tracerProviderFactory);
}
static class Builder implements io.opentelemetry.api.OpenTelemetry.Builder<OtelOpenTelemetry.Builder> {
private ContextPropagators propagators;
private TracerProvider tracerProvider;
private MeterProvider meterProvider;
private final MeterProviderFactory meterProviderFactory;
private final TracerProviderFactory tracerProviderFactory;
Builder(MeterProviderFactory meterProviderFactory, TracerProviderFactory tracerProviderFactory) {
this.meterProviderFactory = meterProviderFactory;
this.tracerProviderFactory = tracerProviderFactory;
}
public OtelOpenTelemetry.Builder setTracerProvider(TracerProvider tracerProvider) {
Objects.requireNonNull(tracerProvider, "tracerProvider");
this.tracerProvider = tracerProvider;
return this;
}
public OtelOpenTelemetry.Builder setMeterProvider(MeterProvider meterProvider) {
Objects.requireNonNull(meterProvider, "meterProvider");
this.meterProvider = meterProvider;
return this;
}
public OtelOpenTelemetry.Builder setPropagators(ContextPropagators propagators) {
Objects.requireNonNull(propagators, "propagators");
this.propagators = propagators;
return this;
}
public OpenTelemetry build() {
MeterProvider meterProvider = this.meterProvider;
if (meterProvider == null) {
meterProvider = this.meterProviderFactory.create();
}
TracerProvider tracerProvider = this.tracerProvider;
if (tracerProvider == null) {
tracerProvider = this.tracerProviderFactory.create();
}
return new OtelOpenTelemetry(tracerProviderFactory, meterProviderFactory, tracerProvider, meterProvider,
this.propagators);
}
}
}

View File

@@ -18,12 +18,10 @@ package org.springframework.cloud.sleuth.otel.bridge;
import java.util.List;
import io.grpc.Context;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.TracingContextUtils;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.TraceContext;
@@ -59,9 +57,19 @@ public class OtelPropagator implements Propagator {
@Override
public <C> Span.Builder extract(C carrier, Getter<C> getter) {
Context extracted = this.propagator.extract(Context.current(), carrier, getter::get);
io.opentelemetry.trace.Span span = TracingContextUtils.getSpanWithoutDefault(extracted);
if (span == null || span.equals(DefaultSpan.getInvalid())) {
Context extracted = this.propagator.extract(Context.current(), carrier, new TextMapPropagator.Getter<C>() {
@Override
public Iterable<String> keys(C carrier) {
return fields();
}
@Override
public String get(C carrier, String key) {
return getter.get(carrier, key);
}
});
io.opentelemetry.api.trace.Span span = io.opentelemetry.api.trace.Span.fromContextOrNull(extracted);
if (span == null || span.equals(io.opentelemetry.api.trace.Span.getInvalid())) {
return OtelSpanBuilder.fromOtel(tracer.spanBuilder(""));
}
return OtelSpanBuilder.fromOtel(this.tracer.spanBuilder("").setParent(extracted));

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.otel.bridge;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import org.springframework.cloud.sleuth.api.ScopedSpan;
import org.springframework.cloud.sleuth.api.TraceContext;

View File

@@ -17,12 +17,13 @@
package org.springframework.cloud.sleuth.otel.bridge;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import io.opentelemetry.common.AttributeKey;
import io.opentelemetry.common.Attributes;
import io.opentelemetry.trace.EndSpanOptions;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.StatusCanonicalCode;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.TraceContext;
@@ -36,10 +37,36 @@ import org.springframework.lang.Nullable;
*/
public class OtelSpan implements Span {
final io.opentelemetry.trace.Span delegate;
final io.opentelemetry.api.trace.Span delegate;
public OtelSpan(io.opentelemetry.trace.Span delegate) {
private final AtomicReference<Context> context;
public OtelSpan(io.opentelemetry.api.trace.Span delegate) {
this.delegate = delegate;
if (delegate instanceof SpanFromSpanContext) {
SpanFromSpanContext fromSpanContext = (SpanFromSpanContext) delegate;
this.context = fromSpanContext.otelTraceContext.context;
}
else {
this.context = new AtomicReference<>(Context.current());
}
}
public OtelSpan(io.opentelemetry.api.trace.Span delegate, Context context) {
this.delegate = delegate;
this.context = new AtomicReference<>(context);
}
public static io.opentelemetry.api.trace.Span toOtel(Span span) {
return ((OtelSpan) span).delegate;
}
public static Span fromOtel(io.opentelemetry.api.trace.Span span) {
return new OtelSpan(span);
}
public static Span fromOtel(io.opentelemetry.api.trace.Span span, Context context) {
return new OtelSpan(span, context);
}
@Override
@@ -52,7 +79,7 @@ public class OtelSpan implements Span {
if (this.delegate == null) {
return null;
}
return new OtelTraceContext(this.delegate.getContext(), this.delegate);
return new OtelTraceContext(this.context, this.delegate.getSpanContext(), this.delegate);
}
@Override
@@ -100,25 +127,17 @@ public class OtelSpan implements Span {
return this.delegate != null ? this.delegate.toString() : "null";
}
public static io.opentelemetry.trace.Span toOtel(Span span) {
return ((OtelSpan) span).delegate;
}
public static Span fromOtel(io.opentelemetry.trace.Span span) {
return new OtelSpan(span);
}
}
class SpanFromSpanContext implements io.opentelemetry.trace.Span {
class SpanFromSpanContext implements io.opentelemetry.api.trace.Span {
final io.opentelemetry.trace.Span span;
final io.opentelemetry.api.trace.Span span;
final SpanContext newSpanContext;
final OtelTraceContext otelTraceContext;
SpanFromSpanContext(io.opentelemetry.trace.Span span, SpanContext newSpanContext,
SpanFromSpanContext(io.opentelemetry.api.trace.Span span, SpanContext newSpanContext,
OtelTraceContext otelTraceContext) {
this.span = span;
this.newSpanContext = newSpanContext;
@@ -126,78 +145,78 @@ class SpanFromSpanContext implements io.opentelemetry.trace.Span {
}
@Override
public void setAttribute(String key, @Nullable String value) {
span.setAttribute(key, value);
public io.opentelemetry.api.trace.Span setAttribute(String key, @Nullable String value) {
return span.setAttribute(key, value);
}
@Override
public void setAttribute(String key, long value) {
span.setAttribute(key, value);
public io.opentelemetry.api.trace.Span setAttribute(String key, long value) {
return span.setAttribute(key, value);
}
@Override
public void setAttribute(String key, double value) {
span.setAttribute(key, value);
public io.opentelemetry.api.trace.Span setAttribute(String key, double value) {
return span.setAttribute(key, value);
}
@Override
public void setAttribute(String key, boolean value) {
span.setAttribute(key, value);
public io.opentelemetry.api.trace.Span setAttribute(String key, boolean value) {
return span.setAttribute(key, value);
}
@Override
public void addEvent(String name) {
span.addEvent(name);
public io.opentelemetry.api.trace.Span addEvent(String name) {
return span.addEvent(name);
}
@Override
public void addEvent(String name, long timestamp) {
span.addEvent(name, timestamp);
public io.opentelemetry.api.trace.Span addEvent(String name, long timestamp) {
return span.addEvent(name, timestamp);
}
@Override
public void addEvent(String name, Attributes attributes) {
span.addEvent(name, attributes);
public io.opentelemetry.api.trace.Span addEvent(String name, Attributes attributes) {
return span.addEvent(name, attributes);
}
@Override
public void addEvent(String name, Attributes attributes, long timestamp) {
span.addEvent(name, attributes, timestamp);
public io.opentelemetry.api.trace.Span addEvent(String name, Attributes attributes, long timestamp) {
return span.addEvent(name, attributes, timestamp);
}
@Override
public void setAttribute(AttributeKey<Long> key, int value) {
span.setAttribute(key, value);
public io.opentelemetry.api.trace.Span setStatus(StatusCode canonicalCode) {
return span.setStatus(canonicalCode);
}
@Override
public <T> void setAttribute(AttributeKey<T> key, T value) {
span.setAttribute(key, value);
public io.opentelemetry.api.trace.Span setStatus(StatusCode canonicalCode, String description) {
return span.setStatus(canonicalCode, description);
}
@Override
public void setStatus(StatusCanonicalCode canonicalCode) {
span.setStatus(canonicalCode);
public io.opentelemetry.api.trace.Span setAttribute(AttributeKey<Long> key, int value) {
return span.setAttribute(key, value);
}
@Override
public void setStatus(StatusCanonicalCode canonicalCode, String description) {
span.setStatus(canonicalCode, description);
public <T> io.opentelemetry.api.trace.Span setAttribute(AttributeKey<T> key, T value) {
return span.setAttribute(key, value);
}
@Override
public void recordException(Throwable exception) {
span.recordException(exception);
public io.opentelemetry.api.trace.Span recordException(Throwable exception) {
return span.recordException(exception);
}
@Override
public void recordException(Throwable exception, Attributes additionalAttributes) {
span.recordException(exception, additionalAttributes);
public io.opentelemetry.api.trace.Span recordException(Throwable exception, Attributes additionalAttributes) {
return span.recordException(exception, additionalAttributes);
}
@Override
public void updateName(String name) {
span.updateName(name);
public io.opentelemetry.api.trace.Span updateName(String name) {
return span.updateName(name);
}
@Override
@@ -206,12 +225,12 @@ class SpanFromSpanContext implements io.opentelemetry.trace.Span {
}
@Override
public void end(EndSpanOptions endOptions) {
span.end(endOptions);
public void end(long l) {
span.end(l);
}
@Override
public SpanContext getContext() {
public SpanContext getSpanContext() {
return newSpanContext;
}

View File

@@ -31,18 +31,22 @@ import org.springframework.util.StringUtils;
*/
public class OtelSpanBuilder implements Span.Builder {
private final io.opentelemetry.trace.Span.Builder delegate;
private final io.opentelemetry.api.trace.Span.Builder delegate;
private final List<String> annotations = new LinkedList<>();
private String name;
private Throwable error;
private final List<String> annotations = new LinkedList<>();
public OtelSpanBuilder(io.opentelemetry.trace.Span.Builder delegate) {
public OtelSpanBuilder(io.opentelemetry.api.trace.Span.Builder delegate) {
this.delegate = delegate;
}
public static Span.Builder fromOtel(io.opentelemetry.api.trace.Span.Builder builder) {
return new OtelSpanBuilder(builder);
}
@Override
public Span.Builder setParent(TraceContext context) {
this.delegate.setParent(OtelTraceContext.toOtelContext(context));
@@ -82,22 +86,22 @@ public class OtelSpanBuilder implements Span.Builder {
@Override
public Span.Builder kind(Span.Kind spanKind) {
if (spanKind == null) {
this.delegate.setSpanKind(io.opentelemetry.trace.Span.Kind.INTERNAL);
this.delegate.setSpanKind(io.opentelemetry.api.trace.Span.Kind.INTERNAL);
return this;
}
io.opentelemetry.trace.Span.Kind kind = io.opentelemetry.trace.Span.Kind.INTERNAL;
io.opentelemetry.api.trace.Span.Kind kind = io.opentelemetry.api.trace.Span.Kind.INTERNAL;
switch (spanKind) {
case CLIENT:
kind = io.opentelemetry.trace.Span.Kind.CLIENT;
kind = io.opentelemetry.api.trace.Span.Kind.CLIENT;
break;
case SERVER:
kind = io.opentelemetry.trace.Span.Kind.SERVER;
kind = io.opentelemetry.api.trace.Span.Kind.SERVER;
break;
case PRODUCER:
kind = io.opentelemetry.trace.Span.Kind.PRODUCER;
kind = io.opentelemetry.api.trace.Span.Kind.PRODUCER;
break;
case CONSUMER:
kind = io.opentelemetry.trace.Span.Kind.CONSUMER;
kind = io.opentelemetry.api.trace.Span.Kind.CONSUMER;
break;
}
this.delegate.setSpanKind(kind);
@@ -112,7 +116,7 @@ public class OtelSpanBuilder implements Span.Builder {
@Override
public Span start() {
io.opentelemetry.trace.Span span = this.delegate.startSpan();
io.opentelemetry.api.trace.Span span = this.delegate.startSpan();
if (StringUtils.hasText(this.name)) {
span.updateName(this.name);
}
@@ -123,8 +127,4 @@ public class OtelSpanBuilder implements Span.Builder {
return OtelSpan.fromOtel(span);
}
public static Span.Builder fromOtel(io.opentelemetry.trace.Span.Builder builder) {
return new OtelSpanBuilder(builder);
}
}

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.otel.bridge;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import org.springframework.cloud.sleuth.api.SpanCustomizer;
import org.springframework.lang.NonNull;
@@ -51,7 +51,7 @@ public class OtelSpanCustomizer implements SpanCustomizer {
}
private Span currentSpan() {
return this.span != null ? this.span : this.tracer.getCurrentSpan();
return this.span != null ? this.span : Span.current();
}
@Override

View File

@@ -17,12 +17,12 @@
package org.springframework.cloud.sleuth.otel.bridge;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import io.grpc.Context;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.TracingContextUtils;
import org.springframework.cloud.sleuth.api.TraceContext;
import org.springframework.lang.Nullable;
@@ -35,17 +35,48 @@ import org.springframework.lang.Nullable;
*/
public class OtelTraceContext implements TraceContext {
final AtomicReference<Context> context;
final SpanContext delegate;
final Span span;
public OtelTraceContext(Context context, SpanContext delegate, @Nullable Span span) {
this(new AtomicReference<>(context), delegate, span);
}
OtelTraceContext(AtomicReference<Context> context, SpanContext delegate, @Nullable Span span) {
this.context = context;
this.delegate = delegate;
this.span = span;
}
public OtelTraceContext(SpanContext delegate, @Nullable Span span) {
this.context = new AtomicReference<>(Context.current());
this.delegate = delegate;
this.span = span;
}
public OtelTraceContext(Span span) {
this(span.getContext(), span);
this(Context.current(), span.getSpanContext(), span);
}
public OtelTraceContext(SpanFromSpanContext span) {
this(span.otelTraceContext.context.get(), span.getSpanContext(), span);
}
public static TraceContext fromOtel(SpanContext traceContext) {
return new OtelTraceContext(traceContext, null);
}
public static Context toOtelContext(TraceContext context) {
if (context instanceof OtelTraceContext) {
Span span = ((OtelTraceContext) context).span;
if (span != null) {
return span.storeInContext(Context.current());
}
}
return Context.current();
}
@Override
@@ -92,29 +123,12 @@ public class OtelTraceContext implements TraceContext {
return this.span;
}
public SpanContext spanContext() {
return this.delegate;
public Context context() {
return this.context.get();
}
public static SpanContext toOtel(TraceContext traceContext) {
if (traceContext == null) {
return null;
}
return ((OtelTraceContext) traceContext).delegate;
}
public static TraceContext fromOtel(SpanContext traceContext) {
return new OtelTraceContext(traceContext, null);
}
public static Context toOtelContext(TraceContext context) {
if (context instanceof OtelTraceContext) {
Span span = ((OtelTraceContext) context).span;
if (span != null) {
return TracingContextUtils.withSpan(span, Context.current());
}
}
return Context.current();
public void updateContext(Context context) {
this.context.set(context);
}
}

View File

@@ -18,16 +18,16 @@ package org.springframework.cloud.sleuth.otel.bridge;
import java.util.Map;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.SpanContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.sleuth.api.BaggageEntry;
import org.springframework.cloud.sleuth.api.BaggageInScope;
import org.springframework.cloud.sleuth.api.ScopedSpan;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.SpanCustomizer;
import org.springframework.cloud.sleuth.api.TraceContext;
import org.springframework.cloud.sleuth.api.Tracer;
/**
@@ -38,15 +38,19 @@ import org.springframework.cloud.sleuth.api.Tracer;
*/
public class OtelTracer implements Tracer {
private final io.opentelemetry.trace.Tracer tracer;
private final io.opentelemetry.api.trace.Tracer tracer;
private final OtelBaggageManager otelBaggageManager;
public OtelTracer(io.opentelemetry.trace.Tracer tracer, OtelBaggageManager otelBaggageManager) {
public OtelTracer(io.opentelemetry.api.trace.Tracer tracer, OtelBaggageManager otelBaggageManager) {
this.tracer = tracer;
this.otelBaggageManager = otelBaggageManager;
}
public static Tracer fromOtel(io.opentelemetry.api.trace.Tracer tracer, OtelBaggageManager otelBaggageManager) {
return new OtelTracer(tracer, otelBaggageManager);
}
@Override
public Span nextSpan(Span parent) {
if (parent == null) {
@@ -58,9 +62,9 @@ public class OtelTracer implements Tracer {
@Override
public SpanInScope withSpan(Span span) {
return new OtelSpanInScope(
tracer.withSpan(span == null ? DefaultSpan.getInvalid() : ((OtelSpan) span).delegate),
((OtelSpan) span).delegate.getContext());
io.opentelemetry.api.trace.Span delegate = span == null ? io.opentelemetry.api.trace.Span.getInvalid()
: ((OtelSpan) span).delegate;
return new OtelSpanInScope((OtelSpan) span, delegate);
}
@Override
@@ -70,8 +74,8 @@ public class OtelTracer implements Tracer {
@Override
public Span currentSpan() {
io.opentelemetry.trace.Span currentSpan = this.tracer.getCurrentSpan();
if (currentSpan == null || currentSpan.equals(DefaultSpan.getInvalid())) {
io.opentelemetry.api.trace.Span currentSpan = io.opentelemetry.api.trace.Span.current();
if (currentSpan == null || currentSpan.equals(io.opentelemetry.api.trace.Span.getInvalid())) {
return null;
}
return new OtelSpan(currentSpan);
@@ -84,8 +88,8 @@ public class OtelTracer implements Tracer {
@Override
public ScopedSpan startScopedSpan(String name) {
io.opentelemetry.trace.Span span = this.tracer.spanBuilder(name).startSpan();
return new OtelScopedSpan(span, this.tracer.withSpan(span));
io.opentelemetry.api.trace.Span span = this.tracer.spanBuilder(name).startSpan();
return new OtelScopedSpan(span, span.makeCurrent());
}
@Override
@@ -93,25 +97,31 @@ public class OtelTracer implements Tracer {
return new OtelSpanBuilder(this.tracer.spanBuilder(""));
}
public static Tracer fromOtel(io.opentelemetry.trace.Tracer tracer, OtelBaggageManager otelBaggageManager) {
return new OtelTracer(tracer, otelBaggageManager);
}
@Override
public Map<String, String> getAllBaggage() {
return this.otelBaggageManager.getAllBaggage();
}
@Override
public BaggageEntry getBaggage(String name) {
public BaggageInScope getBaggage(String name) {
return this.otelBaggageManager.getBaggage(name);
}
@Override
public BaggageEntry createBaggage(String name) {
public BaggageInScope getBaggage(TraceContext traceContext, String name) {
return this.otelBaggageManager.getBaggage(traceContext, name);
}
@Override
public BaggageInScope createBaggage(String name) {
return this.otelBaggageManager.createBaggage(name);
}
@Override
public BaggageInScope createBaggage(String name, String value) {
return this.otelBaggageManager.createBaggage(name, value);
}
}
class OtelSpanInScope implements Tracer.SpanInScope {
@@ -120,11 +130,17 @@ class OtelSpanInScope implements Tracer.SpanInScope {
final Scope delegate;
final OtelSpan sleuthSpan;
final io.opentelemetry.api.trace.Span otelSpan;
final SpanContext spanContext;
OtelSpanInScope(Scope delegate, SpanContext spanContext) {
this.delegate = delegate;
this.spanContext = spanContext;
OtelSpanInScope(OtelSpan sleuthSpan, io.opentelemetry.api.trace.Span otelSpan) {
this.sleuthSpan = sleuthSpan;
this.otelSpan = otelSpan;
this.delegate = otelSpan.makeCurrent();
this.spanContext = otelSpan.getSpanContext();
}
@Override

View File

@@ -16,12 +16,12 @@
package org.springframework.cloud.sleuth.otel.bridge;
import io.opentelemetry.baggage.BaggageManager;
import io.opentelemetry.context.propagation.ContextPropagators;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.api.CurrentTraceContext;
import org.springframework.cloud.sleuth.api.SpanCustomizer;
@@ -41,40 +41,39 @@ import org.springframework.context.annotation.Configuration;
* @author Marcin Grzejszczak
* @since 3.0.0
*/
@Configuration(proxyBeanMethods = true)
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
@ConditionalOnBean(io.opentelemetry.trace.Tracer.class)
@ConditionalOnBean(io.opentelemetry.api.trace.Tracer.class)
@AutoConfigureAfter(TraceOtelAutoConfiguration.class)
@AutoConfigureBefore(TraceAutoConfiguration.class)
public class TraceOtelBridgeAutoConfiguation {
@Bean
Tracer otelTracerBridge(io.opentelemetry.trace.Tracer tracer, BaggageManager baggageManager,
SleuthBaggageProperties sleuthBaggageProperties, ApplicationEventPublisher publisher) {
return new OtelTracer(tracer,
otelBaggageManagerBridge(tracer, baggageManager, sleuthBaggageProperties, publisher));
Tracer otelTracerBridge(io.opentelemetry.api.trace.Tracer tracer, OtelBaggageManager otelBaggageManager) {
return new OtelTracer(tracer, otelBaggageManager);
}
@Bean(autowireCandidate = false)
OtelBaggageManager otelBaggageManagerBridge(io.opentelemetry.trace.Tracer tracer, BaggageManager baggageManager,
SleuthBaggageProperties sleuthBaggageProperties, ApplicationEventPublisher publisher) {
return new OtelBaggageManager(tracer, baggageManager, sleuthBaggageProperties, publisher);
// Both CurrentTraceContext & ContextStorageProvider
@Bean
@ConditionalOnMissingBean
OtelCurrentTraceContext otelCurrentTraceContext(ApplicationEventPublisher publisher) {
return new OtelCurrentTraceContext(publisher);
}
@Bean
CurrentTraceContext otelCurrentTraceContext(io.opentelemetry.trace.Tracer tracer,
ApplicationEventPublisher publisher) {
return new OtelCurrentTraceContext(tracer, publisher);
}
@Bean
SpanCustomizer otelSpanCustomizer(io.opentelemetry.trace.Tracer tracer) {
SpanCustomizer otelSpanCustomizer(io.opentelemetry.api.trace.Tracer tracer) {
return new OtelSpanCustomizer(tracer);
}
@Bean
Propagator otelPropagator(ContextPropagators contextPropagators, io.opentelemetry.trace.Tracer tracer) {
Propagator otelPropagator(ContextPropagators contextPropagators, io.opentelemetry.api.trace.Tracer tracer) {
return new OtelPropagator(contextPropagators, tracer);
}
@Bean
OtelBaggageManager otelBaggageManager(CurrentTraceContext currentTraceContext,
SleuthBaggageProperties sleuthBaggageProperties, ApplicationEventPublisher publisher) {
return new OtelBaggageManager(currentTraceContext, sleuthBaggageProperties, publisher);
}
}

View File

@@ -19,11 +19,10 @@ package org.springframework.cloud.sleuth.otel.bridge.http;
import java.net.URI;
import java.net.URISyntaxException;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Tracer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -72,9 +71,9 @@ public class OtelHttpClientHandler extends HttpClientTracer<HttpClientRequest, H
if (log.isDebugEnabled()) {
log.debug("The sampler function filtered this request, will return an invalid span");
}
return OtelSpan.fromOtel(DefaultSpan.getInvalid());
return OtelSpan.fromOtel(io.opentelemetry.api.trace.Span.getInvalid());
}
io.opentelemetry.trace.Span span = startSpan(request);
io.opentelemetry.api.trace.Span span = startSpan(request);
return span(request, span);
}
@@ -84,19 +83,19 @@ public class OtelHttpClientHandler extends HttpClientTracer<HttpClientRequest, H
if (log.isDebugEnabled()) {
log.debug("Returning an invalid span since url [" + request.path() + "] is on a list of urls to skip");
}
return OtelSpan.fromOtel(DefaultSpan.getInvalid());
return OtelSpan.fromOtel(io.opentelemetry.api.trace.Span.getInvalid());
}
io.opentelemetry.trace.Span span = parent != null ? ((OtelTraceContext) parent).span() : null;
io.opentelemetry.api.trace.Span span = parent != null ? ((OtelTraceContext) parent).span() : null;
if (span == null) {
return span(request, startSpan(request));
}
try (Scope scope = this.tracer.withSpan(span)) {
io.opentelemetry.trace.Span withParent = startSpan(request);
try (Scope scope = span.makeCurrent()) {
io.opentelemetry.api.trace.Span withParent = startSpan(request);
return span(request, withParent);
}
}
private Span span(HttpClientRequest request, io.opentelemetry.trace.Span span) {
private Span span(HttpClientRequest request, io.opentelemetry.api.trace.Span span) {
try (Scope scope = startScope(span, request)) {
if (span.isRecording()) {
String remoteIp = request.remoteIp();
@@ -110,9 +109,9 @@ public class OtelHttpClientHandler extends HttpClientTracer<HttpClientRequest, H
}
@Override
protected io.opentelemetry.trace.Span onRequest(io.opentelemetry.trace.Span span,
protected io.opentelemetry.api.trace.Span onRequest(io.opentelemetry.api.trace.Span span,
HttpClientRequest httpClientRequest) {
io.opentelemetry.trace.Span afterRequest = super.onRequest(span, httpClientRequest);
io.opentelemetry.api.trace.Span afterRequest = super.onRequest(span, httpClientRequest);
if (this.httpClientRequestParser != null) {
Span fromOtel = OtelSpan.fromOtel(afterRequest);
this.httpClientRequestParser.parse(httpClientRequest, fromOtel.context(), fromOtel);
@@ -125,9 +124,9 @@ public class OtelHttpClientHandler extends HttpClientTracer<HttpClientRequest, H
}
@Override
protected io.opentelemetry.trace.Span onResponse(io.opentelemetry.trace.Span span,
protected io.opentelemetry.api.trace.Span onResponse(io.opentelemetry.api.trace.Span span,
HttpClientResponse httpClientResponse) {
io.opentelemetry.trace.Span afterResponse = super.onResponse(span, httpClientResponse);
io.opentelemetry.api.trace.Span afterResponse = super.onResponse(span, httpClientResponse);
if (this.httpClientResponseParser != null) {
Span fromOtel = OtelSpan.fromOtel(afterResponse);
this.httpClientResponseParser.parse(httpClientResponse, fromOtel.context(), fromOtel);
@@ -137,13 +136,13 @@ public class OtelHttpClientHandler extends HttpClientTracer<HttpClientRequest, H
@Override
public void handleReceive(HttpClientResponse response, Span span) {
if (OtelSpan.toOtel(span).equals(DefaultSpan.getInvalid())) {
if (OtelSpan.toOtel(span).equals(io.opentelemetry.api.trace.Span.getInvalid())) {
if (log.isDebugEnabled()) {
log.debug("Not doing anything cause the span is invalid");
}
return;
}
io.opentelemetry.trace.Span otel = OtelSpan.toOtel(span);
io.opentelemetry.api.trace.Span otel = OtelSpan.toOtel(span);
if (response.error() != null) {
if (log.isDebugEnabled()) {
log.debug("There was an error, will finish span [" + otel + "] exceptionally");

View File

@@ -19,14 +19,12 @@ package org.springframework.cloud.sleuth.otel.bridge.http;
import java.net.URI;
import java.util.regex.Pattern;
import io.grpc.Context;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Tracer;
import org.springframework.cloud.sleuth.api.Span;
import org.springframework.cloud.sleuth.api.http.HttpRequest;
import org.springframework.cloud.sleuth.api.http.HttpRequestParser;
import org.springframework.cloud.sleuth.api.http.HttpResponseParser;
import org.springframework.cloud.sleuth.api.http.HttpServerHandler;
@@ -65,15 +63,16 @@ public class OtelHttpServerHandler
String url = request.path();
boolean shouldSkip = !StringUtils.isEmpty(url) && this.pattern.matcher(url).matches();
if (shouldSkip) {
return OtelSpan.fromOtel(DefaultSpan.getInvalid());
return OtelSpan.fromOtel(io.opentelemetry.api.trace.Span.getInvalid());
}
return OtelSpan.fromOtel(startSpan(request, request, request.method()));
Context context = startSpan(request, request, request.method());
return OtelSpan.fromOtel(io.opentelemetry.api.trace.Span.fromContext(context), context);
}
@Override
public void handleSend(HttpServerResponse response, Span span) {
Throwable throwable = response.error();
io.opentelemetry.trace.Span otel = OtelSpan.toOtel(span);
io.opentelemetry.api.trace.Span otel = OtelSpan.toOtel(span);
parseResponse(span, response);
if (throwable == null) {
end(otel, response);
@@ -90,7 +89,7 @@ public class OtelHttpServerHandler
}
@Override
protected void onConnectionAndRequest(io.opentelemetry.trace.Span span, HttpServerRequest connection,
protected void onConnectionAndRequest(io.opentelemetry.api.trace.Span span, HttpServerRequest connection,
HttpServerRequest request) {
super.onConnectionAndRequest(span, connection, request);
if (this.httpServerRequestParser != null) {
@@ -100,7 +99,7 @@ public class OtelHttpServerHandler
}
@Override
protected void onRequest(io.opentelemetry.trace.Span span, HttpServerRequest request) {
protected void onRequest(io.opentelemetry.api.trace.Span span, HttpServerRequest request) {
super.onRequest(span, request);
String path = request.path();
if (StringUtils.hasText(path)) {
@@ -131,7 +130,17 @@ public class OtelHttpServerHandler
@Override
protected TextMapPropagator.Getter<HttpServerRequest> getGetter() {
return HttpRequest::header;
return new TextMapPropagator.Getter<HttpServerRequest>() {
@Override
public Iterable<String> keys(HttpServerRequest carrier) {
return carrier.headerNames();
}
@Override
public String get(HttpServerRequest carrier, String key) {
return carrier.header(key);
}
};
}
@Override

View File

@@ -46,13 +46,13 @@ import org.springframework.lang.Nullable;
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
@ConditionalOnBean({ Tracer.class, io.opentelemetry.trace.Tracer.class })
@ConditionalOnBean({ Tracer.class, io.opentelemetry.api.trace.Tracer.class })
@AutoConfigureBefore(TraceHttpAutoConfiguration.class)
@AutoConfigureAfter({ TraceOtelBridgeAutoConfiguation.class, SkipPatternConfiguration.class })
public class TraceOtelHttpBridgeAutoConfiguration {
@Bean
HttpClientHandler otelHttpClientHandler(io.opentelemetry.trace.Tracer tracer,
HttpClientHandler otelHttpClientHandler(io.opentelemetry.api.trace.Tracer tracer,
@Nullable @HttpClientRequestParser HttpRequestParser httpClientRequestParser,
@Nullable @HttpClientResponseParser HttpResponseParser httpClientResponseParser,
SamplerFunction<HttpRequest> samplerFunction) {
@@ -60,7 +60,7 @@ public class TraceOtelHttpBridgeAutoConfiguration {
}
@Bean
HttpServerHandler otelHttpServerHandler(io.opentelemetry.trace.Tracer tracer,
HttpServerHandler otelHttpServerHandler(io.opentelemetry.api.trace.Tracer tracer,
@Nullable @HttpServerRequestParser HttpRequestParser httpServerRequestParser,
@Nullable @HttpServerResponseParser HttpResponseParser httpServerResponseParser,
SkipPatternProvider skipPatternProvider) {

View File

@@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
@@ -32,7 +33,7 @@ public class ArrayListSpanProcessor implements SpanProcessor, SpanExporter {
Queue<SpanData> spans = new LinkedBlockingQueue<>();
@Override
public void onStart(ReadWriteSpan span) {
public void onStart(Context parent, ReadWriteSpan span) {
}

View File

@@ -17,11 +17,11 @@
package org.springframework.cloud.sleuth.otel.log;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import io.opentelemetry.baggage.BaggageManager;
import io.opentelemetry.baggage.Entry;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
@@ -31,7 +31,8 @@ import org.apache.commons.logging.LogFactory;
import org.slf4j.MDC;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.cloud.sleuth.otel.bridge.OtelBaggageEntry;
import org.springframework.cloud.sleuth.otel.bridge.OtelBaggageInScope;
import org.springframework.cloud.sleuth.otel.bridge.OtelBaggageManager;
import org.springframework.cloud.sleuth.otel.bridge.OtelCurrentTraceContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
@@ -42,16 +43,16 @@ class Slf4jSpanProcessor implements SpanProcessor, ApplicationListener {
private final SleuthBaggageProperties sleuthBaggageProperties;
private final BaggageManager baggageManager;
private final OtelBaggageManager baggageManager;
Slf4jSpanProcessor(SleuthBaggageProperties sleuthBaggageProperties, BaggageManager baggageManager) {
Slf4jSpanProcessor(SleuthBaggageProperties sleuthBaggageProperties, OtelBaggageManager baggageManager) {
this.sleuthBaggageProperties = sleuthBaggageProperties;
this.baggageManager = baggageManager;
}
@Override
public void onStart(ReadWriteSpan span) {
onStart(span.getContext().getTraceIdAsHexString(), span.getContext().getSpanIdAsHexString());
public void onStart(Context parent, ReadWriteSpan span) {
onStart(span.getSpanContext().getTraceIdAsHexString(), span.getSpanContext().getSpanIdAsHexString());
}
private void onStart(String traceId, String spanId) {
@@ -93,10 +94,10 @@ class Slf4jSpanProcessor implements SpanProcessor, ApplicationListener {
onEachCorrelatedBaggageEntry(e -> MDC.put(e.getKey(), e.getValue()));
}
private void onEachCorrelatedBaggageEntry(Consumer<Entry> consumer) {
private void onEachCorrelatedBaggageEntry(Consumer<Map.Entry<String, String>> consumer) {
if (this.sleuthBaggageProperties.isCorrelationEnabled()) {
List<String> correlationFields = lowerCaseCorrelationFields();
this.baggageManager.getCurrentBaggage().getEntries().stream()
this.baggageManager.getAllBaggage().entrySet().stream()
.filter(e -> correlationFields.contains(e.getKey().toLowerCase())).forEach(consumer);
}
}
@@ -110,7 +111,7 @@ class Slf4jSpanProcessor implements SpanProcessor, ApplicationListener {
.collect(Collectors.toList());
}
private void onBaggageChanged(OtelBaggageEntry.BaggageChanged event) {
private void onBaggageChanged(OtelBaggageInScope.BaggageChanged event) {
if (log.isTraceEnabled()) {
log.trace("Got baggage changed event [" + event + "]");
}
@@ -128,8 +129,9 @@ class Slf4jSpanProcessor implements SpanProcessor, ApplicationListener {
if (log.isTraceEnabled()) {
log.trace("Got scope changed event [" + event + "]");
}
if (event.context != null) {
onStart(event.context.traceId(), event.context.spanId());
if (event.span != null) {
onStart(event.span.getSpanContext().getTraceIdAsHexString(),
event.span.getSpanContext().getSpanIdAsHexString());
}
}
@@ -142,8 +144,8 @@ class Slf4jSpanProcessor implements SpanProcessor, ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof OtelBaggageEntry.BaggageChanged) {
onBaggageChanged((OtelBaggageEntry.BaggageChanged) event);
if (event instanceof OtelBaggageInScope.BaggageChanged) {
onBaggageChanged((OtelBaggageInScope.BaggageChanged) event);
}
else if (event instanceof OtelCurrentTraceContext.ScopeChanged) {
onScopeChanged((OtelCurrentTraceContext.ScopeChanged) event);
@@ -151,9 +153,6 @@ class Slf4jSpanProcessor implements SpanProcessor, ApplicationListener {
else if (event instanceof OtelCurrentTraceContext.ScopeClosed) {
onScopeClosed((OtelCurrentTraceContext.ScopeClosed) event);
}
else if (event instanceof OtelBaggageEntry.BaggageScopeEnded) {
removeAllBaggageEntries();
}
}
}

View File

@@ -16,11 +16,11 @@
package org.springframework.cloud.sleuth.otel.log;
import io.opentelemetry.baggage.BaggageManager;
import io.opentelemetry.exporters.logging.LoggingSpanExporter;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -28,6 +28,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.otel.bridge.OtelBaggageManager;
import org.springframework.cloud.sleuth.otel.bridge.TraceOtelBridgeAutoConfiguation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -42,6 +44,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(Tracer.class)
@AutoConfigureBefore(TraceAutoConfiguration.class)
@AutoConfigureAfter(TraceOtelBridgeAutoConfiguation.class)
@EnableConfigurationProperties(OtelLogProperties.class)
public class TraceOtelLogAutoConfiguration {
@@ -52,7 +55,7 @@ public class TraceOtelLogAutoConfiguration {
@Bean
Slf4jSpanProcessor otelSlf4jSpanProcessor(SleuthBaggageProperties sleuthBaggageProperties,
BaggageManager baggageManager) {
OtelBaggageManager baggageManager) {
return new Slf4jSpanProcessor(sleuthBaggageProperties, baggageManager);
}

View File

@@ -16,9 +16,8 @@
package org.springframework.cloud.sleuth.otel.opentracing;
import io.opentelemetry.baggage.BaggageManager;
import io.opentelemetry.opentracingshim.TraceShim;
import io.opentelemetry.trace.TracerProvider;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.opentracingshim.OpenTracingShim;
import io.opentracing.Tracer;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -42,16 +41,16 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.sleuth.opentracing.enabled", matchIfMissing = true)
@ConditionalOnBean(io.opentelemetry.trace.Tracer.class)
@ConditionalOnClass(TraceShim.class)
@ConditionalOnBean(io.opentelemetry.api.trace.Tracer.class)
@ConditionalOnClass(OpenTracingShim.class)
@AutoConfigureAfter(TraceOtelAutoConfiguration.class)
@EnableConfigurationProperties(SleuthOpentracingProperties.class)
class OpentracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
Tracer sleuthOpenTracing(TracerProvider tracerProvider, BaggageManager contextManager) {
return TraceShim.createTracerShim(tracerProvider, contextManager);
Tracer sleuthOpenTracing(OpenTelemetry openTelemetry) {
return OpenTracingShim.createTracerShim(openTelemetry);
}
}

View File

@@ -21,10 +21,9 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import io.grpc.Context;
import io.opentelemetry.baggage.Baggage;
import io.opentelemetry.baggage.BaggageUtils;
import io.opentelemetry.baggage.EntryMetadata;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.EntryMetadata;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -33,24 +32,19 @@ import org.springframework.cloud.sleuth.api.BaggageManager;
import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties;
import org.springframework.context.ApplicationEventPublisher;
// TODO: [OTEL] Experimental - doesn't really work
class BaggageTextMapPropagator implements TextMapPropagator {
private static final Log log = LogFactory.getLog(BaggageTextMapPropagator.class);
private final SleuthBaggageProperties properties;
private final io.opentelemetry.baggage.BaggageManager otelBaggageManager;
private final BaggageManager baggageManager;
private final ApplicationEventPublisher publisher;
BaggageTextMapPropagator(SleuthBaggageProperties properties,
io.opentelemetry.baggage.BaggageManager otelBaggageManager, BaggageManager baggageManager,
BaggageTextMapPropagator(SleuthBaggageProperties properties, BaggageManager baggageManager,
ApplicationEventPublisher publisher) {
this.properties = properties;
this.otelBaggageManager = otelBaggageManager;
this.baggageManager = baggageManager;
this.publisher = publisher;
}
@@ -62,11 +56,11 @@ class BaggageTextMapPropagator implements TextMapPropagator {
@Override
public <C> void inject(Context context, C c, Setter<C> setter) {
List<Map.Entry<String, String>> baggageEntries = applicableBaggageEntries();
List<Map.Entry<String, String>> baggageEntries = applicableBaggageEntries(c);
baggageEntries.forEach(e -> setter.set(c, e.getKey(), e.getValue()));
}
private List<Map.Entry<String, String>> applicableBaggageEntries() {
private <C> List<Map.Entry<String, String>> applicableBaggageEntries(C c) {
Map<String, String> allBaggage = this.baggageManager.getAllBaggage();
List<String> lowerCaseKeys = this.properties.getRemoteFields().stream().map(String::toLowerCase)
.collect(Collectors.toList());
@@ -79,11 +73,11 @@ class BaggageTextMapPropagator implements TextMapPropagator {
Map<String, String> baggageEntries = this.properties.getRemoteFields().stream()
.map(s -> new AbstractMap.SimpleEntry<>(s, getter.get(c, s))).filter(e -> e.getValue() != null)
.collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));
Baggage.Builder builder = otelBaggageManager.baggageBuilder().setParent(context);
baggageEntries.forEach((key, value) -> builder.put(key, value,
EntryMetadata.create(EntryMetadata.EntryTtl.UNLIMITED_PROPAGATION)));
Baggage.Builder builder = Baggage.builder().setParent(context);
// TODO: [OTEL] magic string
baggageEntries.forEach((key, value) -> builder.put(key, value, EntryMetadata.create("propagation=unlimited")));
Baggage baggage = builder.build();
Context withBaggage = BaggageUtils.withBaggage(baggage, context);
Context withBaggage = context.with(baggage);
if (log.isDebugEnabled()) {
log.debug("Will propagate new baggage context for entries " + baggageEntries);
}

View File

@@ -17,29 +17,32 @@
package org.springframework.cloud.sleuth.otel.propagation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import io.grpc.Context;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.propagation.HttpTraceContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.DefaultContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.extensions.trace.propagation.AwsXRayPropagator;
import io.opentelemetry.extensions.trace.propagation.B3Propagator;
import io.opentelemetry.extensions.trace.propagation.JaegerPropagator;
import io.opentelemetry.extensions.trace.propagation.OtTracerPropagator;
import io.opentelemetry.extensions.trace.propagation.TraceMultiPropagator;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.TracingContextUtils;
import io.opentelemetry.trace.propagation.HttpTraceContext;
import io.opentelemetry.extension.trace.propagation.AwsXRayPropagator;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.extension.trace.propagation.JaegerPropagator;
import io.opentelemetry.extension.trace.propagation.OtTracerPropagator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -53,6 +56,7 @@ import org.springframework.cloud.sleuth.otel.bridge.TraceOtelBridgeAutoConfiguat
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
@@ -74,20 +78,21 @@ public class TraceOtelPropagationAutoConfiguration {
ContextPropagators otelContextPropagators(ObjectProvider<List<TextMapPropagator>> propagators) {
List<TextMapPropagator> mapPropagators = propagators.getIfAvailable(ArrayList::new);
if (mapPropagators.isEmpty()) {
return OpenTelemetry.getPropagators();
return OpenTelemetry.getGlobalPropagators();
}
DefaultContextPropagators.Builder builder = DefaultContextPropagators.builder();
mapPropagators.forEach(builder::addTextMapPropagator);
OpenTelemetry.setPropagators(builder.build());
return OpenTelemetry.getPropagators();
OpenTelemetry.setGlobalPropagators(builder.build());
return OpenTelemetry.getGlobalPropagators();
}
@Configuration(proxyBeanMethods = false)
static class PropagatorsConfiguration {
@Bean
CompositeTextMapPropagator compositeTextMapPropagator(SleuthPropagationProperties properties) {
return new CompositeTextMapPropagator(properties);
CompositeTextMapPropagator compositeTextMapPropagator(BeanFactory beanFactory,
SleuthPropagationProperties properties) {
return new CompositeTextMapPropagator(beanFactory, properties);
}
}
@@ -97,10 +102,9 @@ public class TraceOtelPropagationAutoConfiguration {
static class BaggagePropagatorConfiguration {
@Bean
TextMapPropagator baggageTextMapPropagator(SleuthBaggageProperties properties,
io.opentelemetry.baggage.BaggageManager otelBaggageManager, BaggageManager baggageManager,
TextMapPropagator baggageTextMapPropagator(SleuthBaggageProperties properties, BaggageManager baggageManager,
ApplicationEventPublisher publisher) {
return new BaggageTextMapPropagator(properties, otelBaggageManager, baggageManager, publisher);
return new BaggageTextMapPropagator(properties, baggageManager, publisher);
}
}
@@ -115,23 +119,26 @@ class CompositeTextMapPropagator implements TextMapPropagator {
private final SleuthPropagationProperties properties;
CompositeTextMapPropagator(SleuthPropagationProperties properties) {
CompositeTextMapPropagator(BeanFactory beanFactory, SleuthPropagationProperties properties) {
this.properties = properties;
if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.AwsXRayPropagator")) {
this.mapping.put(SleuthPropagationProperties.PropagationType.AWS, AwsXRayPropagator.getInstance());
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.AwsXRayPropagator")) {
this.mapping.put(SleuthPropagationProperties.PropagationType.AWS, beanFactory
.getBeanProvider(AwsXRayPropagator.class).getIfAvailable(AwsXRayPropagator::getInstance));
}
if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.B3Propagator")) {
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.B3Propagator")) {
this.mapping.put(SleuthPropagationProperties.PropagationType.B3,
TraceMultiPropagator.builder().addPropagator(B3Propagator.getSingleHeaderPropagator())
.addPropagator(B3Propagator.getMultipleHeaderPropagator()).build());
beanFactory.getBeanProvider(B3Propagator.class).getIfAvailable(B3Propagator::getInstance));
}
if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.JaegerPropagator")) {
this.mapping.put(SleuthPropagationProperties.PropagationType.JAEGER, JaegerPropagator.getInstance());
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.JaegerPropagator")) {
this.mapping.put(SleuthPropagationProperties.PropagationType.JAEGER,
beanFactory.getBeanProvider(JaegerPropagator.class).getIfAvailable(JaegerPropagator::getInstance));
}
if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.OtTracerPropagator")) {
this.mapping.put(SleuthPropagationProperties.PropagationType.OT_TRACER, OtTracerPropagator.getInstance());
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.OtTracerPropagator")) {
this.mapping.put(SleuthPropagationProperties.PropagationType.OT_TRACER, beanFactory
.getBeanProvider(OtTracerPropagator.class).getIfAvailable(OtTracerPropagator::getInstance));
}
this.mapping.put(SleuthPropagationProperties.PropagationType.W3C, HttpTraceContext.getInstance());
this.mapping.put(SleuthPropagationProperties.PropagationType.W3C, new MultiTextMapPropagator(
Arrays.asList(HttpTraceContext.getInstance(), W3CBaggagePropagator.getInstance())));
this.mapping.put(SleuthPropagationProperties.PropagationType.CUSTOM, NoopTextMapPropagator.INSTANCE);
log.info("Registered the following context propagation types " + this.mapping.keySet());
}
@@ -161,7 +168,7 @@ class CompositeTextMapPropagator implements TextMapPropagator {
continue;
}
Context extractedContext = propagator.extract(context, carrier, getter);
Span span = TracingContextUtils.getSpanWithoutDefault(extractedContext);
Span span = Span.fromContextOrNull(extractedContext);
if (span != null) {
return extractedContext;
}
@@ -189,4 +196,48 @@ class CompositeTextMapPropagator implements TextMapPropagator {
}
// Taken from OTel
private static final class MultiTextMapPropagator implements TextMapPropagator {
private final TextMapPropagator[] textPropagators;
private final List<String> allFields;
private MultiTextMapPropagator(List<TextMapPropagator> textPropagators) {
this.textPropagators = new TextMapPropagator[textPropagators.size()];
textPropagators.toArray(this.textPropagators);
this.allFields = Collections.unmodifiableList(getAllFields(this.textPropagators));
}
private static List<String> getAllFields(TextMapPropagator[] textPropagators) {
Set<String> fields = new LinkedHashSet<>();
for (int i = 0; i < textPropagators.length; i++) {
fields.addAll(textPropagators[i].fields());
}
return new ArrayList<>(fields);
}
@Override
public List<String> fields() {
return allFields;
}
@Override
public <C> void inject(Context context, @Nullable C carrier, Setter<C> setter) {
for (int i = 0; i < textPropagators.length; i++) {
textPropagators[i].inject(context, carrier, setter);
}
}
@Override
public <C> Context extract(Context context, @Nullable C carrier, Getter<C> getter) {
for (int i = 0; i < textPropagators.length; i++) {
context = textPropagators[i].extract(context, carrier, getter);
}
return context;
}
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.cloud.sleuth.otel.autoconfig;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.api.trace.Tracer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

View File

@@ -16,12 +16,14 @@
package org.springframework.cloud.sleuth.otel.opentracing;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.api.trace.Tracer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration;
import org.springframework.cloud.sleuth.otel.propagation.TraceOtelPropagationAutoConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
@@ -36,8 +38,9 @@ class OpentracingAutoConfigurationTests {
}
private ApplicationContextRunner withAutoConfiguration() {
return new ApplicationContextRunner().withConfiguration(
AutoConfigurations.of(TraceOtelAutoConfiguration.class, OpentracingAutoConfiguration.class));
return new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, TraceOtelAutoConfiguration.class,
TraceOtelPropagationAutoConfiguration.class, OpentracingAutoConfiguration.class));
}
@Test

View File

@@ -21,14 +21,14 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.grpc.Context;
import javax.annotation.Nullable;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.TraceFlags;
import io.opentelemetry.trace.TraceState;
import io.opentelemetry.trace.TracingContextUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -95,10 +95,23 @@ class TraceOtelPropagationAutoConfigurationTests {
Map<String, String> carrier = carrierWithTracingData();
// Extraction
Context extract = customPropagator.extract(Context.current(), carrier, Map::get);
Span spanFromContext = TracingContextUtils.getSpan(extract);
assertThat(spanFromContext.getContext().getTraceIdAsHexString()).isEqualTo("ff000000000000000000000000000041");
assertThat(spanFromContext.getContext().getSpanIdAsHexString()).isEqualTo("ff00000000000041");
Context extract = customPropagator.extract(Context.current(), carrier,
new TextMapPropagator.Getter<Map<String, String>>() {
@Override
public Iterable<String> keys(Map<String, String> carrier) {
return carrier.keySet();
}
@Nullable
@Override
public String get(@Nullable Map<String, String> carrier, String key) {
return carrier.get(key);
}
});
Span spanFromContext = Span.fromContext(extract);
assertThat(spanFromContext.getSpanContext().getTraceIdAsHexString())
.isEqualTo("ff000000000000000000000000000041");
assertThat(spanFromContext.getSpanContext().getSpanIdAsHexString()).isEqualTo("ff00000000000041");
// Injection
Map<String, String> emptyMap = new HashMap<>();
@@ -143,7 +156,7 @@ class CustomPropagator implements TextMapPropagator {
@Override
public <C> void inject(Context context, C carrier, Setter<C> setter) {
SpanContext spanContext = TracingContextUtils.getSpan(context).getContext();
SpanContext spanContext = Span.fromContext(context).getSpanContext();
if (!spanContext.isValid()) {
return;
}
@@ -155,11 +168,11 @@ class CustomPropagator implements TextMapPropagator {
public <C> Context extract(Context context, C carrier, Getter<C> getter) {
String traceParent = getter.get(carrier, "myCustomTraceId");
if (traceParent == null) {
return TracingContextUtils.withSpan(DefaultSpan.create(SpanContext.getInvalid()), context);
return Span.getInvalid().storeInContext(context);
}
String spanId = getter.get(carrier, "myCustomSpanId");
return TracingContextUtils.withSpan(DefaultSpan.create(SpanContext.createFromRemoteParent(traceParent, spanId,
TraceFlags.getSampled(), TraceState.builder().build())), context);
return Span.wrap(SpanContext.createFromRemoteParent(traceParent, spanId, TraceFlags.getSampled(),
TraceState.builder().build())).storeInContext(context);
}
}

View File

@@ -51,7 +51,7 @@
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporters-zipkin</artifactId>
<artifactId>opentelemetry-exporter-zipkin</artifactId>
<optional>true</optional>
</dependency>
<dependency>

View File

@@ -74,8 +74,6 @@ import org.springframework.web.client.RestTemplate;
@Import(ZipkinSenderConfigurationImportSelector.class)
public class ZipkinAutoConfiguration {
private static final Log log = LogFactory.getLog(ZipkinAutoConfiguration.class);
/**
* Zipkin reporter bean name. Name of the bean matters for supporting multiple tracing
* systems.
@@ -88,29 +86,7 @@ public class ZipkinAutoConfiguration {
*/
public static final String SENDER_BEAN_NAME = "zipkinSender";
@Bean(REPORTER_BEAN_NAME)
@ConditionalOnMissingBean(name = REPORTER_BEAN_NAME)
public Reporter<Span> reporter(ReporterMetrics reporterMetrics, ZipkinProperties zipkin,
@Qualifier(SENDER_BEAN_NAME) Sender sender) {
CheckResult checkResult = checkResult(sender, 1_000L);
logCheckResult(sender, checkResult);
// historical constraint. Note: AsyncReporter supports memory bounds
AsyncReporter<Span> asyncReporter = AsyncReporter.builder(sender).queuedMaxSpans(1000)
.messageTimeout(zipkin.getMessageTimeout(), TimeUnit.SECONDS).metrics(reporterMetrics)
.build(zipkin.getEncoder());
return asyncReporter;
}
private void logCheckResult(Sender sender, CheckResult checkResult) {
if (log.isDebugEnabled() && checkResult != null && checkResult.ok()) {
log.debug("Check result of the [" + sender.toString() + "] is [" + checkResult + "]");
}
else if (checkResult != null && !checkResult.ok()) {
log.warn("Check result of the [" + sender.toString() + "] contains an error [" + checkResult + "]");
}
}
private static final Log log = LogFactory.getLog(ZipkinAutoConfiguration.class);
/** Limits {@link Sender#check()} to {@code deadlineMillis}. */
static CheckResult checkResult(Sender sender, long deadlineMillis) {
@@ -142,6 +118,30 @@ public class ZipkinAutoConfiguration {
}
}
@Bean(REPORTER_BEAN_NAME)
@ConditionalOnMissingBean(name = REPORTER_BEAN_NAME)
public Reporter<Span> reporter(ReporterMetrics reporterMetrics, ZipkinProperties zipkin,
@Qualifier(SENDER_BEAN_NAME) Sender sender) {
CheckResult checkResult = checkResult(sender, 1_000L);
logCheckResult(sender, checkResult);
// historical constraint. Note: AsyncReporter supports memory bounds
AsyncReporter<Span> asyncReporter = AsyncReporter.builder(sender).queuedMaxSpans(1000)
.messageTimeout(zipkin.getMessageTimeout(), TimeUnit.SECONDS).metrics(reporterMetrics)
.build(zipkin.getEncoder());
return asyncReporter;
}
private void logCheckResult(Sender sender, CheckResult checkResult) {
if (log.isDebugEnabled() && checkResult != null && checkResult.ok()) {
log.debug("Check result of the [" + sender.toString() + "] is [" + checkResult + "]");
}
else if (checkResult != null && !checkResult.ok()) {
log.warn("Check result of the [" + sender.toString() + "] contains an error [" + checkResult + "]");
}
}
@Bean
@ConditionalOnMissingBean
public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(ZipkinProperties zipkinProperties) {

View File

@@ -63,8 +63,6 @@ import org.springframework.web.client.RestTemplate;
@AutoConfigureAfter(ZipkinAutoConfiguration.class)
public class ZipkinBraveAutoConfiguration {
private static final Log log = LogFactory.getLog(ZipkinBraveAutoConfiguration.class);
/**
*
* Sort Zipkin Handlers last, so that redactions etc happen prior.
@@ -82,6 +80,8 @@ public class ZipkinBraveAutoConfiguration {
return 0;
};
private static final Log log = LogFactory.getLog(ZipkinBraveAutoConfiguration.class);
/** Returns one handler for as many reporters as exist. */
@Bean
SpanHandler zipkinSpanHandler(@Nullable List<Reporter<Span>> spanReporters, @Nullable Tag<Throwable> errorTag) {

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.zipkin2;
import io.opentelemetry.exporters.zipkin.ZipkinSpanExporter;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
import zipkin2.reporter.Sender;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -62,7 +62,7 @@ public class ZipkinOtelAutoConfiguration {
@ConditionalOnMissingBean
ZipkinSpanExporter otelZipkinSpanExporter(ZipkinProperties zipkinProperties,
@Qualifier(ZipkinAutoConfiguration.SENDER_BEAN_NAME) Sender sender, Environment env) {
return ZipkinSpanExporter.newBuilder().setEndpoint(zipkinProperties.getBaseUrl() + "api/v2/spans")
return ZipkinSpanExporter.builder().setEndpoint(zipkinProperties.getBaseUrl() + "api/v2/spans")
.setSender(sender).setEncoder(zipkinProperties.getEncoder())
.setServiceName(
StringUtils.hasText(zipkinProperties.getService().getName())

View File

@@ -174,17 +174,6 @@ class ZipkinRestTemplateSenderConfiguration {
}
/**
* Internal interface to provide a way to retrieve Zipkin URI. If there's no discovery
* client then this value will be taken from the properties. Otherwise host will be
* assumed to be a service id.
*/
interface ZipkinUrlExtractor {
URI zipkinUrl(ZipkinProperties zipkinProperties);
}
/**
* Resolves at runtime where the Zipkin server is. If there's no discovery client then
* {@link URI} from the properties is taken. Otherwise service discovery is pinged for
@@ -238,6 +227,17 @@ class ZipkinRestTemplateWrapper extends RestTemplate {
}
/**
* Internal interface to provide a way to retrieve Zipkin URI. If there's no discovery
* client then this value will be taken from the properties. Otherwise host will be
* assumed to be a service id.
*/
interface ZipkinUrlExtractor {
URI zipkinUrl(ZipkinProperties zipkinProperties);
}
class NoOpZipkinLoadBalancer implements ZipkinLoadBalancer {
private final ZipkinProperties zipkinProperties;

View File

@@ -16,8 +16,7 @@
package org.springframework.cloud.sleuth.zipkin2;
import io.opentelemetry.sdk.trace.Sampler;
import io.opentelemetry.sdk.trace.Samplers;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@@ -34,7 +33,7 @@ public class OtelZipkinDiscoveryClientTests extends ZipkinDiscoveryClientTests {
@Bean
Sampler alwaysSampler() {
return Samplers.alwaysOn();
return Sampler.alwaysOn();
}
}

View File

@@ -85,6 +85,10 @@ public class ZipkinAutoConfigurationTests {
public MockWebServer server = new MockWebServer();
MockEnvironment environment = new MockEnvironment();
AnnotationConfigApplicationContext context;
@BeforeEach
void setup() throws IOException {
server.start();
@@ -95,10 +99,6 @@ public class ZipkinAutoConfigurationTests {
server.close();
}
MockEnvironment environment = new MockEnvironment();
AnnotationConfigApplicationContext context;
@AfterEach
public void close() {
if (this.context != null) {
@@ -167,7 +167,7 @@ public class ZipkinAutoConfigurationTests {
Awaitility.await().atMost(250, TimeUnit.MILLISECONDS)
.untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(1));
Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS);
then(request.getPath()).isEqualTo("/api/v2/spans");
then(request.getBody().readUtf8()).contains("localEndpoint");
@@ -195,7 +195,7 @@ public class ZipkinAutoConfigurationTests {
Awaitility.await().atMost(250, TimeUnit.MILLISECONDS)
.untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(0));
Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS);
then(request.getPath()).isEqualTo("/api/v1/spans");
then(request.getBody().readUtf8()).contains("binaryAnnotations");
@@ -310,7 +310,7 @@ public class ZipkinAutoConfigurationTests {
Awaitility.await().atMost(250, TimeUnit.MILLISECONDS)
.untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(1));
Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS);
then(request.getPath()).isEqualTo("/api/v2/spans");
then(request.getBody().readUtf8()).contains("localEndpoint");

View File

@@ -48,15 +48,15 @@ public class RestTemplateSenderTest {
public MockWebServer server = new MockWebServer();
String endpoint = this.server.url("/api/v2/spans").toString();
RestTemplateSender sender = new RestTemplateSender(new RestTemplate(), this.endpoint, JSON_V2);
@AfterEach
void clean() throws IOException {
server.close();
}
String endpoint = this.server.url("/api/v2/spans").toString();
RestTemplateSender sender = new RestTemplateSender(new RestTemplate(), this.endpoint, JSON_V2);
/**
* Tests that json is not manipulated as a side-effect of using rest template.
* @throws Exception when span sending or receiving fails

View File

@@ -44,7 +44,7 @@ public class MultipleHopsIntegrationTests
@Override
protected void assertSpanNames() {
then(this.spans).extracting(FinishedSpan::name).containsAll(asList("GET /greeting", "send"));
then(this.spans).extracting(FinishedSpan::getName).containsAll(asList("GET /greeting", "send"));
}
@Override

View File

@@ -34,7 +34,7 @@ public class CircuitBreakerIntegrationTests
@Override
public void assertException(FinishedSpan finishedSpan) {
BDDAssertions.then(finishedSpan.tags().get("error")).contains("boom");
BDDAssertions.then(finishedSpan.getTags().get("error")).contains("boom");
}
@Configuration(proxyBeanMethods = false)

View File

@@ -37,7 +37,7 @@ public class CircuitBreakerTests
@Override
public void additionalAssertions(FinishedSpan finishedSpan) {
BDDAssertions.then(finishedSpan.tags().get("error")).contains("boom2");
BDDAssertions.then(finishedSpan.getTags().get("error")).contains("boom2");
}
}

View File

@@ -38,7 +38,7 @@ public class FeignRetriesTests extends org.springframework.cloud.sleuth.instrume
@Override
public void assertException() {
BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).error()).isInstanceOf(IOException.class);
BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).getError()).isInstanceOf(IOException.class);
}
}

View File

@@ -36,7 +36,7 @@ public class TracingFeignClientTests
@Override
public void assertException(RuntimeException error) {
BDDAssertions.then(this.tracerTest().handler().reportedSpans().get(0).error()).isSameAs(error);
BDDAssertions.then(this.tracerTest().handler().reportedSpans().get(0).getError()).isSameAs(error);
}
}

View File

@@ -66,7 +66,7 @@ public class TraceFilterTests extends org.springframework.cloud.sleuth.instrumen
BDDAssertions.then(tracerTest().tracing().tracer().currentSpan()).isNull();
BDDAssertions.then(tracerTest().handler()).hasSize(1);
BDDAssertions.then(tracerTest().handler().get(0).parentId()).isEqualTo("000000000000000a");
BDDAssertions.then(tracerTest().handler().get(0).getParentId()).isEqualTo("000000000000000a");
}
@Test
@@ -77,7 +77,7 @@ public class TraceFilterTests extends org.springframework.cloud.sleuth.instrumen
BDDAssertions.then(this.tracer.currentSpan()).isNull();
BDDAssertions.then(this.spans).hasSize(1);
BDDAssertions.then(this.spans.get(0).name()).isEqualTo("GET");
BDDAssertions.then(this.spans.get(0).getName()).isEqualTo("GET");
}
@Test
@@ -87,7 +87,7 @@ public class TraceFilterTests extends org.springframework.cloud.sleuth.instrumen
BDDAssertions.then(this.tracer.currentSpan()).isNull();
BDDAssertions.then(this.spans).hasSize(1);
BDDAssertions.then(this.spans.get(0).tags()).doesNotContainKey("http.status_code");
BDDAssertions.then(this.spans.get(0).getTags()).doesNotContainKey("http.status_code");
}
@Test
@@ -109,8 +109,8 @@ public class TraceFilterTests extends org.springframework.cloud.sleuth.instrumen
BDDAssertions.then(this.tracer.currentSpan()).isNull();
BDDAssertions.then(this.spans).hasSize(1);
BDDAssertions.then(this.spans.get(0).spanId()).isEqualTo("0000000000000003");
BDDAssertions.then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method",
BDDAssertions.then(this.spans.get(0).getSpanId()).isEqualTo("0000000000000003");
BDDAssertions.then(this.spans.get(0).getTags()).containsEntry("http.path", "/").containsEntry("http.method",
HttpMethod.GET.toString());
}

View File

@@ -27,6 +27,7 @@ import brave.propagation.CurrentTraceContext;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -68,9 +69,11 @@ public class Issue585Tests {
ResponseEntity<String> entity = this.testRestTemplate
.getForEntity("http://localhost:" + this.port + "/sleuthtest?greeting=foo", String.class);
then(this.currentTraceContext.get()).isNull();
then(entity.getStatusCode().value()).isEqualTo(500);
then(this.spans.get(0).tags()).containsEntry("custom", "tag").containsKeys("error");
Awaitility.await().untilAsserted(() -> {
then(this.currentTraceContext.get()).isNull();
then(entity.getStatusCode().value()).isEqualTo(500);
then(this.spans.get(0).tags()).containsEntry("custom", "tag").containsKeys("error");
});
}
}

View File

@@ -39,6 +39,7 @@ import rx.plugins.RxJavaPlugins;
import rx.plugins.RxJavaSchedulersHook;
import org.springframework.cloud.sleuth.api.Tracer;
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager;
import org.springframework.cloud.sleuth.brave.bridge.BraveTracer;
import static org.assertj.core.api.BDDAssertions.then;
@@ -59,7 +60,9 @@ public class SleuthRxJavaSchedulersHookTests {
Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans)
.build();
Tracer tracer = BraveTracer.fromBrave(this.tracing.tracer());
BraveBaggageManager braveBaggageManager = new BraveBaggageManager();
Tracer tracer = BraveTracer.fromBrave(this.tracing.tracer(), this.braveBaggageManager);
@AfterEach
public void clean() {

View File

@@ -46,7 +46,7 @@ public class ReactorNettyHttpClientSpringBootTests
@Override
public void assertSingleB3Header(String b3SingleHeaderReadByServer, FinishedSpan clientSpan, TraceContext parent) {
Assertions.assertThat(b3SingleHeaderReadByServer)
.isEqualTo(parent.traceId() + "-" + clientSpan.spanId() + "-1-" + parent.spanId());
.isEqualTo(parent.traceId() + "-" + clientSpan.getSpanId() + "-1-" + parent.spanId());
}
@Configuration(proxyBeanMethods = false)

Some files were not shown because too many files have changed in this diff Show More