Jaeger配置
pom.xml
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.0.6</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.opentracing</groupId>
<artifactId>feign-opentracing</artifactId>
<version>0.4.0</version>
</dependency>
<!-- log -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.13</version>
</dependency>
application.yml
opentracing:
jaeger:
enabled: true
enable-b3-propagation: true
log-spans: true
http-sender:
url: http://<jaeger-collector-service-name>:14268/api/traces
rate-limiting-sampler:
max-traces-per-second: 5
配置traceId, spanid等的key
@Configuration
public class JaegerConfiguration {
public static final String TRACE_ID_NAME = "X-B3-TraceId";
public static final String SPAN_ID_NAME = "X-B3-SpanId";
public static final String SAMPLED_NAME = "X-B3-Sampled";
@Bean
public TracerBuilderCustomizer mdcBuilderCustomizer() {
return builder -> builder.withScopeManager(new MDCScopeManager.Builder().
withMDCSpanIdKey(SPAN_ID_NAME).
withMDCTraceIdKey(TRACE_ID_NAME).
withMDCSampledKey(SAMPLED_NAME).
build());
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnProperty(prefix = "opentracing.jaeger.neverReport", value = "enabled", matchIfMissing = true)
public Reporter reporter(JaegerConfigurationProperties properties,
@Autowired(required = false) ReporterAppender reporterAppender) {
List<Reporter> reporters = new LinkedList<>();
if (properties.isLogSpans()) {
reporters.add(new LoggingReporter());
}
if (reporterAppender != null) {
reporterAppender.append(reporters);
}
return new CompositeReporter(reporters.toArray(new Reporter[reporters.size()]));
}
}
打印日志时使用%X
将追踪信息打印出来,logback-spring.xml:
...
<pattern>
{
...
"trace": "%X{X-B3-TraceId:-}",
"span": "%X{X-B3-SpanId:-}",
"sample": "%X{X-B3-Sampled:-}",
...
}
</pattern>
...
全链路请求追踪,主要包括两部分:
一、线程内共享上下文,通过ThreadLocal实现
public class LogbackMDCAdapter implements MDCAdapter {
final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal();
public void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
} else {
Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
Integer lastOp = this.getAndSetLastOperation(1);
if (!this.wasLastOpReadOrNull(lastOp) && oldMap != null) {
oldMap.put(key, val);
} else {
Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
newMap.put(key, val);
}
}
}
public void remove(String key) {
if (key != null) {
Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
if (oldMap != null) {
Integer lastOp = this.getAndSetLastOperation(1);
if (this.wasLastOpReadOrNull(lastOp)) {
Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
newMap.remove(key);
} else {
oldMap.remove(key);
}
}
}
}
public void clear() {
this.lastOperation.set(1);
this.copyOnThreadLocal.remove();
}
public String get(String key) {
Map<String, String> map = (Map)this.copyOnThreadLocal.get();
return map != null && key != null ? (String)map.get(key) : null;
}
...
}
二、跨进程上下文传递,通过将上下文信息放入http header中实现
Feign中的实现(io.github.openfeign.opentracing:feign-opentracing)
TracingClient.java
@Override
public Response execute(Request request, Request.Options options) throws IOException {
Span span = tracer.buildSpan(request.method())
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.start();
for (FeignSpanDecorator spanDecorator: spanDecorators) {
try {
spanDecorator.onRequest(request, options, span);
} catch (Exception ex) {
log.log(Level.SEVERE, "Exception during decorating span", ex);
}
}
request = inject(span.context(), request);
try (Scope scope = tracer.activateSpan(span)) {
Response response = delegate.execute(request, options);
for (FeignSpanDecorator spanDecorator : spanDecorators) {
try {
spanDecorator.onResponse(response, options, span);
} catch (Exception ex) {
log.log(Level.SEVERE, "Exception during decorating span", ex);
}
}
return response;
} catch (Exception ex) {
for (FeignSpanDecorator spanDecorator: spanDecorators) {
try {
spanDecorator.onError(ex, request, span);
} catch (Exception exDecorator) {
log.log(Level.SEVERE, "Exception during decorating span", exDecorator);
}
}
throw ex;
} finally {
span.finish();
}
}
private Request inject(SpanContext spanContext, Request request) {
Map<String, Collection<String>> headersWithTracingContext = new HashMap<>(request.headers());
tracer.inject(spanContext, Format.Builtin.HTTP_HEADERS, new HttpHeadersInjectAdapter(headersWithTracingContext));
return request.create(request.method(), request.url(), headersWithTracingContext,request.body(),
request.charset());
}
RestTemplate的实现(io.opentracing.contrib:opentracing-spring-web)
TracingRestTemplateInterceptor.java
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse httpResponse;
Span span = tracer.buildSpan(httpRequest.getMethod().toString())
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
.start();
tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS,
new HttpHeadersCarrier(httpRequest.getHeaders()));
for (RestTemplateSpanDecorator spanDecorator : spanDecorators) {
try {
spanDecorator.onRequest(httpRequest, span);
} catch (RuntimeException exDecorator) {
log.error("Exception during decorating span", exDecorator);
}
}
try (Scope scope = tracer.activateSpan(span)) {
httpResponse = execution.execute(httpRequest, body);
for (RestTemplateSpanDecorator spanDecorator : spanDecorators) {
try {
spanDecorator.onResponse(httpRequest, httpResponse, span);
} catch (RuntimeException exDecorator) {
log.error("Exception during decorating span", exDecorator);
}
}
} catch (Exception ex) {
for (RestTemplateSpanDecorator spanDecorator : spanDecorators) {
try {
spanDecorator.onError(httpRequest, ex, span);
} catch (RuntimeException exDecorator) {
log.error("Exception during decorating span", exDecorator);
}
}
throw ex;
} finally {
span.finish();
}
return httpResponse;
}