001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.actuate.metrics.web.reactive.server;
018
019import java.util.concurrent.TimeUnit;
020
021import io.micrometer.core.instrument.MeterRegistry;
022import io.micrometer.core.instrument.Tag;
023import org.reactivestreams.Publisher;
024import reactor.core.publisher.Mono;
025
026import org.springframework.core.Ordered;
027import org.springframework.core.annotation.Order;
028import org.springframework.http.server.reactive.ServerHttpResponse;
029import org.springframework.web.server.ServerWebExchange;
030import org.springframework.web.server.WebFilter;
031import org.springframework.web.server.WebFilterChain;
032
033/**
034 * Intercepts incoming HTTP requests handled by Spring WebFlux handlers.
035 *
036 * @author Jon Schneider
037 * @author Brian Clozel
038 * @since 2.0.0
039 */
040@Order(Ordered.HIGHEST_PRECEDENCE + 1)
041public class MetricsWebFilter implements WebFilter {
042
043        private final MeterRegistry registry;
044
045        private final WebFluxTagsProvider tagsProvider;
046
047        private final String metricName;
048
049        private final boolean autoTimeRequests;
050
051        /**
052         * Create a new {@code MetricsWebFilter}.
053         * @param registry the registry to which metrics are recorded
054         * @param tagsProvider provider for metrics tags
055         * @param metricName name of the metric to record
056         * @deprecated since 2.0.6 in favor of
057         * {@link #MetricsWebFilter(MeterRegistry, WebFluxTagsProvider, String, boolean)}
058         */
059        @Deprecated
060        public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
061                        String metricName) {
062                this(registry, tagsProvider, metricName, true);
063        }
064
065        public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider,
066                        String metricName, boolean autoTimeRequests) {
067                this.registry = registry;
068                this.tagsProvider = tagsProvider;
069                this.metricName = metricName;
070                this.autoTimeRequests = autoTimeRequests;
071        }
072
073        @Override
074        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
075                if (this.autoTimeRequests) {
076                        return chain.filter(exchange).compose((call) -> filter(exchange, call));
077                }
078                return chain.filter(exchange);
079        }
080
081        private Publisher<Void> filter(ServerWebExchange exchange, Mono<Void> call) {
082                long start = System.nanoTime();
083                ServerHttpResponse response = exchange.getResponse();
084                return call.doOnSuccess((done) -> success(exchange, start)).doOnError((cause) -> {
085                        if (response.isCommitted()) {
086                                error(exchange, start, cause);
087                        }
088                        else {
089                                response.beforeCommit(() -> {
090                                        error(exchange, start, cause);
091                                        return Mono.empty();
092                                });
093                        }
094                });
095        }
096
097        private void success(ServerWebExchange exchange, long start) {
098                Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, null);
099                this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
100                                TimeUnit.NANOSECONDS);
101        }
102
103        private void error(ServerWebExchange exchange, long start, Throwable cause) {
104                Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, cause);
105                this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
106                                TimeUnit.NANOSECONDS);
107        }
108
109}