前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊springboot2的httptrace

聊聊springboot2的httptrace

作者头像
code4it
发布2018-09-17 16:32:11
2.1K0
发布2018-09-17 16:32:11
举报
文章被收录于专栏:码匠的流水账码匠的流水账

本文主要研究下springboot2的httptrace

HttpTraceAutoConfiguration

spring-boot-actuator-autoconfigure-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/trace/http/HttpTraceAutoConfiguration.java

代码语言:javascript
复制
@Configuration
@ConditionalOnWebApplication
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(HttpTraceProperties.class)
public class HttpTraceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(HttpTraceRepository.class)
    public InMemoryHttpTraceRepository traceRepository() {
        return new InMemoryHttpTraceRepository();
    }

    @Bean
    @ConditionalOnMissingBean
    public HttpExchangeTracer httpExchangeTracer(HttpTraceProperties traceProperties) {
        return new HttpExchangeTracer(traceProperties.getInclude());
    }

    @ConditionalOnWebApplication(type = Type.SERVLET)
    static class ServletTraceFilterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public HttpTraceFilter httpTraceFilter(HttpTraceRepository repository,
                HttpExchangeTracer tracer) {
            return new HttpTraceFilter(repository, tracer);
        }

    }

    @ConditionalOnWebApplication(type = Type.REACTIVE)
    static class ReactiveTraceFilterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public HttpTraceWebFilter httpTraceWebFilter(HttpTraceRepository repository,
                HttpExchangeTracer tracer, HttpTraceProperties traceProperties) {
            return new HttpTraceWebFilter(repository, tracer,
                    traceProperties.getInclude());
        }

    }

}

可以看到这里按servlet及reactive两种方式分别注入不同的filter,servlet的是HttpTraceFilter,reactive的是HttpTraceWebFilter 无论是servlet方式还是reactive方式,都会注入HttpTraceRepository以及HttpExchangeTracer

InMemoryHttpTraceRepository

spring-boot-actuator-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/trace/http/InMemoryHttpTraceRepository.java

代码语言:javascript
复制
public class InMemoryHttpTraceRepository implements HttpTraceRepository {

    private int capacity = 100;

    private boolean reverse = true;

    private final List<HttpTrace> traces = new LinkedList<>();

    /**
     * Flag to say that the repository lists traces in reverse order.
     * @param reverse flag value (default true)
     */
    public void setReverse(boolean reverse) {
        synchronized (this.traces) {
            this.reverse = reverse;
        }
    }

    /**
     * Set the capacity of the in-memory repository.
     * @param capacity the capacity
     */
    public void setCapacity(int capacity) {
        synchronized (this.traces) {
            this.capacity = capacity;
        }
    }

    @Override
    public List<HttpTrace> findAll() {
        synchronized (this.traces) {
            return Collections.unmodifiableList(new ArrayList<>(this.traces));
        }
    }

    @Override
    public void add(HttpTrace trace) {
        synchronized (this.traces) {
            while (this.traces.size() >= this.capacity) {
                this.traces.remove(this.reverse ? this.capacity - 1 : 0);
            }
            if (this.reverse) {
                this.traces.add(0, trace);
            }
            else {
                this.traces.add(trace);
            }
        }
    }

}

这里默认设置了只存最近100请求记录,不然这个使用synchronized,貌似不是很高效

HttpExchangeTracer

spring-boot-actuator-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/trace/http/HttpExchangeTracer.java

代码语言:javascript
复制
public class HttpExchangeTracer {

    private final Set<Include> includes;

    /**
     * Creates a new {@code HttpExchangeTracer} that will use the given {@code includes}
     * to determine the contents of its traces.
     * @param includes the includes
     */
    public HttpExchangeTracer(Set<Include> includes) {
        this.includes = includes;
    }

    /**
     * Begins the tracing of the exchange that was initiated by the given {@code request}
     * being received.
     * @param request the received request
     * @return the HTTP trace for the
     */
    public final HttpTrace receivedRequest(TraceableRequest request) {
        return new HttpTrace(new FilteredTraceableRequest(request));
    }

    /**
     * Ends the tracing of the exchange that is being concluded by sending the given
     * {@code response}.
     * @param trace the trace for the exchange
     * @param response the response that concludes the exchange
     * @param principal a supplier for the exchange's principal
     * @param sessionId a supplier for the id of the exchange's session
     */
    public final void sendingResponse(HttpTrace trace, TraceableResponse response,
            Supplier<Principal> principal, Supplier<String> sessionId) {
        setIfIncluded(Include.TIME_TAKEN,
                () -> System.currentTimeMillis() - trace.getTimestamp().toEpochMilli(),
                trace::setTimeTaken);
        setIfIncluded(Include.SESSION_ID, sessionId, trace::setSessionId);
        setIfIncluded(Include.PRINCIPAL, principal, trace::setPrincipal);
        trace.setResponse(
                new HttpTrace.Response(new FilteredTraceableResponse(response)));
    }
    //......
}

HttpExchangeTracer是一个简易的tracer,主要是receivedRequest方法记录请求生成HttpTrace,然后sendingResponse结束本次trace,并将记录添加到HttpTrace

Filter

HttpTraceFilter

spring-boot-actuator-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/web/trace/servlet/HttpTraceFilter.java

代码语言:javascript
复制
public class HttpTraceFilter extends OncePerRequestFilter implements Ordered {

    // Not LOWEST_PRECEDENCE, but near the end, so it has a good chance of catching all
    // enriched headers, but users can add stuff after this if they want to
    private int order = Ordered.LOWEST_PRECEDENCE - 10;

    private final HttpTraceRepository repository;

    private final HttpExchangeTracer tracer;

    /**
     * Create a new {@link HttpTraceFilter} instance.
     * @param repository the trace repository
     * @param tracer used to trace exchanges
     */
    public HttpTraceFilter(HttpTraceRepository repository, HttpExchangeTracer tracer) {
        this.repository = repository;
        this.tracer = tracer;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        TraceableHttpServletRequest traceableRequest = new TraceableHttpServletRequest(
                request);
        HttpTrace trace = this.tracer.receivedRequest(traceableRequest);
        int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
        try {
            filterChain.doFilter(request, response);
            status = response.getStatus();
        }
        finally {
            TraceableHttpServletResponse traceableResponse = new TraceableHttpServletResponse(
                    status == response.getStatus() ? response
                            : new CustomStatusResponseWrapper(response, status));
            this.tracer.sendingResponse(trace, traceableResponse,
                    request::getUserPrincipal, () -> getSessionId(request));
            this.repository.add(trace);
        }
    }

    private String getSessionId(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        return session == null ? null : session.getId();
    }

    private static final class CustomStatusResponseWrapper
            extends HttpServletResponseWrapper {

        private final int status;

        private CustomStatusResponseWrapper(HttpServletResponse response, int status) {
            super(response);
            this.status = status;
        }

        @Override
        public int getStatus() {
            return this.status;
        }

    }

}

可以看到继承的是OncePerRequestFilter

HttpTraceWebFilter

spring-boot-actuator-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/web/trace/reactive/HttpTraceWebFilter.java

代码语言:javascript
复制
public class HttpTraceWebFilter implements WebFilter, Ordered {

    private static final Object NONE = new Object();

    // Not LOWEST_PRECEDENCE, but near the end, so it has a good chance of catching all
    // enriched headers, but users can add stuff after this if they want to
    private int order = Ordered.LOWEST_PRECEDENCE - 10;

    private final HttpTraceRepository repository;

    private final HttpExchangeTracer tracer;

    private final Set<Include> includes;

    public HttpTraceWebFilter(HttpTraceRepository repository, HttpExchangeTracer tracer,
            Set<Include> includes) {
        this.repository = repository;
        this.tracer = tracer;
        this.includes = includes;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        Mono<?> principal = this.includes.contains(Include.PRINCIPAL)
                ? exchange.getPrincipal().cast(Object.class).defaultIfEmpty(NONE)
                : Mono.just(NONE);
        Mono<?> session = this.includes.contains(Include.SESSION_ID)
                ? exchange.getSession() : Mono.just(NONE);
        return Mono.zip(principal, session)
                .flatMap((tuple) -> filter(exchange, chain,
                        asType(tuple.getT1(), Principal.class),
                        asType(tuple.getT2(), WebSession.class)));
    }

    private <T> T asType(Object object, Class<T> type) {
        if (type.isInstance(object)) {
            return type.cast(object);
        }
        return null;
    }

    private Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain,
            Principal principal, WebSession session) {
        ServerWebExchangeTraceableRequest request = new ServerWebExchangeTraceableRequest(
                exchange);
        HttpTrace trace = this.tracer.receivedRequest(request);
        return chain.filter(exchange).doAfterSuccessOrError((aVoid, ex) -> {
            this.tracer.sendingResponse(trace,
                    new TraceableServerHttpResponse(ex == null ? exchange.getResponse()
                            : new CustomStatusResponseDecorator(ex,
                                    exchange.getResponse())),
                    () -> principal, () -> getStartedSessionId(session));
            this.repository.add(trace);
        });
    }

    private String getStartedSessionId(WebSession session) {
        return (session != null && session.isStarted()) ? session.getId() : null;
    }

    private static final class CustomStatusResponseDecorator
            extends ServerHttpResponseDecorator {

        private final HttpStatus status;

        private CustomStatusResponseDecorator(Throwable ex, ServerHttpResponse delegate) {
            super(delegate);
            this.status = ex instanceof ResponseStatusException
                    ? ((ResponseStatusException) ex).getStatus()
                    : HttpStatus.INTERNAL_SERVER_ERROR;
        }

        @Override
        public HttpStatus getStatusCode() {
            return this.status;
        }

    }

}

可以看到继承的是WebFilter

Endpoint

HttpTraceEndpointAutoConfiguration

spring-boot-actuator-autoconfigure-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/trace/http/HttpTraceEndpointAutoConfiguration.java

代码语言:javascript
复制
@Configuration
@AutoConfigureAfter(HttpTraceAutoConfiguration.class)
public class HttpTraceEndpointAutoConfiguration {

    @Bean
    @ConditionalOnBean(HttpTraceRepository.class)
    @ConditionalOnMissingBean
    @ConditionalOnEnabledEndpoint
    public HttpTraceEndpoint httpTraceEndpoint(HttpTraceRepository traceRepository) {
        return new HttpTraceEndpoint(traceRepository);
    }

}

这里使用traceRepository创建了HttpTraceEndpoint

HttpTraceEndpoint

spring-boot-actuator-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/trace/http/HttpTraceEndpoint.java

代码语言:javascript
复制
/**
 * {@link Endpoint} to expose {@link HttpTrace} information.
 *
 * @author Dave Syer
 * @author Andy Wilkinson
 * @since 2.0.0
 */
@Endpoint(id = "httptrace")
public class HttpTraceEndpoint {

    private final HttpTraceRepository repository;

    /**
     * Create a new {@link HttpTraceEndpoint} instance.
     * @param repository the trace repository
     */
    public HttpTraceEndpoint(HttpTraceRepository repository) {
        Assert.notNull(repository, "Repository must not be null");
        this.repository = repository;
    }

    @ReadOperation
    public HttpTraceDescriptor traces() {
        return new HttpTraceDescriptor(this.repository.findAll());
    }

    /**
     * A description of an application's {@link HttpTrace} entries. Primarily intended for
     * serialization to JSON.
     */
    public static final class HttpTraceDescriptor {

        private final List<HttpTrace> traces;

        private HttpTraceDescriptor(List<HttpTrace> traces) {
            this.traces = traces;
        }

        public List<HttpTrace> getTraces() {
            return this.traces;
        }

    }

}

输出实例

代码语言:javascript
复制
{
  "traces": [
    {
      "timestamp": "2018-04-21T14:14:36.256Z",
      "principal": null,
      "session": null,
      "request": {
        "method": "GET",
        "uri": "http://localhost:8080/actuator",
        "headers": {
          "host": [
            "localhost:8080"
          ],
          "connection": [
            "keep-alive"
          ],
          "upgrade-insecure-requests": [
            "1"
          ],
          "user-agent": [
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36"
          ],
          "accept": [
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
          ],
          "accept-encoding": [
            "gzip, deflate, br"
          ],
          "accept-language": [
            "zh-CN,zh;q=0.9,en;q=0.8"
          ],
          "cookie": [
            "hibext_instdsigdipv2=1; _ga=GA1.1.933052261.1524234775; _gid=GA1.1.1398833521.1524234775"
          ]
        },
        "remoteAddress": null
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": [
            "application/vnd.spring-boot.actuator.v2+json;charset=UTF-8"
          ],
          "Transfer-Encoding": [
            "chunked"
          ],
          "Date": [
            "Sat, 21 Apr 2018 14:14:36 GMT"
          ]
        }
      },
      "timeTaken": 110
    }
  ]
}

小结

httptrace默认是开的,对于servlet及reactive的方式,分别使用了不同的filter,前者是HttpTraceFilter继承了OncePerRequestFilter,后者是HttpTraceWebFilter继承了WebFilter。可以自定义替换掉InMemoryHttpTraceRepository,自己将请求日志异步传递到日志中心。

doc

  • Spring Boot Reference Guide
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • HttpTraceAutoConfiguration
    • InMemoryHttpTraceRepository
      • HttpExchangeTracer
      • Filter
        • HttpTraceFilter
          • HttpTraceWebFilter
          • Endpoint
            • HttpTraceEndpointAutoConfiguration
              • HttpTraceEndpoint
              • 小结
              • doc
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档