Aligned HTTP instrumentation with Brave (#878)
* Aligned HTTP instrumentation with Brave with this change we remove TraceFilter and make TraceHandlerInterceptor a legacy component. All HTTP instrumentation is done via Brave. fixes gh-873
This commit is contained in:
committed by
GitHub
parent
a9779b15d8
commit
347c63ea82
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Filter running after {@link brave.servlet.TracingFilter}
|
||||
* that logs uncaught exceptions
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class ExceptionLoggingFilter implements Filter {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ExceptionLoggingFilter.class);
|
||||
|
||||
@Override public void init(FilterConfig filterConfig) throws ServletException {
|
||||
|
||||
}
|
||||
|
||||
@Override public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
try {
|
||||
chain.doFilter(request, response);
|
||||
} catch (Exception e) {
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("Uncaught exception thrown", e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import brave.Span;
|
||||
import brave.http.HttpTracing;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.web.servlet.HandlerInterceptor} that wraps handling of a
|
||||
* adds tags related to the class and method name.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.3
|
||||
*/
|
||||
class SleuthTraceHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private static final Log log = LogFactory.getLog(SleuthTraceHandlerInterceptor.class);
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private HttpTracing tracing;
|
||||
private TraceKeys traceKeys;
|
||||
private ErrorParser errorParser;
|
||||
|
||||
public SleuthTraceHandlerInterceptor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) {
|
||||
Span span = httpTracing().tracing()
|
||||
.tracer().currentSpan();
|
||||
if (span == null) {
|
||||
return true;
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding tags to span " + span);
|
||||
}
|
||||
addClassMethodTag(handler, span);
|
||||
addClassNameTag(handler, span);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addClassMethodTag(Object handler, Span span) {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
String methodName = ((HandlerMethod) handler).getMethod().getName();
|
||||
span.tag(traceKeys().getMvc().getControllerMethod(), methodName);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding a method tag with value [" + methodName + "] to a span " + span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addClassNameTag(Object handler, Span span) {
|
||||
String className;
|
||||
if (handler instanceof HandlerMethod) {
|
||||
className = ((HandlerMethod) handler).getBeanType().getSimpleName();
|
||||
} else {
|
||||
className = handler.getClass().getSimpleName();
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding a class tag with value [" + className + "] to a span " + span);
|
||||
}
|
||||
span.tag(traceKeys().getMvc().getControllerClass(), className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConcurrentHandlingStarted(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) {
|
||||
Span span = httpTracing().tracing().tracer().currentSpan();
|
||||
if (ex != null && span != null) {
|
||||
errorParser().parseErrorTags(span, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpTracing httpTracing() {
|
||||
if (this.tracing == null) {
|
||||
this.tracing = this.beanFactory.getBean(HttpTracing.class);
|
||||
}
|
||||
return this.tracing;
|
||||
}
|
||||
|
||||
private TraceKeys traceKeys() {
|
||||
if (this.traceKeys == null) {
|
||||
this.traceKeys = this.beanFactory.getBean(TraceKeys.class);
|
||||
}
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
private ErrorParser errorParser() {
|
||||
if (this.errorParser == null) {
|
||||
this.errorParser = this.beanFactory.getBean(ErrorParser.class);
|
||||
}
|
||||
return this.errorParser;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,366 +0,0 @@
|
||||
/*
|
||||
* 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.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.http.HttpServerHandler;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.servlet.HttpServletAdapter;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Filter that takes the value of the headers from either request and uses them to
|
||||
* create a new span.
|
||||
*
|
||||
* <p>
|
||||
* In order to keep the size of spans manageable, this only add tags defined in
|
||||
* {@link TraceKeys}.
|
||||
*
|
||||
* @author Jakub Nabrdalik, 4financeIT
|
||||
* @author Tomasz Nurkiewicz, 4financeIT
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @see Tracer
|
||||
* @see TraceKeys
|
||||
* @see TraceWebServletAutoConfiguration#traceFilter
|
||||
*/
|
||||
@Order(TraceFilter.ORDER)
|
||||
public class TraceFilter extends GenericFilterBean {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceFilter.class);
|
||||
|
||||
private static final String HTTP_COMPONENT = "http";
|
||||
|
||||
/**
|
||||
* If you register your filter before the {@link TraceFilter} then you will not
|
||||
* have the tracing context passed for you out of the box. That means that e.g. your
|
||||
* logs will not get correlated.
|
||||
*/
|
||||
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
|
||||
|
||||
protected static final String TRACE_REQUEST_ATTR = TraceFilter.class.getName()
|
||||
+ ".TRACE";
|
||||
|
||||
protected static final String TRACE_ERROR_HANDLED_REQUEST_ATTR = TraceFilter.class.getName()
|
||||
+ ".ERROR_HANDLED";
|
||||
|
||||
protected static final String TRACE_CLOSE_SPAN_REQUEST_ATTR = TraceFilter.class.getName()
|
||||
+ ".CLOSE_SPAN";
|
||||
|
||||
private static final String TRACE_SPAN_WITHOUT_PARENT = TraceFilter.class.getName()
|
||||
+ ".SPAN_WITH_NO_PARENT";
|
||||
|
||||
private static final String TRACE_EXCEPTION_REQUEST_ATTR = TraceFilter.class.getName()
|
||||
+ ".EXCEPTION";
|
||||
|
||||
private HttpTracing tracing;
|
||||
private TraceKeys traceKeys;
|
||||
private final BeanFactory beanFactory;
|
||||
private HttpServerHandler<HttpServletRequest, HttpServletResponse> handler;
|
||||
private Boolean hasErrorController;
|
||||
|
||||
private final UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public TraceFilter(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
|
||||
FilterChain filterChain) throws IOException, ServletException {
|
||||
if (!(servletRequest instanceof HttpServletRequest) ||
|
||||
!(servletResponse instanceof HttpServletResponse)) {
|
||||
throw new ServletException("Filter just supports HTTP requests");
|
||||
}
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(request);
|
||||
Span spanFromRequest = getSpanFromAttribute(request);
|
||||
Tracer.SpanInScope ws = null;
|
||||
if (spanFromRequest != null) {
|
||||
ws = continueSpan(request, spanFromRequest);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Received a request to uri [" + uri + "]");
|
||||
}
|
||||
// in case of a response with exception status a exception controller will close the span
|
||||
if (!httpStatusSuccessful(response) && isSpanContinued(request)) {
|
||||
processErrorRequest(filterChain, request, response, spanFromRequest, ws);
|
||||
return;
|
||||
}
|
||||
String name = HTTP_COMPONENT + ":" + uri;
|
||||
SpanAndScope spanAndScope = new SpanAndScope();
|
||||
Throwable exception = null;
|
||||
try {
|
||||
spanAndScope = createSpan(request, spanFromRequest, name, ws);
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (Throwable e) {
|
||||
exception = e;
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("Uncaught exception thrown", e);
|
||||
}
|
||||
request.setAttribute(TRACE_EXCEPTION_REQUEST_ATTR, e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (isAsyncStarted(request) || request.isAsyncStarted()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("The span " + spanFromRequest + " was created for async");
|
||||
}
|
||||
// TODO: how to deal with response annotations and async?
|
||||
} else {
|
||||
detachOrCloseSpans(request, response, spanAndScope, exception);
|
||||
}
|
||||
if (spanAndScope.scope != null) {
|
||||
spanAndScope.scope.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processErrorRequest(FilterChain filterChain, HttpServletRequest request,
|
||||
HttpServletResponse response, Span spanFromRequest, Tracer.SpanInScope ws)
|
||||
throws IOException, ServletException {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("The span " + spanFromRequest + " was already detached once and we're processing an error");
|
||||
}
|
||||
try {
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
request.setAttribute(TRACE_ERROR_HANDLED_REQUEST_ATTR, true);
|
||||
if (request.getAttribute(TraceRequestAttributes.ERROR_HANDLED_SPAN_REQUEST_ATTR) == null) {
|
||||
handler().handleSend(response,
|
||||
(Throwable) request.getAttribute(TRACE_EXCEPTION_REQUEST_ATTR), spanFromRequest);
|
||||
request.setAttribute(TRACE_EXCEPTION_REQUEST_ATTR, null);
|
||||
}
|
||||
if (ws != null) {
|
||||
ws.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Tracer.SpanInScope continueSpan(HttpServletRequest request, Span spanFromRequest) {
|
||||
request.setAttribute(TraceRequestAttributes.SPAN_CONTINUED_REQUEST_ATTR, "true");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("There has already been a span in the request " + spanFromRequest);
|
||||
}
|
||||
return httpTracing().tracing().tracer().withSpanInScope(spanFromRequest);
|
||||
}
|
||||
|
||||
private boolean requestHasAlreadyBeenHandled(HttpServletRequest request) {
|
||||
return request.getAttribute(TraceRequestAttributes.HANDLED_SPAN_REQUEST_ATTR) != null;
|
||||
}
|
||||
|
||||
private void detachOrCloseSpans(HttpServletRequest request,
|
||||
HttpServletResponse response, SpanAndScope spanFromRequest, Throwable exception) {
|
||||
Span span = spanFromRequest.span;
|
||||
if (span != null) {
|
||||
addResponseTagsForSpanWithoutParent(exception, request, response, span);
|
||||
// in case of a response with exception status will close the span when exception dispatch is handled
|
||||
// checking if tracing is in progress due to async / different order of view controller processing
|
||||
if (httpStatusSuccessful(response) && !requestHasAlreadyBeenHandled(request)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing the span " + span + " since the response was successful");
|
||||
}
|
||||
if (exception == null || !hasErrorController()) {
|
||||
clearTraceAttribute(request);
|
||||
handler().handleSend(response, exception, span);
|
||||
}
|
||||
} else if (errorAlreadyHandled(request) && !shouldCloseSpan(request)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
"Won't detach the span " + span + " since error has already been handled");
|
||||
}
|
||||
} else if ((shouldCloseSpan(request) || isRootSpan(span)) && stillTracingCurrentSpan(span)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will handle sent for span " + span);
|
||||
}
|
||||
handler().handleSend(response, exception, span);
|
||||
if (shouldCloseSpan(request)) {
|
||||
clearTraceAttribute(request);
|
||||
}
|
||||
} else if (span != null || requestHasAlreadyBeenHandled(request)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Detaching the span " + span + " since the response was unsuccessful");
|
||||
}
|
||||
if (!hasErrorController()) {
|
||||
clearTraceAttribute(request);
|
||||
}
|
||||
if (exception == null || !hasErrorController()) {
|
||||
handler().handleSend(response, exception, span);
|
||||
} else {
|
||||
abandonSpan(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// visible for tests
|
||||
void abandonSpan(Span span) {
|
||||
span.abandon();
|
||||
}
|
||||
|
||||
private void addResponseTagsForSpanWithoutParent(Throwable exception,
|
||||
HttpServletRequest request, HttpServletResponse response, Span span) {
|
||||
if (exception == null && spanWithoutParent(request) && response.getStatus() >= 100) {
|
||||
span.tag(traceKeys().getHttp().getStatusCode(),
|
||||
String.valueOf(response.getStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean spanWithoutParent(HttpServletRequest request) {
|
||||
return request.getAttribute(TRACE_SPAN_WITHOUT_PARENT) != null;
|
||||
}
|
||||
|
||||
private boolean isRootSpan(Span span) {
|
||||
return span.context().traceId() == span.context().spanId();
|
||||
}
|
||||
|
||||
private boolean stillTracingCurrentSpan(Span span) {
|
||||
Span currentSpan = httpTracing().tracing().tracer().currentSpan();
|
||||
return currentSpan != null && currentSpan.equals(span);
|
||||
}
|
||||
|
||||
private boolean httpStatusSuccessful(HttpServletResponse response) {
|
||||
if (response.getStatus() == 0) {
|
||||
return false;
|
||||
}
|
||||
HttpStatus.Series httpStatusSeries = HttpStatus.Series.valueOf(response.getStatus());
|
||||
return httpStatusSeries == HttpStatus.Series.SUCCESSFUL || httpStatusSeries == HttpStatus.Series.REDIRECTION;
|
||||
}
|
||||
|
||||
private Span getSpanFromAttribute(HttpServletRequest request) {
|
||||
return (Span) request.getAttribute(TRACE_REQUEST_ATTR);
|
||||
}
|
||||
|
||||
private void clearTraceAttribute(HttpServletRequest request) {
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, null);
|
||||
}
|
||||
|
||||
private boolean errorAlreadyHandled(HttpServletRequest request) {
|
||||
return Boolean.valueOf(
|
||||
String.valueOf(request.getAttribute(TRACE_ERROR_HANDLED_REQUEST_ATTR)));
|
||||
}
|
||||
|
||||
private boolean shouldCloseSpan(HttpServletRequest request) {
|
||||
return Boolean.valueOf(
|
||||
String.valueOf(request.getAttribute(TRACE_CLOSE_SPAN_REQUEST_ATTR)));
|
||||
}
|
||||
|
||||
private boolean isSpanContinued(HttpServletRequest request) {
|
||||
return getSpanFromAttribute(request) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a span and appends it as the current request's attribute
|
||||
*/
|
||||
private SpanAndScope createSpan(HttpServletRequest request,
|
||||
Span spanFromRequest, String name, Tracer.SpanInScope ws) {
|
||||
if (spanFromRequest != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span has already been created - continuing with the previous one");
|
||||
}
|
||||
return new SpanAndScope(spanFromRequest, ws);
|
||||
}
|
||||
spanFromRequest = handler().handleReceive(httpTracing().tracing()
|
||||
.propagation().extractor(HttpServletRequest::getHeader), request);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Found a parent span " + spanFromRequest.context() + " in the request");
|
||||
}
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parent span is " + spanFromRequest + "");
|
||||
}
|
||||
return new SpanAndScope(spanFromRequest, httpTracing().tracing()
|
||||
.tracer().withSpanInScope(spanFromRequest));
|
||||
}
|
||||
|
||||
class SpanAndScope {
|
||||
|
||||
final Span span;
|
||||
final Tracer.SpanInScope scope;
|
||||
|
||||
SpanAndScope(Span span, Tracer.SpanInScope scope) {
|
||||
this.span = span;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
SpanAndScope() {
|
||||
this.span = null;
|
||||
this.scope = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAsyncStarted(HttpServletRequest request) {
|
||||
return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpServerHandler<HttpServletRequest, HttpServletResponse> handler() {
|
||||
if (this.handler == null) {
|
||||
this.handler = HttpServerHandler.create(this.beanFactory.getBean(HttpTracing.class),
|
||||
new HttpServletAdapter());
|
||||
}
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
TraceKeys traceKeys() {
|
||||
if (this.traceKeys == null) {
|
||||
this.traceKeys = this.beanFactory.getBean(TraceKeys.class);
|
||||
}
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
HttpTracing httpTracing() {
|
||||
if (this.tracing == null) {
|
||||
this.tracing = this.beanFactory.getBean(HttpTracing.class);
|
||||
}
|
||||
return this.tracing;
|
||||
}
|
||||
|
||||
// null check is only for tests
|
||||
private boolean hasErrorController() {
|
||||
if (this.hasErrorController == null) {
|
||||
try {
|
||||
this.hasErrorController = this.beanFactory.getBean(ErrorController.class) != null;
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
this.hasErrorController = false;
|
||||
}
|
||||
}
|
||||
return this.hasErrorController;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
* 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.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.http.HttpServerHandler;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.servlet.HttpServletAdapter;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.web.servlet.HandlerInterceptor} that wraps handling of a
|
||||
* request in a Span. Adds tags related to the class and method name.
|
||||
*
|
||||
* The interceptor will not create spans for error controller related paths.
|
||||
*
|
||||
* It's important to note that this implementation will set the request attribute
|
||||
* {@link TraceRequestAttributes#HANDLED_SPAN_REQUEST_ATTR} when the request is processed.
|
||||
* That way the {@link TraceFilter} will not create the "fallback" span.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.3
|
||||
*/
|
||||
public class TraceHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceHandlerInterceptor.class);
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private HttpTracing tracing;
|
||||
private TraceKeys traceKeys;
|
||||
private ErrorParser errorParser;
|
||||
private AtomicReference<ErrorController> errorController;
|
||||
private HttpServerHandler<HttpServletRequest, HttpServletResponse> handler;
|
||||
|
||||
public TraceHandlerInterceptor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
String spanName = spanName(handler);
|
||||
boolean continueSpan = getRootSpanFromAttribute(request) != null;
|
||||
Span span = continueSpan ? getRootSpanFromAttribute(request) :
|
||||
httpTracing().tracing().tracer().nextSpan().name(spanName).start();
|
||||
try (Tracer.SpanInScope ws = httpTracing().tracing().tracer().withSpanInScope(span)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Handling span " + span);
|
||||
}
|
||||
addClassMethodTag(handler, span);
|
||||
addClassNameTag(handler, span);
|
||||
setSpanInAttribute(request, span);
|
||||
if (!continueSpan) {
|
||||
setNewSpanCreatedAttribute(request, span);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isErrorControllerRelated(HttpServletRequest request) {
|
||||
return errorController() != null && errorController().getErrorPath()
|
||||
.equals(request.getRequestURI());
|
||||
}
|
||||
|
||||
private void addClassMethodTag(Object handler, Span span) {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
String methodName = ((HandlerMethod) handler).getMethod().getName();
|
||||
span.tag(traceKeys().getMvc().getControllerMethod(), methodName);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding a method tag with value [" + methodName + "] to a span " + span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addClassNameTag(Object handler, Span span) {
|
||||
String className;
|
||||
if (handler instanceof HandlerMethod) {
|
||||
className = ((HandlerMethod) handler).getBeanType().getSimpleName();
|
||||
} else {
|
||||
className = handler.getClass().getSimpleName();
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding a class tag with value [" + className + "] to a span " + span);
|
||||
}
|
||||
span.tag(traceKeys().getMvc().getControllerClass(), className);
|
||||
}
|
||||
|
||||
private String spanName(Object handler) {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
return SpanNameUtil.toLowerHyphen(((HandlerMethod) handler).getMethod().getName());
|
||||
}
|
||||
return SpanNameUtil.toLowerHyphen(handler.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConcurrentHandlingStarted(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler) throws Exception {
|
||||
Span spanFromRequest = getNewSpanFromAttribute(request);
|
||||
if (spanFromRequest != null) {
|
||||
try (Tracer.SpanInScope ws = httpTracing().tracing().tracer().withSpanInScope(spanFromRequest)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing the span " + spanFromRequest);
|
||||
}
|
||||
} finally {
|
||||
spanFromRequest.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) throws Exception {
|
||||
if (isErrorControllerRelated(request)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Skipping closing of a span for error controller processing");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Span span = getRootSpanFromAttribute(request);
|
||||
if (ex != null) {
|
||||
errorParser().parseErrorTags(span, ex);
|
||||
}
|
||||
if (getNewSpanFromAttribute(request) != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing span " + span);
|
||||
}
|
||||
Span newSpan = getNewSpanFromAttribute(request);
|
||||
handler().handleSend(response, ex, newSpan);
|
||||
clearNewSpanCreatedAttribute(request);
|
||||
}
|
||||
}
|
||||
|
||||
private Span getNewSpanFromAttribute(HttpServletRequest request) {
|
||||
return (Span) request.getAttribute(TraceRequestAttributes.NEW_SPAN_REQUEST_ATTR);
|
||||
}
|
||||
|
||||
private Span getRootSpanFromAttribute(HttpServletRequest request) {
|
||||
return (Span) request.getAttribute(TraceFilter.TRACE_REQUEST_ATTR);
|
||||
}
|
||||
|
||||
private void setSpanInAttribute(HttpServletRequest request, Span span) {
|
||||
request.setAttribute(TraceRequestAttributes.HANDLED_SPAN_REQUEST_ATTR, span);
|
||||
}
|
||||
|
||||
private void setNewSpanCreatedAttribute(HttpServletRequest request, Span span) {
|
||||
request.setAttribute(TraceRequestAttributes.NEW_SPAN_REQUEST_ATTR, span);
|
||||
}
|
||||
|
||||
private void clearNewSpanCreatedAttribute(HttpServletRequest request) {
|
||||
request.removeAttribute(TraceRequestAttributes.NEW_SPAN_REQUEST_ATTR);
|
||||
}
|
||||
|
||||
private HttpTracing httpTracing() {
|
||||
if (this.tracing == null) {
|
||||
this.tracing = this.beanFactory.getBean(HttpTracing.class);
|
||||
}
|
||||
return this.tracing;
|
||||
}
|
||||
|
||||
private TraceKeys traceKeys() {
|
||||
if (this.traceKeys == null) {
|
||||
this.traceKeys = this.beanFactory.getBean(TraceKeys.class);
|
||||
}
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpServerHandler<HttpServletRequest, HttpServletResponse> handler() {
|
||||
if (this.handler == null) {
|
||||
this.handler = HttpServerHandler.create(this.beanFactory.getBean(HttpTracing.class),
|
||||
new HttpServletAdapter());
|
||||
}
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
private ErrorParser errorParser() {
|
||||
if (this.errorParser == null) {
|
||||
this.errorParser = this.beanFactory.getBean(ErrorParser.class);
|
||||
}
|
||||
return this.errorParser;
|
||||
}
|
||||
|
||||
ErrorController errorController() {
|
||||
if (this.errorController == null) {
|
||||
try {
|
||||
ErrorController errorController = this.beanFactory.getBean(ErrorController.class);
|
||||
this.errorController = new AtomicReference<>(errorController);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("ErrorController bean not found");
|
||||
}
|
||||
this.errorController = new AtomicReference<>();
|
||||
}
|
||||
}
|
||||
return this.errorController.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import brave.spring.webmvc.TracingHandlerInterceptor;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
@@ -38,10 +39,10 @@ class TraceSpringDataBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceSpringDataBeanPostProcessor.class);
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public TraceSpringDataBeanPostProcessor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
public TraceSpringDataBeanPostProcessor(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,7 +54,7 @@ class TraceSpringDataBeanPostProcessor implements BeanPostProcessor {
|
||||
"] in its trace representation");
|
||||
}
|
||||
return new TraceDelegatingHandlerMapping((DelegatingHandlerMapping) bean,
|
||||
this.beanFactory);
|
||||
this.applicationContext);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
@@ -67,13 +68,13 @@ class TraceSpringDataBeanPostProcessor implements BeanPostProcessor {
|
||||
private static class TraceDelegatingHandlerMapping extends DelegatingHandlerMapping {
|
||||
|
||||
private final DelegatingHandlerMapping delegate;
|
||||
private final BeanFactory beanFactory;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public TraceDelegatingHandlerMapping(DelegatingHandlerMapping delegate,
|
||||
BeanFactory beanFactory) {
|
||||
ApplicationContext beanFactory) {
|
||||
super(Collections.<HandlerMapping>emptyList());
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
this.applicationContext = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,7 +89,12 @@ class TraceSpringDataBeanPostProcessor implements BeanPostProcessor {
|
||||
if (handlerExecutionChain == null) {
|
||||
return null;
|
||||
}
|
||||
handlerExecutionChain.addInterceptor(new TraceHandlerInterceptor(this.beanFactory));
|
||||
handlerExecutionChain.addInterceptor(this.applicationContext.getBean(TracingHandlerInterceptor.class));
|
||||
String legacyEnabled = this.applicationContext.getEnvironment()
|
||||
.getProperty("spring.sleuth.http.legacy.enabled", "false");
|
||||
if (Boolean.parseBoolean(legacyEnabled)) {
|
||||
handlerExecutionChain.addInterceptor(this.applicationContext.getBean(SleuthTraceHandlerInterceptor.class));
|
||||
}
|
||||
return handlerExecutionChain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,19 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import brave.http.HttpTracing;
|
||||
import brave.spring.webmvc.TracingHandlerInterceptor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* MVC Adapter that adds the {@link TraceHandlerInterceptor}
|
||||
* MVC Adapter that adds the {@link TracingHandlerInterceptor}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
@@ -32,15 +36,26 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
*/
|
||||
@Configuration
|
||||
class TraceWebMvcConfigurer implements WebMvcConfigurer {
|
||||
@Autowired BeanFactory beanFactory;
|
||||
@Autowired ApplicationContext applicationContext;
|
||||
|
||||
@Bean
|
||||
public TraceHandlerInterceptor traceHandlerInterceptor(BeanFactory beanFactory) {
|
||||
return new TraceHandlerInterceptor(beanFactory);
|
||||
public TracingHandlerInterceptor tracingHandlerInterceptor(HttpTracing tracing) {
|
||||
return (TracingHandlerInterceptor) TracingHandlerInterceptor.create(tracing);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("spring.sleuth.http.legacy.enabled")
|
||||
public SleuthTraceHandlerInterceptor legacySleuthTraceHandlerInterceptor(BeanFactory beanFactory) {
|
||||
return new SleuthTraceHandlerInterceptor(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(this.beanFactory.getBean(TraceHandlerInterceptor.class));
|
||||
registry.addInterceptor(this.applicationContext.getBean(TracingHandlerInterceptor.class));
|
||||
String legacyEnabled = this.applicationContext.getEnvironment()
|
||||
.getProperty("spring.sleuth.http.legacy.enabled", "false");
|
||||
if (Boolean.parseBoolean(legacyEnabled)) {
|
||||
registry.addInterceptor(this.applicationContext.getBean(SleuthTraceHandlerInterceptor.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.http.HttpTracing;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import brave.servlet.TracingFilter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -28,9 +28,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import static javax.servlet.DispatcherType.ASYNC;
|
||||
@@ -54,6 +56,8 @@ import static javax.servlet.DispatcherType.REQUEST;
|
||||
@AutoConfigureAfter(TraceHttpAutoConfiguration.class)
|
||||
public class TraceWebServletAutoConfiguration {
|
||||
|
||||
public static final int TRACING_FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
|
||||
|
||||
/**
|
||||
* Nested config that configures Web MVC if it's present (without adding a runtime
|
||||
* dependency to it)
|
||||
@@ -72,22 +76,30 @@ public class TraceWebServletAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping")
|
||||
public static TraceSpringDataBeanPostProcessor traceSpringDataBeanPostProcessor(
|
||||
BeanFactory beanFactory) {
|
||||
return new TraceSpringDataBeanPostProcessor(beanFactory);
|
||||
ApplicationContext applicationContext) {
|
||||
return new TraceSpringDataBeanPostProcessor(applicationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean traceWebFilter(
|
||||
TraceFilter traceFilter) {
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(traceFilter);
|
||||
TracingFilter tracingFilter) {
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(tracingFilter);
|
||||
filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
|
||||
filterRegistrationBean.setOrder(TraceFilter.ORDER);
|
||||
filterRegistrationBean.setOrder(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER);
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean exceptionThrowingFilter() {
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new ExceptionLoggingFilter());
|
||||
filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
|
||||
filterRegistrationBean.setOrder(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1);
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TraceFilter traceFilter(BeanFactory beanFactory) {
|
||||
return new TraceFilter(beanFactory);
|
||||
public TracingFilter tracingFilter(HttpTracing tracing) {
|
||||
return (TracingFilter) TracingFilter.create(tracing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,10 @@ import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceFilter;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceRequestAttributes;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* A post request {@link ZuulFilter} that marks a span for closing in {@link TraceFilter}
|
||||
* A post request {@link ZuulFilter}
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
@@ -67,9 +65,6 @@ class TracePostZuulFilter extends ZuulFilter {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Marking current span as handled");
|
||||
}
|
||||
RequestContext.getCurrentContext()
|
||||
.getRequest().setAttribute(
|
||||
TraceRequestAttributes.HANDLED_SPAN_REQUEST_ATTR, "true");
|
||||
HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
|
||||
Throwable exception = RequestContext.getCurrentContext().getThrowable();
|
||||
this.handler.handleSend(response, exception, this.tracer.currentSpan());
|
||||
|
||||
@@ -18,13 +18,13 @@ package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import brave.spring.webmvc.TracingHandlerInterceptor;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceHandlerInterceptor;
|
||||
|
||||
/**
|
||||
* Bean post processor that wraps {@link ZuulHandlerMapping} in its
|
||||
@@ -58,7 +58,7 @@ class TraceZuulHandlerMappingBeanPostProcessor implements BeanPostProcessor {
|
||||
}
|
||||
ZuulHandlerMapping zuulHandlerMapping = (ZuulHandlerMapping) bean;
|
||||
zuulHandlerMapping.setInterceptors(
|
||||
new TraceHandlerInterceptor(this.beanFactory));
|
||||
this.beanFactory.getBean(TracingHandlerInterceptor.class));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import brave.servlet.TracingFilter;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
@@ -40,7 +41,6 @@ import org.slf4j.MDC;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
|
||||
@@ -69,7 +69,8 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = TraceFilterIntegrationTests.Config.class)
|
||||
@SpringBootTest(classes = TraceFilterIntegrationTests.Config.class,
|
||||
properties = "spring.sleuth.http.legacy.enabled=true")
|
||||
public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
@@ -78,7 +79,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
private static Log logger = LogFactory.getLog(
|
||||
TraceFilterIntegrationTests.class);
|
||||
|
||||
@Autowired TraceFilter traceFilter;
|
||||
@Autowired TracingFilter traceFilter;
|
||||
@Autowired MyFilter myFilter;
|
||||
@Autowired ArrayListSpanReporter reporter;
|
||||
@Autowired Tracer tracer;
|
||||
@@ -95,7 +96,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
public void should_create_a_trace() throws Exception {
|
||||
whenSentPingWithoutTracingData();
|
||||
|
||||
then(this.reporter.getSpans()).hasSize(1);
|
||||
then(this.reporter.getSpans()).hasSize(2);
|
||||
zipkin2.Span span = this.reporter.getSpans().get(0);
|
||||
then(span.tags())
|
||||
.containsKey(new TraceKeys().getMvc().getControllerClass())
|
||||
@@ -173,7 +174,15 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
|
||||
whenSentToNonExistentEndpointWithTraceId(expectedTraceId);
|
||||
|
||||
then(this.reporter.getSpans()).hasSize(1);
|
||||
// it's a span with the same ids
|
||||
then(this.reporter.getSpans()).hasSize(2);
|
||||
zipkin2.Span serverSpan = this.reporter.getSpans().get(0);
|
||||
then(serverSpan.tags())
|
||||
.containsEntry("custom", "tag")
|
||||
.containsEntry("http.status_code", "404");
|
||||
zipkin2.Span handlerSpan = this.reporter.getSpans().get(0);
|
||||
then(handlerSpan.tags())
|
||||
.containsEntry("http.status_code", "404");
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@@ -190,8 +199,13 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
|
||||
// we need to dump the span cause it's not in TraceFilter since TF
|
||||
// has also error dispatch and the ErrorController would report the span
|
||||
then(this.reporter.getSpans()).hasSize(1);
|
||||
then(this.reporter.getSpans().get(0).tags()).containsKey("error");
|
||||
then(this.reporter.getSpans()).hasSize(2);
|
||||
// server
|
||||
then(this.reporter.getSpans().get(0).tags())
|
||||
.containsEntry("error", "java.lang.RuntimeException");
|
||||
// handler
|
||||
then(this.reporter.getSpans().get(1).tags())
|
||||
.containsEntry("error", "Request processing failed; nested exception is java.lang.RuntimeException");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -357,17 +371,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
}
|
||||
|
||||
@Bean
|
||||
TraceFilter myTraceFilter(BeanFactory beanFactory) {
|
||||
return new TraceFilter(beanFactory) {
|
||||
@Override void abandonSpan(Span span) {
|
||||
log.info("Simulating Error Controller");
|
||||
span.finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(TraceFilter.ORDER + 1)
|
||||
@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)
|
||||
Filter myFilter(Tracer tracer) {
|
||||
return new MyFilter(tracer);
|
||||
}
|
||||
@@ -376,7 +380,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
|
||||
//tag::response_headers[]
|
||||
@Component
|
||||
@Order(TraceFilter.ORDER + 1)
|
||||
@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)
|
||||
class MyFilter extends GenericFilterBean {
|
||||
|
||||
private final Tracer tracer;
|
||||
@@ -388,7 +392,9 @@ class MyFilter extends GenericFilterBean {
|
||||
@Override public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
Span currentSpan = this.tracer.currentSpan();
|
||||
then(currentSpan).isNotNull();
|
||||
if (currentSpan == null) {
|
||||
return;
|
||||
}
|
||||
// for readability we're returning trace id in a hex form
|
||||
((HttpServletResponse) response)
|
||||
.addHeader("ZIPKIN-TRACE-ID",
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
@@ -25,17 +26,12 @@ import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import brave.servlet.TracingFilter;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.autoconfig.SleuthProperties;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.sleuth.util.SpanUtil;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -76,12 +72,11 @@ public class TraceFilterTests {
|
||||
new ExceptionMessageErrorParser()))
|
||||
.serverSampler(new SleuthHttpSampler(() -> Pattern.compile("")))
|
||||
.build();
|
||||
SleuthProperties properties = new SleuthProperties();
|
||||
Filter filter = TracingFilter.create(this.httpTracing);
|
||||
|
||||
MockHttpServletRequest request;
|
||||
MockHttpServletResponse response;
|
||||
MockFilterChain filterChain;
|
||||
BeanFactory beanFactory = Mockito.mock(BeanFactory.class);
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
@@ -103,19 +98,16 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void notTraced() throws Exception {
|
||||
BeanFactory beanFactory = neverSampleTracing();
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
|
||||
this.request = get("/favicon.ico").accept(MediaType.ALL)
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
}
|
||||
|
||||
private BeanFactory neverSampleTracing() {
|
||||
private Filter neverSampleFilter() {
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
@@ -128,14 +120,11 @@ public class TraceFilterTests {
|
||||
new ExceptionMessageErrorParser()))
|
||||
.serverSampler(new SleuthHttpSampler(() -> Pattern.compile("")))
|
||||
.build();
|
||||
BeanFactory beanFactory = beanFactory();
|
||||
BDDMockito.given(beanFactory.getBean(HttpTracing.class)).willReturn(httpTracing);
|
||||
return beanFactory;
|
||||
return TracingFilter.create(httpTracing);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startsNewTrace() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(this.reporter.getSpans())
|
||||
@@ -149,32 +138,8 @@ public class TraceFilterTests {
|
||||
//.containsEntry("http.status_code", "200")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startsNewTraceWithTraceHandlerInterceptor() throws Exception {
|
||||
final BeanFactory beanFactory = beanFactory();
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
filter.doFilter(this.request, this.response, (req, resp) -> {
|
||||
this.filterChain.doFilter(req, resp);
|
||||
// Simulate execution of the TraceHandlerInterceptor
|
||||
request.setAttribute(TraceRequestAttributes.HANDLED_SPAN_REQUEST_ATTR,
|
||||
tracing.tracer().currentSpan());
|
||||
});
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans())
|
||||
.hasSize(1);
|
||||
then(this.reporter.getSpans().get(0).tags())
|
||||
.containsEntry("http.url", "http://localhost/?foo=bar")
|
||||
.containsEntry("http.host", "localhost")
|
||||
.containsEntry("http.path", "/")
|
||||
.containsEntry("http.method", HttpMethod.GET.toString());
|
||||
// we don't check for status_code anymore cause Brave doesn't support it oob
|
||||
//.containsEntry("http.status_code", "200")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotStoreHttpStatusCodeWhenResponseCodeHasNotYetBeenSet() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
this.response.setStatus(0);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -192,9 +157,7 @@ public class TraceFilterTests {
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(2L))
|
||||
.header(PARENT_SPAN_ID_NAME, SpanUtil.idToHex(3L))
|
||||
.buildRequest(new MockServletContext());
|
||||
BeanFactory beanFactory = beanFactory();
|
||||
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
@@ -217,9 +180,7 @@ public class TraceFilterTests {
|
||||
.header(PARENT_SPAN_ID_NAME, SpanUtil.idToHex(3L))
|
||||
.header(SAMPLED_ID_NAME, 0)
|
||||
.buildRequest(new MockServletContext());
|
||||
BeanFactory beanFactory = beanFactory();
|
||||
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
|
||||
filter.doFilter(this.request, this.response, (req, resp) -> {
|
||||
this.filterChain.doFilter(req, resp);
|
||||
span.set(this.tracing.tracer().currentSpan());
|
||||
@@ -233,26 +194,20 @@ public class TraceFilterTests {
|
||||
@Test
|
||||
public void continuesSpanInRequestAttr() throws Exception {
|
||||
Span span = this.tracer.nextSpan().name("http:foo");
|
||||
this.request.setAttribute(TraceFilter.TRACE_REQUEST_ATTR, span);
|
||||
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.request.getAttribute(TraceFilter.TRACE_ERROR_HANDLED_REQUEST_ATTR)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closesSpanInRequestAttrIfStatusCodeNotSuccessful() throws Exception {
|
||||
Span span = this.tracer.nextSpan().name("http:foo");
|
||||
this.request.setAttribute(TraceFilter.TRACE_REQUEST_ATTR, span);
|
||||
this.response.setStatus(404);
|
||||
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.request.getAttribute(TraceFilter.TRACE_ERROR_HANDLED_REQUEST_ATTR)).isNotNull();
|
||||
then(this.reporter.getSpans())
|
||||
.hasSize(1);
|
||||
}
|
||||
@@ -260,12 +215,8 @@ public class TraceFilterTests {
|
||||
@Test
|
||||
public void doesntDetachASpanIfStatusCodeNotSuccessfulAndRequestWasProcessed() throws Exception {
|
||||
Span span = this.tracer.nextSpan().name("http:foo");
|
||||
this.request.setAttribute(TraceFilter.TRACE_REQUEST_ATTR, span);
|
||||
this.request.setAttribute(TraceFilter.TRACE_ERROR_HANDLED_REQUEST_ATTR, true);
|
||||
this.response.setStatus(404);
|
||||
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
}
|
||||
@@ -275,8 +226,6 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
BeanFactory beanFactory = beanFactory();
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -295,11 +244,8 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
BeanFactory beanFactory = beanFactory();
|
||||
BDDMockito.given(beanFactory.getBean(HttpTracing.class)).willReturn(httpTracing);
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
TracingFilter.create(httpTracing).doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans())
|
||||
@@ -314,9 +260,7 @@ public class TraceFilterTests {
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
this.traceKeys.getHttp().getHeaders().add("x-foo");
|
||||
BeanFactory beanFactory = beanFactory();
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
this.request.addHeader("X-Foo", "bar");
|
||||
this.request.addHeader("X-Foo", "bar");
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -332,8 +276,7 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
this.traceKeys.getHttp().getHeaders().add("x-foo");BeanFactory beanFactory = beanFactory();
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
this.traceKeys.getHttp().getHeaders().add("x-foo");
|
||||
this.request.addHeader("X-Foo", "bar");
|
||||
this.request.addHeader("X-Foo", "spam");
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -351,8 +294,6 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
BeanFactory beanFactory = beanFactory();
|
||||
TraceFilter filter = new TraceFilter(beanFactory);
|
||||
|
||||
this.filterChain = new MockFilterChain() {
|
||||
@Override
|
||||
@@ -382,7 +323,6 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
|
||||
this.response.setStatus(404);
|
||||
|
||||
@@ -395,7 +335,6 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
this.response.setStatus(200);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -410,7 +349,6 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
this.response.setStatus(302);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -425,7 +363,6 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(SPAN_ID_NAME, "asd")
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -440,7 +377,6 @@ public class TraceFilterTests {
|
||||
.header(PARENT_SPAN_ID_NAME, "-")
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -454,9 +390,8 @@ public class TraceFilterTests {
|
||||
this.request = builder()
|
||||
.header(SPAN_FLAGS, 1)
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(neverSampleTracing());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).isNotEmpty();
|
||||
@@ -467,7 +402,6 @@ public class TraceFilterTests {
|
||||
this.request = builder()
|
||||
.header(SPAN_FLAGS, 0)
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -483,8 +417,7 @@ public class TraceFilterTests {
|
||||
.header(SPAN_ID_NAME, SpanUtil.idToHex(10L))
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
TraceFilter filter = new TraceFilter(neverSampleTracing());
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
// It is ok to go without a trace ID, if sampling or debug is set
|
||||
@@ -500,9 +433,8 @@ public class TraceFilterTests {
|
||||
.header(SPAN_FLAGS, 1)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(10L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(neverSampleTracing());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
@@ -515,7 +447,6 @@ public class TraceFilterTests {
|
||||
.param("foo", "bar")
|
||||
.buildRequest(new MockServletContext());
|
||||
this.response.setStatus(295);
|
||||
TraceFilter filter = new TraceFilter(beanFactory());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -536,9 +467,8 @@ public class TraceFilterTests {
|
||||
this.request = builder()
|
||||
.header(SPAN_FLAGS, 1)
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(neverSampleTracing());
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans())
|
||||
@@ -581,18 +511,4 @@ public class TraceFilterTests {
|
||||
.containsEntry("http.status_code", status.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private BeanFactory beanFactory() {
|
||||
BDDMockito.given(beanFactory.getBean(SkipPatternProvider.class))
|
||||
.willThrow(new NoSuchBeanDefinitionException("foo"));
|
||||
BDDMockito.given(beanFactory.getBean(SleuthProperties.class))
|
||||
.willReturn(this.properties);
|
||||
BDDMockito.given(beanFactory.getBean(HttpTracing.class))
|
||||
.willReturn(this.httpTracing);
|
||||
BDDMockito.given(beanFactory.getBean(TraceKeys.class))
|
||||
.willReturn(this.traceKeys);
|
||||
BDDMockito.given(beanFactory.getBean(ErrorParser.class))
|
||||
.willReturn(new ExceptionMessageErrorParser());
|
||||
return beanFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,13 +85,12 @@ public class TraceFilterWebIntegrationTests {
|
||||
then(fromFirstTraceFilterFlow.tags())
|
||||
.containsEntry("http.status_code", "500")
|
||||
.containsEntry("http.method", "GET")
|
||||
.containsEntry("error", "Request processing failed; nested exception is java.lang.RuntimeException: Throwing exception")
|
||||
.containsEntry("error", "Throwing exception")
|
||||
.containsEntry("mvc.controller.class", "ExceptionThrowingController");
|
||||
Span fromErrorController = this.accumulator.getSpans().get(1);
|
||||
then(fromErrorController.tags())
|
||||
.containsEntry("http.status_code", "500")
|
||||
.containsEntry("error", "Request processing failed; nested exception is java.lang.RuntimeException: Throwing exception")
|
||||
.containsEntry("mvc.controller.class", "BasicErrorController");
|
||||
.containsEntry("error", "Request processing failed; nested exception is java.lang.RuntimeException: Throwing exception");
|
||||
// issue#714
|
||||
String hex = fromErrorController.traceId();
|
||||
String[] split = capture.toString().split("\n");
|
||||
@@ -110,10 +109,10 @@ public class TraceFilterWebIntegrationTests {
|
||||
} catch (HttpClientErrorException e) {
|
||||
}
|
||||
|
||||
//TODO: Check if it should be 1 or 2 spans
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
then(this.accumulator.getSpans()).hasSize(2).as("spans with same id, one from server, one from handler");
|
||||
then(this.accumulator.getSpans().get(0).kind().ordinal()).isEqualTo(Span.Kind.SERVER.ordinal());
|
||||
then(this.accumulator.getSpans().get(0).tags()).containsEntry("http.status_code", "400");
|
||||
}
|
||||
|
||||
private int port() {
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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.web;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.only;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceHandlerInterceptorTests {
|
||||
|
||||
@Mock BeanFactory beanFactory;
|
||||
@InjectMocks TraceHandlerInterceptor traceHandlerInterceptor;
|
||||
|
||||
@Test
|
||||
public void should_cache_the_retrieved_bean_when_exception_took_place() throws Exception {
|
||||
given(this.beanFactory.getBean(ErrorController.class)).willThrow(new NoSuchBeanDefinitionException("errorController"));
|
||||
|
||||
then(this.traceHandlerInterceptor.errorController()).isNull();
|
||||
then(this.traceHandlerInterceptor.errorController()).isNull();
|
||||
BDDMockito.then(this.beanFactory).should(only()).getBean(ErrorController.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_cache_the_retrieved_bean_when_no_exception_took_place() throws Exception {
|
||||
given(this.beanFactory.getBean(ErrorController.class)).willReturn(() -> null);
|
||||
|
||||
then(this.traceHandlerInterceptor.errorController()).isNotNull();
|
||||
then(this.traceHandlerInterceptor.errorController()).isNotNull();
|
||||
BDDMockito.then(this.beanFactory).should(only()).getBean(ErrorController.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,7 +38,6 @@ import org.springframework.cloud.netflix.zuul.metrics.EmptyTracerFactory;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceRequestAttributes;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
@@ -114,7 +113,5 @@ public class TracePostZuulFilterTests {
|
||||
.containsEntry("http.status_code", "456");
|
||||
then(spans.get(0).name()).isEqualTo("http:start");
|
||||
then(this.tracing.tracer().currentSpan()).isNull();
|
||||
BDDMockito.then(this.httpServletRequest).should().setAttribute(
|
||||
TraceRequestAttributes.HANDLED_SPAN_REQUEST_ATTR, "true");
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,8 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = { IntegrationSpanCollectorConfig.class, SampleMessagingApplication.class },
|
||||
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
@TestPropertySource(properties = { "sample.zipkin.enabled=true" })
|
||||
@TestPropertySource(properties = { "sample.zipkin.enabled=true",
|
||||
"spring.sleuth.http.legacy.enabled=true" })
|
||||
@DirtiesContext
|
||||
public class MessagingApplicationTests extends AbstractIntegrationTest {
|
||||
|
||||
@@ -76,7 +77,7 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
|
||||
long spanId = new Random().nextLong();
|
||||
|
||||
await().atMost(15, SECONDS).untilAsserted(() ->
|
||||
httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/", traceId, spanId).run()
|
||||
httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/", traceId, spanId).run()
|
||||
);
|
||||
|
||||
await().atMost(15, SECONDS).untilAsserted(() -> {
|
||||
@@ -140,7 +141,7 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
|
||||
|
||||
private Optional<Span> findLastHttpSpansParent() {
|
||||
return this.integrationTestSpanCollector.hashedSpans.stream()
|
||||
.filter(span -> "get".equals(span.name()) && span.kind() != null).findFirst();
|
||||
.filter(span -> "http:/".equals(span.name()) && span.kind() != null).findFirst();
|
||||
}
|
||||
|
||||
private Optional<Span> findSpanWithKind(Span.Kind kind) {
|
||||
|
||||
Reference in New Issue
Block a user