Added OpenTracing support; fixes #599
This commit is contained in:
@@ -39,14 +39,14 @@ a baggage element then it will be sent downstream either via HTTP or messaging t
|
||||
|
||||
* Provides a way to create / continue spans and add tags and logs via annotations.
|
||||
|
||||
* Provides simple metrics of accepted / dropped spans.
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces.
|
||||
By default it sends them via HTTP to a Zipkin server on localhost (port 9411).
|
||||
Configure the location of the service using `spring.zipkin.baseUrl`.
|
||||
- If you depend on `spring-rabbit` or `spring-kafka` your app will send traces to a broker instead of http.
|
||||
- Note: `spring-cloud-sleuth-stream` is deprecated and should no longer be used.
|
||||
|
||||
* Spring Cloud Sleuth is http://opentracing.io/[OpenTracing] compatible
|
||||
|
||||
IMPORTANT: If using Zipkin, configure the percentage of spans exported using `spring.sleuth.sampler.percentage`
|
||||
(default 0.1, i.e. 10%). *Otherwise you might think that Sleuth is not working cause it's omitting some spans.*
|
||||
|
||||
|
||||
@@ -931,6 +931,12 @@ on how to create a Stream Zipkin server.
|
||||
|
||||
== Integrations
|
||||
|
||||
=== OpenTracing
|
||||
|
||||
Spring Cloud Sleuth is http://opentracing.io/[OpenTracing] compatible. If you have
|
||||
OpenTracing on the classpath we will automatically register the OpenTracing
|
||||
`Tracer` bean. If you wish to disable this just set `spring.sleuth.opentracing.enabled` to `false`
|
||||
|
||||
=== Runnable and Callable
|
||||
|
||||
If you're wrapping your logic in `Runnable` or `Callable` it's enough to wrap those classes in their Sleuth representative.
|
||||
|
||||
@@ -152,6 +152,11 @@
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentracing.brave</groupId>
|
||||
<artifactId>brave-opentracing</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- BRAVE -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.springframework.cloud.sleuth.instrument.opentracing;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.opentracing.BraveTracer;
|
||||
import io.opentracing.Tracer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
* to enable tracing via Opentracing.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value="spring.sleuth.opentracing.enabled", matchIfMissing=true)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@ConditionalOnClass(Tracer.class)
|
||||
@EnableConfigurationProperties(SleuthOpentracingProperties.class)
|
||||
public class OpentracingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
Tracer sleuthOpenTracing(brave.Tracing braveTracing) {
|
||||
return BraveTracer.create(braveTracing);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.instrument.opentracing;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Sleuth Opentracing settings
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.sleuth.opentracing")
|
||||
public class SleuthOpentracingProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import org.springframework.web.server.WebFilterChain;
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class TraceWebFilter implements WebFilter, Ordered {
|
||||
public final class TraceWebFilter implements WebFilter, Ordered {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceWebFilter.class);
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguratio
|
||||
org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration
|
||||
org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.opentracing.OpentracingAutoConfiguration
|
||||
|
||||
# Environment Post Processor
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* Copyright 2016-2018 The OpenZipkin 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
|
||||
*
|
||||
* http://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.instrument.opentracing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracer.SpanInScope;
|
||||
import brave.Tracing;
|
||||
import brave.opentracing.BraveSpan;
|
||||
import brave.opentracing.BraveSpanContext;
|
||||
import brave.opentracing.BraveTracer;
|
||||
import brave.propagation.B3Propagation;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.ExtraFieldPropagation;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.propagation.StrictCurrentTraceContext;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import io.opentracing.Scope;
|
||||
import io.opentracing.propagation.Format;
|
||||
import io.opentracing.propagation.TextMap;
|
||||
import io.opentracing.propagation.TextMapExtractAdapter;
|
||||
import io.opentracing.propagation.TextMapInjectAdapter;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import zipkin2.Annotation;
|
||||
import zipkin2.Endpoint;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.data.MapEntry.entry;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* This shows how one might make an OpenTracing adapter for Brave, and how to navigate in and out of
|
||||
* the core concepts.
|
||||
*
|
||||
* Adopted from: https://github.com/openzipkin-contrib/brave-opentracing/tree/master/src/test/java/brave/opentracing
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
|
||||
properties = "spring.sleuth.baggage-keys=country-code,user-id")
|
||||
public class BraveTracerTest {
|
||||
|
||||
@Autowired ArrayListSpanReporter spans;
|
||||
@Autowired Tracing brave;
|
||||
@Autowired BraveTracer opentracing;
|
||||
|
||||
@Test public void startWithOpenTracingAndFinishWithBrave() {
|
||||
io.opentracing.Span openTracingSpan = opentracing.buildSpan("encode")
|
||||
.withTag("lc", "codec")
|
||||
.withStartTimestamp(1L)
|
||||
.start();
|
||||
|
||||
Span braveSpan = ((BraveSpan) openTracingSpan).unwrap();
|
||||
|
||||
braveSpan.annotate(2L, "pump fake");
|
||||
braveSpan.finish(3L);
|
||||
|
||||
checkSpanReportedToZipkin();
|
||||
}
|
||||
|
||||
@Test public void extractTraceContext() throws Exception {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("X-B3-SpanId", "0000000000000002");
|
||||
map.put("X-B3-Sampled", "1");
|
||||
|
||||
BraveSpanContext openTracingContext =
|
||||
(BraveSpanContext) opentracing.extract(Format.Builtin.HTTP_HEADERS,
|
||||
new TextMapExtractAdapter(map));
|
||||
|
||||
assertThat(openTracingContext.unwrap())
|
||||
.isEqualTo(TraceContext.newBuilder()
|
||||
.traceId(1L)
|
||||
.spanId(2L)
|
||||
.sampled(true).build());
|
||||
}
|
||||
|
||||
@Test public void extractBaggage() throws Exception {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("X-B3-SpanId", "0000000000000002");
|
||||
map.put("X-B3-Sampled", "1");
|
||||
map.put("baggage-country-code", "FO");
|
||||
|
||||
BraveSpanContext openTracingContext = opentracing.extract(Format.Builtin.HTTP_HEADERS,
|
||||
new TextMapExtractAdapter(map));
|
||||
|
||||
assertThat(openTracingContext.baggageItems())
|
||||
.containsExactly(entry("country-code", "FO"));
|
||||
}
|
||||
|
||||
@Test public void extractTraceContextTextMap() throws Exception {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("X-B3-SpanId", "0000000000000002");
|
||||
map.put("X-B3-Sampled", "1");
|
||||
|
||||
BraveSpanContext openTracingContext =
|
||||
(BraveSpanContext) opentracing.extract(Format.Builtin.TEXT_MAP,
|
||||
new TextMapExtractAdapter(map));
|
||||
|
||||
assertThat(openTracingContext.unwrap())
|
||||
.isEqualTo(TraceContext.newBuilder()
|
||||
.traceId(1L)
|
||||
.spanId(2L)
|
||||
.sampled(true).build());
|
||||
}
|
||||
|
||||
@Test public void extractTraceContextCaseInsensitive() throws Exception {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("x-b3-spanid", "0000000000000002");
|
||||
map.put("x-b3-SaMpLeD", "1");
|
||||
map.put("other", "1");
|
||||
|
||||
BraveSpanContext openTracingContext =
|
||||
(BraveSpanContext) opentracing.extract(Format.Builtin.HTTP_HEADERS,
|
||||
new TextMapExtractAdapter(map));
|
||||
|
||||
assertThat(openTracingContext.unwrap())
|
||||
.isEqualTo(TraceContext.newBuilder()
|
||||
.traceId(1L)
|
||||
.spanId(2L)
|
||||
.sampled(true).build());
|
||||
}
|
||||
|
||||
@Test public void extractTraceContextReturnsNull() throws Exception {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("other", "1");
|
||||
|
||||
BraveSpanContext openTracingContext = opentracing.extract(Format.Builtin.HTTP_HEADERS,
|
||||
new TextMapExtractAdapter(map));
|
||||
|
||||
assertThat(openTracingContext).isNull();
|
||||
}
|
||||
|
||||
@Test public void injectTraceContext_baggage() throws Exception {
|
||||
BraveSpan span = opentracing.buildSpan("foo").start();
|
||||
span.setBaggageItem("country-code", "FO");
|
||||
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
TextMapInjectAdapter carrier = new TextMapInjectAdapter(map);
|
||||
opentracing.inject(span.context(), Format.Builtin.HTTP_HEADERS, carrier);
|
||||
|
||||
assertThat(map).containsEntry("baggage-country-code", "FO");
|
||||
}
|
||||
|
||||
void checkSpanReportedToZipkin() {
|
||||
assertThat(spans.getSpans()).first().satisfies(s -> {
|
||||
assertThat(s.name()).isEqualTo("encode");
|
||||
assertThat(s.timestamp()).isEqualTo(1L);
|
||||
assertThat(s.annotations())
|
||||
.containsExactly(Annotation.create(2L, "pump fake"));
|
||||
assertThat(s.tags())
|
||||
.containsExactly(entry("lc", "codec"));
|
||||
assertThat(s.duration()).isEqualTo(2L);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void subsequentChildrenNestProperly_OTStyle() {
|
||||
// this test is semantically identical to subsequentChildrenNestProperly_BraveStyle, but uses
|
||||
// the OpenTracingAPI instead of the Brave API.
|
||||
|
||||
Long idOfSpanA;
|
||||
Long shouldBeIdOfSpanA;
|
||||
Long idOfSpanB;
|
||||
Long shouldBeIdOfSpanB;
|
||||
Long parentIdOfSpanB;
|
||||
Long parentIdOfSpanC;
|
||||
|
||||
try (Scope scopeA = opentracing.buildSpan("spanA").startActive(false)) {
|
||||
idOfSpanA = getTraceContext(scopeA).spanId();
|
||||
try (Scope scopeB = opentracing.buildSpan("spanB").startActive(false)) {
|
||||
idOfSpanB = getTraceContext(scopeB).spanId();
|
||||
parentIdOfSpanB = getTraceContext(scopeB).parentId();
|
||||
shouldBeIdOfSpanB = getTraceContext(opentracing.scopeManager().active()).spanId();
|
||||
}
|
||||
shouldBeIdOfSpanA = getTraceContext(opentracing.scopeManager().active()).spanId();
|
||||
try (Scope scopeC = opentracing.buildSpan("spanC").startActive(false)) {
|
||||
parentIdOfSpanC = getTraceContext(scopeC).parentId();
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals("SpanA should have been active again after closing B", idOfSpanA,
|
||||
shouldBeIdOfSpanA);
|
||||
assertEquals("SpanB should have been active prior to its closure", idOfSpanB,
|
||||
shouldBeIdOfSpanB);
|
||||
assertEquals("SpanB's parent should be SpanA", idOfSpanA, parentIdOfSpanB);
|
||||
assertEquals("SpanC's parent should be SpanA", idOfSpanA, parentIdOfSpanC);
|
||||
}
|
||||
|
||||
@Test public void subsequentChildrenNestProperly_BraveStyle() {
|
||||
// this test is semantically identical to subsequentChildrenNestProperly_OTStyle, but uses
|
||||
// the Brave API instead of the OpenTracing API.
|
||||
|
||||
Long shouldBeIdOfSpanA;
|
||||
Long idOfSpanB;
|
||||
Long shouldBeIdOfSpanB;
|
||||
Long parentIdOfSpanB;
|
||||
Long parentIdOfSpanC;
|
||||
|
||||
Span spanA = brave.tracer().newTrace().name("spanA").start();
|
||||
Long idOfSpanA = spanA.context().spanId();
|
||||
try (SpanInScope scopeA = brave.tracer().withSpanInScope(spanA)) {
|
||||
|
||||
Span spanB = brave.tracer().newChild(spanA.context()).name("spanB").start();
|
||||
idOfSpanB = spanB.context().spanId();
|
||||
parentIdOfSpanB = spanB.context().parentId();
|
||||
try (SpanInScope scopeB = brave.tracer().withSpanInScope(spanB)) {
|
||||
shouldBeIdOfSpanB = brave.currentTraceContext().get().spanId();
|
||||
} finally {
|
||||
spanB.finish();
|
||||
}
|
||||
|
||||
shouldBeIdOfSpanA = brave.currentTraceContext().get().spanId();
|
||||
|
||||
Span spanC = brave.tracer().newChild(spanA.context()).name("spanC").start();
|
||||
parentIdOfSpanC = spanC.context().parentId();
|
||||
try (SpanInScope scopeC = brave.tracer().withSpanInScope(spanC)) {
|
||||
// nothing to do here
|
||||
} finally {
|
||||
spanC.finish();
|
||||
}
|
||||
} finally {
|
||||
spanA.finish();
|
||||
}
|
||||
|
||||
assertEquals("SpanA should have been active again after closing B", idOfSpanA,
|
||||
shouldBeIdOfSpanA);
|
||||
assertEquals("SpanB should have been active prior to its closure", idOfSpanB,
|
||||
shouldBeIdOfSpanB);
|
||||
assertEquals("SpanB's parent should be SpanA", idOfSpanA, parentIdOfSpanB);
|
||||
assertEquals("SpanC's parent should be SpanA", idOfSpanA, parentIdOfSpanC);
|
||||
}
|
||||
|
||||
@Test public void implicitParentFromSpanManager_startActive() {
|
||||
try (Scope scopeA = opentracing.buildSpan("spanA").startActive(true)) {
|
||||
try (Scope scopeB = opentracing.buildSpan("spanA").startActive(true)) {
|
||||
assertThat(getTraceContext(scopeB).parentId())
|
||||
.isEqualTo(getTraceContext(scopeA).spanId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void implicitParentFromSpanManager_start() {
|
||||
try (Scope scopeA = opentracing.buildSpan("spanA").startActive(true)) {
|
||||
BraveSpan span = opentracing.buildSpan("spanB").start();
|
||||
assertThat(span.unwrap().context().parentId())
|
||||
.isEqualTo(getTraceContext(scopeA).spanId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void implicitParentFromSpanManager_startActive_ignoreActiveSpan() {
|
||||
try (Scope scopeA = opentracing.buildSpan("spanA").startActive(true)) {
|
||||
try (Scope scopeB = opentracing.buildSpan("spanA")
|
||||
.ignoreActiveSpan().startActive(true)) {
|
||||
assertThat(getTraceContext(scopeB).parentId())
|
||||
.isNull(); // new trace
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void implicitParentFromSpanManager_start_ignoreActiveSpan() {
|
||||
try (Scope scopeA = opentracing.buildSpan("spanA").startActive(true)) {
|
||||
BraveSpan span = opentracing.buildSpan("spanB")
|
||||
.ignoreActiveSpan().start();
|
||||
assertThat(span.unwrap().context().parentId())
|
||||
.isNull(); // new trace
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void ignoresErrorFalseTag_beforeStart() {
|
||||
opentracing.buildSpan("encode")
|
||||
.withTag("error", false)
|
||||
.start().finish();
|
||||
|
||||
assertThat(spans.getSpans().get(0).tags())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test public void ignoresErrorFalseTag_afterStart() {
|
||||
opentracing.buildSpan("encode")
|
||||
.start()
|
||||
.setTag("error", false)
|
||||
.finish();
|
||||
|
||||
assertThat(spans.getSpans().get(0).tags())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
private static TraceContext getTraceContext(Scope scope) {
|
||||
return ((BraveSpanContext) scope.span().context()).unwrap();
|
||||
}
|
||||
|
||||
@Before public void clear() {
|
||||
this.spans.clear();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config {
|
||||
@Bean Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
@Bean ArrayListSpanReporter reporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean CurrentTraceContext currentTraceContext() {
|
||||
return new StrictCurrentTraceContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
<zipkin-reporter.version>1.1.2</zipkin-reporter.version>
|
||||
<zipkin-reporter2.version>2.3.0</zipkin-reporter2.version>
|
||||
<brave.version>4.13.3</brave.version>
|
||||
<brave.opentracing.version>0.25.0</brave.opentracing.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -87,7 +88,7 @@
|
||||
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
|
||||
<version>${brave.version}</version>
|
||||
</dependency>
|
||||
<!-- BRAVE -->
|
||||
<!-- ZIPKIN -->
|
||||
<dependency>
|
||||
<groupId>io.zipkin.java</groupId>
|
||||
<artifactId>zipkin</artifactId>
|
||||
@@ -153,6 +154,11 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentracing.brave</groupId>
|
||||
<artifactId>brave-opentracing</artifactId>
|
||||
<version>${brave.opentracing.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<profiles>
|
||||
|
||||
Reference in New Issue
Block a user