未验证 提交 79c1c0cc 编写于 作者: X xbkaishui 提交者: GitHub

Support http header tag (#5348)

上级 494d2a5b
......@@ -83,6 +83,8 @@ public final class Tags {
public static final StringTag PARAMS = new StringTag(11, "http.params", true);
public static final StringTag BODY = new StringTag(13, "http.body");
public static final StringTag HEADERS = new StringTag(14, "http.headers");
}
public static final StringTag LOGIC_ENDPOINT = new StringTag(12, "x-le");
......
......@@ -19,7 +19,9 @@
package org.apache.skywalking.apm.plugin.spring.mvc.v4;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
......@@ -40,8 +42,10 @@ import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.EnhanceRequireObjectCache;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.PathMappingCache;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.SpringMVCPluginConfig;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.RestMappingMethodInterceptor;
import org.hamcrest.MatcherAssert;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
......@@ -113,6 +117,12 @@ public class RestMappingMethodInterceptorTest {
response.getClass()
};
SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS = Arrays.asList("connection");
}
@After
public void cleanup() {
SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS = null;
}
@Test
......@@ -300,6 +310,35 @@ public class RestMappingMethodInterceptorTest {
SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class);
}
@Test
public void testGetWithRequestHeaderCollected() throws Throwable {
SpringTestCaseHelper.createCaseHandler(request, response, new SpringTestCaseHelper.CaseHandler() {
@Override
public void handleCase() throws Throwable {
controllerConstructorInterceptor.onConstruct(enhancedInstance, null);
RestMappingClass1 mappingClass1 = new RestMappingClass1();
Method m = mappingClass1.getClass().getMethod("getRequestURL");
when(request.getRequestURI()).thenReturn("/test/testRequestURL");
when(request.getRequestURL()).thenReturn(new StringBuffer("http://localhost:8080/test/getRequestURL"));
when(request.getHeaderNames()).thenReturn(new Vector(Arrays.asList("Connection", "Cookie")).elements());
when(request.getHeaders("connection")).thenReturn(new Vector(Arrays.asList("keep-alive")).elements());
when(request.getHeaders("cookie")).thenReturn(new Vector(Arrays.asList("dummy cookies")).elements());
ServletRequestAttributes servletRequestAttributes = new ServletRequestAttributes(request, response);
RequestContextHolder.setRequestAttributes(servletRequestAttributes);
interceptor.beforeMethod(enhancedInstance, m, arguments, argumentType, methodInterceptResult);
interceptor.afterMethod(enhancedInstance, m, arguments, argumentType, null);
}
});
assertThat(segmentStorage.getTraceSegments().size(), is(1));
TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
assertHttpSpan(spans.get(0), "/getRequestURL");
SpanAssert.assertTag(spans.get(0), 2, "connection=[keep-alive]");
}
private void assertTraceSegmentRef(TraceSegmentRef ref) {
MatcherAssert.assertThat(SegmentRefHelper.getParentServiceInstance(ref), is("instance"));
assertThat(SegmentRefHelper.getSpanId(ref), is(3));
......
......@@ -20,6 +20,8 @@ package org.apache.skywalking.apm.plugin.spring.mvc.commons;
import org.apache.skywalking.apm.agent.core.boot.PluginConfig;
import java.util.List;
public class SpringMVCPluginConfig {
public static class Plugin {
@PluginConfig(root = SpringMVCPluginConfig.class)
......@@ -45,6 +47,18 @@ public class SpringMVCPluginConfig {
* added for the sake of performance
*/
public static int HTTP_PARAMS_LENGTH_THRESHOLD = 1024;
/**
* When {@link Plugin.Http#INCLUDE_HTTP_HEADERS} declares header names, this threshold controls the length
* limitation of all header values. use negative values to keep and send the complete headers.
* Note. this config item is added for the sake of performance.
*/
public static int HTTP_HEADERS_LENGTH_THRESHOLD = 2048;
/**
* It controls what header data should be collected, this is for security purpose, values must be in lower case
*/
public static List<String> INCLUDE_HTTP_HEADERS ;
}
}
}
......@@ -19,9 +19,15 @@
package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.Map;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.skywalking.apm.agent.core.context.CarrierItem;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
......@@ -113,6 +119,10 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
collectHttpParam(request, span);
}
if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) {
collectHttpHeaders(request, span);
}
stackDepth = new StackDepth();
ContextManager.getRuntimeContext().put(CONTROLLER_METHOD_STACK_DEPTH, stackDepth);
} else {
......@@ -208,4 +218,23 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
Tags.HTTP.PARAMS.set(span, tagValue);
}
}
private void collectHttpHeaders(HttpServletRequest request, AbstractSpan span) {
final List<String> headersList = new LinkedList<>();
SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream().filter(headerName -> request.getHeaders(headerName) != null).forEach(headerName -> {
Enumeration<String> headerValues = request.getHeaders(headerName);
List<String> valueList = Collections.list(headerValues);
if (!CollectionUtil.isEmpty(valueList)) {
String headerValue = valueList.toString();
headersList.add(headerName + "=" + headerValue);
}
});
if (!headersList.isEmpty()) {
String tagValue = headersList.stream().collect(Collectors.joining("\n"));
tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD > 0 ?
StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.HEADERS.set(span, tagValue);
}
}
}
......@@ -132,6 +132,8 @@ property key | Description | Default |
`plugin.tomcat.collect_http_params`| This config item controls that whether the Tomcat plugin should collect the parameters of the request. Also, activate implicitly in the profiled trace. | `false` |
`plugin.springmvc.collect_http_params`| This config item controls that whether the SpringMVC plugin should collect the parameters of the request, when your Spring application is based on Tomcat, consider only setting either `plugin.tomcat.collect_http_params` or `plugin.springmvc.collect_http_params`. Also, activate implicitly in the profiled trace. | `false` |
`plugin.http.http_params_length_threshold`| When `COLLECT_HTTP_PARAMS` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete parameters, NB. this config item is added for the sake of performance. | `1024` |
`plugin.http.http_headers_length_threshold`| When `include_http_headers` declares header names, this threshold controls the length limitation of all header values. use negative values to keep and send the complete headers. Note. this config item is added for the sake of performance. | `2048` |
`plugin.http.include_http_headers`| Set the header names, which should be collected by the plugin. Header name must follow `javax.servlet.http` definition. Multiple names should be split by comma. | ``(No header would be collected) |
`plugin.feign.collect_request_body`| This config item controls that whether the Feign plugin should collect the http body of the request. | `false` |
`plugin.feign.filter_length_limit`| When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. | `1024` |
`plugin.feign.supported_content_types_prefix`| When `COLLECT_REQUEST_BODY` is enabled and content-type start with SUPPORTED_CONTENT_TYPES_PREFIX, collect the body of the request , multiple paths should be separated by `,` | `application/json,text/` |
......
......@@ -62,6 +62,7 @@ segmentItems:
tags:
- {key: url, value: 'http://localhost:8080/spring-4.3.x-scenario/case/spring3/'}
- {key: http.method, value: GET}
- {key: http.headers, value: 'mock_header=[mock_value]'}
refs:
- {parentEndpoint: /case/resttemplate, networkAddress: 'localhost:8080', refType: CrossProcess,
parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not
......
......@@ -19,3 +19,5 @@ entryService: http://localhost:8080/spring-4.3.x-scenario/case/resttemplate
healthCheck: http://localhost:8080/spring-4.3.x-scenario/healthCheck
runningMode: with_optional
withPlugins: apm-spring-annotation-plugin-*.jar
environment:
- CATALINA_OPTS="-Dskywalking.plugin.http.include_http_headers=mock_header"
......@@ -43,7 +43,7 @@ public class RestTemplateController {
@RequestMapping("/case/resttemplate")
@ResponseBody
public String restTemplate() throws IOException {
Request request = new Request.Builder().url(url + "/case/spring3/").build();
Request request = new Request.Builder().header("mock_header", "mock_value").url(url + "/case/spring3/").build();
Response response = new OkHttpClient().newCall(request).execute();
logger.info(response.toString());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册