可以在请求中看到自定义请求信息input和返回值output,方便快速定位问题
- <dependency>
- <groupId>org.apache.skywalking</groupId>
- <artifactId>apm-toolkit-trace</artifactId>
- <version>9.1.0</version>
- <scope>provided</scope>
- </dependency>
- //将入参发送skywalking
- ActiveSpan.tag("input", sb.toString());
- //将返回值发送skywalking
- ActiveSpan.tag("output", responseBody);
因为HttpServletRequest
和HttpServletResponse
中的body只能读取一次,如果在Filte中读取的话,应用本身就读取不到,所以需要使用ContentCachingRequestWrapper
和ContentCachingResponseWrapper
,所以需要添加一个过滤器即可
-
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
- import java.util.Enumeration;
- import java.util.Set;
- import java.util.stream.Collectors;
-
- import javax.servlet.FilterChain;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpFilter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Component;
- import org.springframework.util.StringUtils;
- import org.springframework.web.util.ContentCachingRequestWrapper;
- import org.springframework.web.util.ContentCachingResponseWrapper;
-
- import com.google.common.collect.ImmutableSet;
-
- @Component
- public class ApmHttpInfo extends HttpFilter {
-
- private Logger log = LoggerFactory.getLogger(getClass());
- private static final ImmutableSet
IGNORED_HEADERS; - static {
- Set
ignoredHeaders = ImmutableSet.of("Content-Type", "User-Agent", "Accept", "Cache-Control", - "Postman-Token", "Host", "Accept-Encoding", "Connection", "Content-Length").stream()
- .map(String::toUpperCase).collect(Collectors.toSet());
- IGNORED_HEADERS = ImmutableSet.copyOf(ignoredHeaders);
- }
-
- @Override
- public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws IOException, ServletException {
- ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
- ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
-
- try {
- filterChain.doFilter(requestWrapper, responseWrapper);
- } finally {
- try {
- // 构造请求信息: 比如 curl -X GET http://localhost:18080/getPerson?id=1 -H 'token:
- // me-token' -d '{ "name": "hello" }'
- // 构造请求的方法&URL&参数
- StringBuilder sb = new StringBuilder("curl").append(" -X ").append(request.getMethod()).append(" ")
- .append(request.getRequestURL().toString());
- if (StringUtils.hasLength(request.getQueryString())) {
- sb.append("?").append(request.getQueryString());
- }
-
- // 构造header
- Enumeration
headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) {
- String headerName = headerNames.nextElement();
- if (!IGNORED_HEADERS.contains(headerName.toUpperCase())) {
- sb.append(" -H '").append(headerName).append(": ").append(request.getHeader(headerName))
- .append("'");
- }
- }
- // 获取body
- String body = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
- if (StringUtils.hasLength(body)) {
- sb.append(" -d '").append(body).append("'");
- }
- // 输出到input
- ActiveSpan.tag("input", sb.toString());
-
- // 获取返回值body
- String responseBody = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
- // 输出到output
- ActiveSpan.tag("output", responseBody);
- } catch (Exception e) {
- log.error("fail to build http log", e);
- } finally {
- // 这一行必须添加,否则就一直不返回
- responseWrapper.copyBodyToResponse();
- }
- }
- }
- }