package org.skywalking.apm.plugin.tomcat78x; import org.skywalking.apm.agent.core.context.ContextCarrier; import org.skywalking.apm.agent.core.context.ContextManager; import org.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext; import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodInvokeContext; import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.skywalking.apm.util.StringUtil; import org.skywalking.apm.trace.Span; import org.skywalking.apm.trace.TraceSegment; import org.skywalking.apm.trace.tag.Tags; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * {@link TomcatInterceptor} fetch the serialized context data by using {@link HttpServletRequest#getHeader(String)}. * The {@link TraceSegment#refs} of current trace segment will reference to the trace * segment id of the previous level if the serialized context is not null. */ public class TomcatInterceptor implements InstanceMethodsAroundInterceptor { /** * Header name that the serialized context data stored in {@link HttpServletRequest#getHeader(String)}. */ public static final String HEADER_NAME_OF_CONTEXT_DATA = "SWTraceContext"; /** * Tomcat component. */ public static final String TOMCAT_COMPONENT = "Tomcat"; /** * The {@link TraceSegment#refs} of current trace segment will reference to the * trace segment id of the previous level if the serialized context is not null. * * @param context instance context, a class instance only has one {@link EnhancedClassInstanceContext} instance. * @param interceptorContext method context, includes class name, method name, etc. * @param result change this result, if you want to truncate the method. */ @Override public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, MethodInterceptResult result) { Object[] args = interceptorContext.allArguments(); HttpServletRequest request = (HttpServletRequest) args[0]; Span span = ContextManager.createSpan(request.getRequestURI()); Tags.COMPONENT.set(span, TOMCAT_COMPONENT); Tags.PEER_HOST.set(span, fetchRequestPeerHost(request)); Tags.PEER_PORT.set(span, request.getRemotePort()); Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_SERVER); Tags.URL.set(span, request.getRequestURL().toString()); Tags.SPAN_LAYER.asHttp(span); String tracingHeaderValue = request.getHeader(HEADER_NAME_OF_CONTEXT_DATA); if (!StringUtil.isEmpty(tracingHeaderValue)) { ContextManager.extract(new ContextCarrier().deserialize(tracingHeaderValue)); } } @Override public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, Object ret) { HttpServletResponse response = (HttpServletResponse) interceptorContext.allArguments()[1]; Span span = ContextManager.activeSpan(); Tags.STATUS_CODE.set(span, response.getStatus()); if (response.getStatus() != 200) { Tags.ERROR.set(span, true); } ContextManager.stopSpan(); return ret; } @Override public void handleMethodException(Throwable t, EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext) { Span span = ContextManager.activeSpan(); span.log(t); Tags.ERROR.set(span, true); } /** * @param request * @return */ public String fetchRequestPeerHost(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }