使用skywalking跟蹤請求的時候,是看不到http請求的參數的,這樣不方便定位問題。本文通過自定義的方式(
ActiveSpan.tag
),實現了http請求和響應的輸出,方便快速定位問題
效果圖
可以在請求中看到自定義請求信息input和返回值output,方便快速定位問題
實現請求和響應的輸出
- 添加依賴
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.7.0</version>
<scope>provided</scope>
</dependency>
- 使用
ActiveSpan.tag
輸出到skywalking
ActiveSpan.tag("input", sb.toString());
- 實現http請求和返回值的的輸出
因爲
HttpServletRequest
和HttpServletResponse
中的body只能讀取一次,如果在Filte中讀取的話,應用本身就讀取不到,所以需要使用ContentCachingRequestWrapper
和ContentCachingResponseWrapper
@Slf4j
@Component
public class ApmHttpInfo extends HttpFilter {
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);
}
@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<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("'");
}
}
//獲取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.warn("fail to build http log", e);
} finally {
//這一行必須添加,否則就一直不返回
responseWrapper.copyBodyToResponse();
}
}
}
}