|
|
|
@ -16,17 +16,17 @@
|
|
|
|
|
|
|
|
|
|
package org.springframework.boot.actuate.metrics.web.reactive.server;
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
import java.util.function.BiFunction;
|
|
|
|
|
|
|
|
|
|
import io.micrometer.core.instrument.MeterRegistry;
|
|
|
|
|
import io.micrometer.core.instrument.Tag;
|
|
|
|
|
import io.micrometer.core.instrument.Tags;
|
|
|
|
|
import reactor.core.publisher.Mono;
|
|
|
|
|
|
|
|
|
|
import org.springframework.util.Assert;
|
|
|
|
|
import org.springframework.web.reactive.function.server.HandlerFilterFunction;
|
|
|
|
|
import org.springframework.web.reactive.function.server.HandlerFunction;
|
|
|
|
|
import org.springframework.web.reactive.function.server.RouterFunction;
|
|
|
|
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
|
|
|
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
|
|
|
@ -41,53 +41,53 @@ public class RouterFunctionMetrics {
|
|
|
|
|
|
|
|
|
|
private final MeterRegistry registry;
|
|
|
|
|
|
|
|
|
|
private BiFunction<ServerRequest, ServerResponse, Collection<Tag>> defaultTags = (
|
|
|
|
|
ServerRequest request, ServerResponse response) -> response != null
|
|
|
|
|
? Arrays.asList(method(request), status(response))
|
|
|
|
|
: Collections.singletonList(method(request));
|
|
|
|
|
private final BiFunction<ServerRequest, ServerResponse, Iterable<Tag>> defaultTags;
|
|
|
|
|
|
|
|
|
|
public RouterFunctionMetrics(MeterRegistry registry) {
|
|
|
|
|
Assert.notNull(registry, "Registry must not be null");
|
|
|
|
|
this.registry = registry;
|
|
|
|
|
this.defaultTags = this::defaultTags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private RouterFunctionMetrics(MeterRegistry registry,
|
|
|
|
|
BiFunction<ServerRequest, ServerResponse, Iterable<Tag>> defaultTags) {
|
|
|
|
|
Assert.notNull(registry, "Registry must not be null");
|
|
|
|
|
Assert.notNull(defaultTags, "DefaultTags must not be null");
|
|
|
|
|
this.registry = registry;
|
|
|
|
|
this.defaultTags = defaultTags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Iterable<Tag> defaultTags(ServerRequest request, ServerResponse response) {
|
|
|
|
|
if (response == null) {
|
|
|
|
|
return Tags.of(getMethodTag(request));
|
|
|
|
|
}
|
|
|
|
|
return Tags.of(getMethodTag(request), getStatusTag(response));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configures the default tags.
|
|
|
|
|
* Returns a new {@link RouterFunctionMetrics} instance with the specified default
|
|
|
|
|
* tags.
|
|
|
|
|
* @param defaultTags Generate a list of tags to apply to the timer.
|
|
|
|
|
* {@code ServerResponse} may be null.
|
|
|
|
|
* @return {@code this} for further configuration
|
|
|
|
|
*/
|
|
|
|
|
public RouterFunctionMetrics defaultTags(
|
|
|
|
|
BiFunction<ServerRequest, ServerResponse, Collection<Tag>> defaultTags) {
|
|
|
|
|
this.defaultTags = defaultTags;
|
|
|
|
|
return this;
|
|
|
|
|
BiFunction<ServerRequest, ServerResponse, Iterable<Tag>> defaultTags) {
|
|
|
|
|
return new RouterFunctionMetrics(this.registry, defaultTags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public HandlerFilterFunction<ServerResponse, ServerResponse> timer(String name) {
|
|
|
|
|
return timer(name, Collections.emptyList());
|
|
|
|
|
return timer(name, Tags.empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public HandlerFilterFunction<ServerResponse, ServerResponse> timer(String name,
|
|
|
|
|
String... tags) {
|
|
|
|
|
return timer(name, Tags.zip(tags));
|
|
|
|
|
return timer(name, Tags.of(tags));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public HandlerFilterFunction<ServerResponse, ServerResponse> timer(String name,
|
|
|
|
|
Iterable<Tag> tags) {
|
|
|
|
|
return (request, next) -> {
|
|
|
|
|
final long start = System.nanoTime();
|
|
|
|
|
return next.handle(request).doOnSuccess((response) -> {
|
|
|
|
|
Iterable<Tag> allTags = Tags.concat(tags,
|
|
|
|
|
this.defaultTags.apply(request, response));
|
|
|
|
|
this.registry.timer(name, allTags).record(System.nanoTime() - start,
|
|
|
|
|
TimeUnit.NANOSECONDS);
|
|
|
|
|
}).doOnError((error) -> {
|
|
|
|
|
// FIXME how do we get the response under an error condition?
|
|
|
|
|
Iterable<Tag> allTags = Tags.concat(tags,
|
|
|
|
|
this.defaultTags.apply(request, null));
|
|
|
|
|
this.registry.timer(name, allTags).record(System.nanoTime() - start,
|
|
|
|
|
TimeUnit.NANOSECONDS);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
return new MetricsFilter(name, Tags.of(tags));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -95,7 +95,7 @@ public class RouterFunctionMetrics {
|
|
|
|
|
* @param request The HTTP request.
|
|
|
|
|
* @return A "method" tag whose value is a capitalized method (e.g. GET).
|
|
|
|
|
*/
|
|
|
|
|
public static Tag method(ServerRequest request) {
|
|
|
|
|
public static Tag getMethodTag(ServerRequest request) {
|
|
|
|
|
return Tag.of("method", request.method().toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -104,8 +104,48 @@ public class RouterFunctionMetrics {
|
|
|
|
|
* @param response The HTTP response.
|
|
|
|
|
* @return A "status" tag whose value is the numeric status code.
|
|
|
|
|
*/
|
|
|
|
|
public static Tag status(ServerResponse response) {
|
|
|
|
|
public static Tag getStatusTag(ServerResponse response) {
|
|
|
|
|
return Tag.of("status", response.statusCode().toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@link HandlerFilterFunction} to handle calling micrometer.
|
|
|
|
|
*/
|
|
|
|
|
private class MetricsFilter
|
|
|
|
|
implements HandlerFilterFunction<ServerResponse, ServerResponse> {
|
|
|
|
|
|
|
|
|
|
private final String name;
|
|
|
|
|
|
|
|
|
|
private final Tags tags;
|
|
|
|
|
|
|
|
|
|
MetricsFilter(String name, Tags tags) {
|
|
|
|
|
this.name = name;
|
|
|
|
|
this.tags = tags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Mono<ServerResponse> filter(ServerRequest request,
|
|
|
|
|
HandlerFunction<ServerResponse> next) {
|
|
|
|
|
long start = System.nanoTime();
|
|
|
|
|
return next.handle(request).doOnSuccess((response) -> {
|
|
|
|
|
timer(start, request, response);
|
|
|
|
|
}).doOnError((error) -> {
|
|
|
|
|
// FIXME how do we get the response under an error condition?
|
|
|
|
|
timer(start, request, null);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Iterable<Tag> getDefaultTags(ServerRequest request,
|
|
|
|
|
ServerResponse response) {
|
|
|
|
|
return RouterFunctionMetrics.this.defaultTags.apply(request, response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void timer(long start, ServerRequest request, ServerResponse response) {
|
|
|
|
|
Tags allTags = this.tags.and(getDefaultTags(request, response));
|
|
|
|
|
RouterFunctionMetrics.this.registry.timer(this.name, allTags)
|
|
|
|
|
.record(System.nanoTime() - start, TimeUnit.NANOSECONDS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|