Java tutorial
/* * Copyright 2013-2016 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.lang.invoke.MethodHandles; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.web.ErrorController; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.util.ExceptionUtils; 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(MethodHandles.lookup().lookupClass()); private final BeanFactory beanFactory; private Tracer tracer; private TraceKeys traceKeys; private ErrorController errorController; 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) : getTracer().createSpan(spanName); 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 getErrorController() != null && getErrorController().getErrorPath().equals(request.getRequestURI()); } private void addClassMethodTag(Object handler, Span span) { if (handler instanceof HandlerMethod) { String methodName = ((HandlerMethod) handler).getMethod().getName(); getTracer().addTag(getTraceKeys().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); } getTracer().addTag(getTraceKeys().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); Span rootSpanFromRequest = getRootSpanFromAttribute(request); if (log.isDebugEnabled()) { log.debug("Closing the span " + spanFromRequest + " and detaching its parent " + rootSpanFromRequest + " since the request is asynchronous"); } getTracer().close(spanFromRequest); getTracer().detach(rootSpanFromRequest); } @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) { String errorMsg = ExceptionUtils.getExceptionMessage(ex); if (log.isDebugEnabled()) { log.debug("Adding an error tag [" + errorMsg + "] to span " + span + ""); } getTracer().addTag(Span.SPAN_ERROR_TAG_NAME, errorMsg); } if (getNewSpanFromAttribute(request) != null) { if (log.isDebugEnabled()) { log.debug("Closing span " + span); } Span newSpan = getNewSpanFromAttribute(request); getTracer().continueSpan(newSpan); getTracer().close(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 Tracer getTracer() { if (this.tracer == null) { this.tracer = this.beanFactory.getBean(Tracer.class); } return this.tracer; } private TraceKeys getTraceKeys() { if (this.traceKeys == null) { this.traceKeys = this.beanFactory.getBean(TraceKeys.class); } return this.traceKeys; } private ErrorController getErrorController() { if (this.errorController == null) { this.errorController = this.beanFactory.getBean(ErrorController.class); } return this.errorController; } }