Skip to content

Instantly share code, notes, and snippets.

@wpik
Created February 16, 2021 12:18
Show Gist options
  • Save wpik/871ba8903456eddbcc387c941206b930 to your computer and use it in GitHub Desktop.
Save wpik/871ba8903456eddbcc387c941206b930 to your computer and use it in GitHub Desktop.
External system metrics with micrometer
package p;
import feign.FeignException;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Supplier;
import static p.SystemResponseMetrics.Outcome.FAILURE;
import static p.SystemResponseMetrics.Outcome.SUCCESS;
public class SystemResponseMetrics<E extends Enum> {
private static final String RESPONSE_TIMER_SUFFIX = ".response.time";
private static final String OPERATION = "operation";
private static final String OUTCOME = "outcome";
private static final String HTTP_STATUS = "http.status";
private final MeterRegistry meterRegistry;
private final String system;
private final String timerName;
private final double[] percentiles;
public SystemResponseMetrics(MeterRegistry meterRegistry, String system, Class<E> operations) {
this(meterRegistry, system, operations, 0.5, 0.9, 0.95);
}
public SystemResponseMetrics(MeterRegistry meterRegistry, String system,
Class<E> operations, double... percentiles) {
Objects.requireNonNull(meterRegistry);
Assert.hasLength(system, "System cannot be empty");
Objects.requireNonNull(operations);
this.meterRegistry = meterRegistry;
this.system = system;
this.timerName = system.toLowerCase() + RESPONSE_TIMER_SUFFIX;
this.percentiles = percentiles;
Arrays.stream(operations.getEnumConstants())
.map(Enum::name)
.map(String::toLowerCase)
.forEach(this::registerTimer);
}
private void registerTimer(String operation) {
Timer.builder(timerName)
.description(system + " response time")
.publishPercentiles(percentiles)
.tag(OPERATION, operation)
.tag(OUTCOME, SUCCESS.getLowerCaseValue())
.tag(HTTP_STATUS, "200")
.register(meterRegistry);
}
public Timer.Sample startTimer() {
return Timer.start(meterRegistry);
}
public void stopResponseTimer(Timer.Sample sample, E operation, Outcome outcome, int httpStatus) {
sample.stop(meterRegistry.timer(timerName,
OPERATION, operation.name().toLowerCase(),
OUTCOME, outcome.getLowerCaseValue(),
HTTP_STATUS, Integer.toString(httpStatus)));
}
public void timeExecution(E operationId, Runnable functionToTime) {
timeExecution(operationId, 200, 999, functionToTime);
}
public <T> T timeExecution(E operationId, Supplier<T> functionToTime) {
return timeExecution(operationId, 200, 999, functionToTime);
}
public void timeExecution(E operationId, int successStatus, int unknownStatus, Runnable functionToTime) {
timeExecution(operationId, successStatus, unknownStatus, () -> {
functionToTime.run();
return null;
});
}
public <T> T timeExecution(E operationId, int successStatus, int unknownStatus, Supplier<T> functionToTime) {
Timer.Sample sample = startTimer();
try {
T result = functionToTime.get();
stopResponseTimer(sample, operationId, SUCCESS, successStatus);
return result;
} catch (FeignException e) {
stopResponseTimer(sample, operationId, FAILURE, e.status());
throw e;
} catch (Throwable e) {
stopResponseTimer(sample, operationId, FAILURE, unknownStatus);
throw e;
}
}
public enum Outcome {
SUCCESS,
FAILURE;
private String lowerCaseValue = name().toLowerCase();
String getLowerCaseValue() {
return lowerCaseValue;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment