skywalking展示http請求和響應

使用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請求和返回值的的輸出

因爲HttpServletRequestHttpServletResponse中的body只能讀取一次,如果在Filte中讀取的話,應用本身就讀取不到,所以需要使用ContentCachingRequestWrapperContentCachingResponseWrapper

@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();
            }
        }
    }
}

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章