1.效果图
可以在请求中看到自定义请求信息input和返回值output,方便快速定位问题
2.添加依赖
<dependency><groupId>org.apache.skywalking</groupId><artifactId>apm-toolkit-trace</artifactId><version>9.1.0</version><scope>provided</scope></dependency>
3.核心语法
//将入参发送skywalking
ActiveSpan.tag("input", sb.toString());
//将返回值发送skywalking
ActiveSpan.tag("output", responseBody);
4.添加过滤器在过滤器中发送到skywalking
因为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<String> IGNORED_HEADERS;static {Set<String> 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);}@Overridepublic 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());}// 构造headerEnumeration<String> 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("'");}}// 获取bodyString body = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);if (StringUtils.hasLength(body)) {sb.append(" -d '").append(body).append("'");}// 输出到inputActiveSpan.tag("input", sb.toString());// 获取返回值bodyString responseBody = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);// 输出到outputActiveSpan.tag("output", responseBody);} catch (Exception e) {log.error("fail to build http log", e);} finally {// 这一行必须添加,否则就一直不返回responseWrapper.copyBodyToResponse();}}}
}