Skip to content

Commit

Permalink
Add longTask to @timed (fixes #8)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkschneider committed May 18, 2017
1 parent 2b88f69 commit be99318
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package org.springframework.metrics.instrument;

import java.util.concurrent.Callable;
import java.util.function.Consumer;

public interface LongTaskTimer extends Meter {
Expand All @@ -25,7 +24,7 @@ public interface LongTaskTimer extends Meter {
* @param f Function to execute and measure the execution time.
* @return The return value of `f`.
*/
default <T> T record(Callable<T> f) throws Exception {
default <T> T recordThrowable(ThrowableCallable<T> f) throws Throwable {
long id = start();
try {
return f.call();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@

@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Repeatable(TimedSet.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
String value() default "";

String[] extraTags() default {};

boolean longTask() default false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.springframework.metrics.instrument.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface TimedSet {
Timed[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.springframework.metrics.instrument.internal;

import org.springframework.metrics.instrument.annotation.Timed;
import org.springframework.metrics.instrument.annotation.TimedSet;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static java.util.Arrays.stream;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.empty;
import static java.util.stream.Stream.of;

/**
* @author Jon Schneider
*/
public class TimedUtils {
public static Stream<Timed> findTimed(Class<?> clazz) {
Timed t = clazz.getAnnotation(Timed.class);
if(t != null)
return of(t);

TimedSet ts = clazz.getAnnotation(TimedSet.class);
if(ts != null)
return stream(ts.value()).sorted(comparing(Timed::value));

return empty();
}

public static Stream<Timed> findTimed(Method m) {
Timed t = m.getAnnotation(Timed.class);
if(t != null)
return of(t);

TimedSet ts = m.getAnnotation(TimedSet.class);
if(ts != null)
return stream(ts.value()).sorted(comparing(Timed::value));

return empty();
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
package org.springframework.metrics.instrument.scheduling;

import com.google.common.base.Functions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.metrics.instrument.LongTaskTimer;
import org.springframework.metrics.instrument.MeterRegistry;
import org.springframework.metrics.instrument.Tags;
import org.springframework.metrics.instrument.Timer;
import org.springframework.metrics.instrument.annotation.Timed;
import org.springframework.metrics.instrument.internal.TimedUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.stream.Collectors;

@Aspect
public class MetricsSchedulingAspect {
protected final Log logger = LogFactory.getLog(MetricsSchedulingAspect.class);
private static final Log logger = LogFactory.getLog(MetricsSchedulingAspect.class);

private final MeterRegistry registry;

Expand All @@ -24,7 +31,6 @@ public MetricsSchedulingAspect(MeterRegistry registry) {
@Around("execution (@org.springframework.scheduling.annotation.Scheduled * *.*(..))")
public Object timeScheduledOperation(ProceedingJoinPoint pjp) throws Throwable {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();

String signature = pjp.getSignature().toShortString();

if (method.getDeclaringClass().isInterface()) {
Expand All @@ -37,18 +43,31 @@ public Object timeScheduledOperation(ProceedingJoinPoint pjp) throws Throwable {
}
}

Timed timed = method.getAnnotation(Timed.class);
Map<Boolean, Timed> timedAnnots = TimedUtils.findTimed(method)
.filter(t -> !t.value().isEmpty())
.collect(Collectors.toMap(Timed::longTask, Functions.identity()));

if (timed == null) {
logger.debug("Skipping metrics timing on " + signature + ": no @Timed annotation is present on the method");
return pjp.proceed();
}
Timed shortTaskTimerAnnot = timedAnnots.get(false);
Timer shortTaskTimer = null;
if(shortTaskTimerAnnot != null)
shortTaskTimer = registry.timer(shortTaskTimerAnnot.value(), Tags.tagList(shortTaskTimerAnnot.extraTags()));

Timed longTaskTimerAnnot = timedAnnots.get(true);
LongTaskTimer longTaskTimer = null;
if(longTaskTimerAnnot != null)
longTaskTimer = registry.longTaskTimer(longTaskTimerAnnot.value(), Tags.tagList(longTaskTimerAnnot.extraTags()));

if (timed.value().isEmpty()) {
logger.warn("Unable to perform metrics timing on " + signature + ": @Timed annotation must have a value used to name the metric");
return pjp.proceed();
if(shortTaskTimer != null && longTaskTimer != null) {
final Timer finalTimer = shortTaskTimer;
return longTaskTimer.recordThrowable(() -> finalTimer.recordThrowable(pjp::proceed));
}
else if(shortTaskTimer != null) {
return shortTaskTimer.recordThrowable(pjp::proceed);
}
else if(longTaskTimer != null) {
return longTaskTimer.recordThrowable(pjp::proceed);
}

return registry.timer(timed.value()).recordThrowable(pjp::proceed);
return pjp.proceed();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,12 @@ private Meter storeId(MeterId id, Meter m) {
idMap.put(m, id);
return m;
}

/**
* Clear the registry of all registered meters and their values.
*/
public void clear() {
meterMap.clear();
idMap.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,28 @@ public Stream<Tag> clientHttpRequestTags(HttpRequest request,
Tag.of("clientName", host));
}

@Override
public Stream<Tag> httpLongRequestTags(HttpServletRequest request, Object handler) {
Stream.Builder<Tag> tags = Stream.builder();

tags.add(Tag.of("method", request.getMethod()));

String uri = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
if (uri == null) {
uri = request.getPathInfo();
}
if (!StringUtils.hasText(uri)) {
uri = "/";
}
uri = sanitizeUrlTemplate(uri.substring(1));
tags.add(Tag.of("uri", uri.isEmpty() ? "root" : uri));

return tags.build();
}

@Override
public Stream<Tag> httpRequestTags(HttpServletRequest request,
HttpServletResponse response, Object handler, String caller) {
HttpServletResponse response, Object handler) {
Stream.Builder<Tag> tags = Stream.builder();

tags.add(Tag.of("method", request.getMethod()));
Expand All @@ -90,15 +109,11 @@ public Stream<Tag> httpRequestTags(HttpServletRequest request,
tags.add(Tag.of("exception", exception.getClass().getSimpleName()));
}

if (caller != null) {
tags.add(Tag.of("caller", caller));
}

return tags.build();
}

@Override
public Stream<Tag> httpRequestTags(ServerWebExchange exchange, Throwable exception, String caller) {
public Stream<Tag> httpRequestTags(ServerWebExchange exchange, Throwable exception) {
Stream.Builder<Tag> tags = Stream.builder();

ServerHttpRequest request = exchange.getRequest();
Expand All @@ -120,15 +135,11 @@ public Stream<Tag> httpRequestTags(ServerWebExchange exchange, Throwable excepti
tags.add(Tag.of("exception", exception.getClass().getSimpleName()));
}

if (caller != null) {
tags.add(Tag.of("caller", caller));
}

return tags.build();
}

@Override
public Stream<Tag> httpRequestTags(ServerRequest request, ServerResponse response, String uri, Throwable exception, String caller) {
public Stream<Tag> httpRequestTags(ServerRequest request, ServerResponse response, String uri, Throwable exception) {
Stream.Builder<Tag> tags = Stream.builder();

tags.add(Tag.of("method", request.method().toString()));
Expand All @@ -144,10 +155,6 @@ public Stream<Tag> httpRequestTags(ServerRequest request, ServerResponse respons
tags.add(Tag.of("exception", exception.getClass().getSimpleName()));
}

if (caller != null) {
tags.add(Tag.of("caller", caller));
}

return tags.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public HandlerFilterFunction<ServerResponse, ServerResponse> timer(String name,
return next
.handle(request)
.doOnSuccess(response -> {
Stream<Tag> allTags = Stream.concat(tags, tagProvider.httpRequestTags(request, response, "", null, null));
Stream<Tag> allTags = Stream.concat(tags, tagProvider.httpRequestTags(request, response, "", null));
registry.timer(name, allTags).record(System.nanoTime() - start, TimeUnit.NANOSECONDS);
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
*/
public interface WebMetricsTagProvider {
/**
* Supplies default tags to timers monitoring RestTemplate requests.
*
* @param request RestTemplate client HTTP request
* @param response may be null in the event of a client error
* @return a map of tags added to every client HTTP request metric
Expand All @@ -46,39 +48,48 @@ default Stream<Tag> clientHttpRequestTags(HttpRequest request,
}

/**
* Supplies default tags to the Web MVC server programming model
* Supplies default tags to long task timers monitoring the Web MVC server programming model.
*
* @param request HTTP request
* @param handler the request method that is responsible for handling the request
* @return a map of tags added to every Spring MVC HTTP request metric
*/
default Stream<Tag> httpLongRequestTags(HttpServletRequest request, Object handler) {
return Stream.empty();
}

/**
* Supplies default tags to the Web MVC server programming model.
*
* @param request HTTP request
* @param response HTTP response
* @param handler the request method that is responsible for handling the request
* @return a map of tags added to every Spring MVC HTTP request metric
*/
default Stream<Tag> httpRequestTags(HttpServletRequest request,
HttpServletResponse response, Object handler, String caller) {
HttpServletResponse response, Object handler) {
return Stream.empty();
}

/**
* Supplies default tags to the WebFlux annotation-based server programming model
* Supplies default tags to the WebFlux annotation-based server programming model.
* @param exchange
* @param exception
* @param caller
* @return
*/
default Stream<Tag> httpRequestTags(ServerWebExchange exchange, Throwable exception, String caller) {
default Stream<Tag> httpRequestTags(ServerWebExchange exchange, Throwable exception) {
return Stream.empty();
}

/**
* Supplies default tags to the WebFlux functional server programming model
* Supplies default tags to the WebFlux functional server programming model.
* @param request
* @param response
* @param uri
* @param exception
* @param caller
* @return
*/
default Stream<Tag> httpRequestTags(ServerRequest request, ServerResponse response, String uri, Throwable exception, String caller) {
default Stream<Tag> httpRequestTags(ServerRequest request, ServerResponse response, String uri, Throwable exception) {
return Stream.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
Mono<Void> filtered = chain.filter(exchange);
return filtered
.doOnSuccess(done ->
registry.timer(metricName, tagProvider.httpRequestTags(exchange, null, null))
registry.timer(metricName, tagProvider.httpRequestTags(exchange, null))
.record(System.nanoTime() - start, TimeUnit.NANOSECONDS)
)
.doOnError(t ->
registry.timer(metricName, tagProvider.httpRequestTags(exchange, t, null))
registry.timer(metricName, tagProvider.httpRequestTags(exchange, t))
.record(System.nanoTime() - start, TimeUnit.NANOSECONDS)
);
}
Expand Down
Loading

0 comments on commit be99318

Please sign in to comment.