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}