io.helidon.microprofile.faulttolerance.FaultToleranceMetrics Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helidon-microprofile-fault-tolerance Show documentation
Show all versions of helidon-microprofile-fault-tolerance Show documentation
Microprofile fault tolerance implementation
The newest version!
/*
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.microprofile.faulttolerance;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import io.helidon.common.LazyValue;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.enterprise.util.AnnotationLiteral;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.Gauge;
import org.eclipse.microprofile.metrics.Histogram;
import org.eclipse.microprofile.metrics.Metadata;
import org.eclipse.microprofile.metrics.Metric;
import org.eclipse.microprofile.metrics.MetricID;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.annotation.RegistryType;
import static io.helidon.microprofile.faulttolerance.FaultToleranceExtension.getRealClass;
/**
* Utility class to register and fetch FT metrics.
*/
class FaultToleranceMetrics {
static final String METRIC_NAME_TEMPLATE = "ft.%s.%s.%s";
private static final ReentrantLock LOCK = new ReentrantLock();
private static LazyValue metricRegistry = metricRegistryLazyValue();
private FaultToleranceMetrics() {
}
static boolean enabled() {
return getMetricRegistry() != null;
}
static MetricRegistry getMetricRegistry() {
return metricRegistry.get();
}
static void close() {
// Facilitates reuse in successive tests in the same JVM.
metricRegistry = metricRegistryLazyValue();
}
private static LazyValue metricRegistryLazyValue() {
return LazyValue.create(
() -> CDI.current().select(MetricRegistry.class, new BaseRegistryTypeLiteral()).get());
}
/**
* Annotation literal to inject base registry.
*/
static class BaseRegistryTypeLiteral extends AnnotationLiteral implements RegistryType {
@Override
public MetricRegistry.Type type() {
return MetricRegistry.Type.BASE;
}
}
/**
* Base class for Fault Tolerance metrics. Shares common logic for registration
* and lookup of metrics.
*/
abstract static class FaultToleranceMetric {
abstract String name();
abstract String description();
abstract Class extends Metric> metricType();
abstract String unit();
protected Counter getCounter(Tag... tags) {
MetricID metricID = new MetricID(name(), tags);
return (Counter) getMetricRegistry().getMetrics().get(metricID);
}
protected Counter registerCounter(Tag... tags) {
Counter counter = getCounter(tags);
if (counter == null) {
Metadata metadata = Metadata.builder()
.withName(name())
.withDescription(description())
.withUnit(unit())
.build();
try {
counter = getMetricRegistry().counter(metadata, tags);
} catch (IllegalArgumentException e) {
// Looks like we lost registration race
counter = getCounter(tags);
Objects.requireNonNull(counter);
}
}
return counter;
}
protected Histogram getHistogram(Tag... tags) {
MetricID metricID = new MetricID(name(), tags);
return (Histogram) getMetricRegistry().getMetrics().get(metricID);
}
protected Histogram registerHistogram(Tag... tags) {
Histogram histogram = getHistogram(tags);
if (histogram == null) {
Metadata metadata = Metadata.builder()
.withName(name())
.withDescription(description())
.withUnit(unit())
.build();
try {
histogram = getMetricRegistry().histogram(metadata, tags);
} catch (IllegalArgumentException e) {
// Looks like we lost the registration race
histogram = getHistogram(tags);
Objects.requireNonNull(histogram);
}
}
return histogram;
}
@SuppressWarnings("unchecked")
protected Gauge getGauge(Tag... tags) {
MetricID metricID = new MetricID(name(), tags);
return (Gauge) getMetricRegistry().getMetrics().get(metricID);
}
@SuppressWarnings("unchecked")
static T getMetric(Method method, String name) {
MetricID metricID = new MetricID(String.format(METRIC_NAME_TEMPLATE,
method.getDeclaringClass().getName(),
method.getName(), name));
return (T) getMetricRegistry().getMetrics().get(metricID);
}
static Counter getCounter(Method method, String name) {
return getMetric(method, name);
}
static Histogram getHistogram(Method method, String name) {
return getMetric(method, name);
}
@SuppressWarnings("unchecked")
static Gauge getGauge(Method method, String name) {
return getMetric(method, name);
}
static long getCounter(Object bean, String methodName, String name,
Class>... params) throws Exception {
Method method = findMethod(getRealClass(bean), methodName, params);
return getCounter(method, name).getCount();
}
static Histogram getHistogram(Object bean, String methodName, String name,
Class>... params) throws Exception {
Method method = findMethod(getRealClass(bean), methodName, params);
return getHistogram(method, name);
}
static Gauge getGauge(Object bean, String methodName, String name,
Class>... params) throws Exception {
Method method = findMethod(getRealClass(bean), methodName, params);
return getGauge(method, name);
}
/**
* Attempts to find a method even if not accessible.
*
* @param beanClass bean class.
* @param methodName name of method.
* @param params param types.
* @return method found.
* @throws NoSuchMethodException if not found.
*/
private static Method findMethod(Class> beanClass, String methodName,
Class>... params) throws NoSuchMethodException {
try {
Method method = beanClass.getDeclaredMethod(methodName, params);
method.setAccessible(true);
return method;
} catch (Exception e) {
return beanClass.getMethod(methodName, params);
}
}
@SuppressWarnings("unchecked")
protected Gauge registerGauge(Gauge newGauge, Tag... tags) {
Gauge gauge = getGauge(tags);
if (gauge == null) {
Metadata metadata = Metadata.builder()
.withName(name())
.withDescription(description())
.withUnit(unit())
.build();
try {
gauge = getMetricRegistry().gauge(metadata, newGauge::getValue, tags);
} catch (IllegalArgumentException e) {
// Looks like we lost the registration race
gauge = getGauge(tags);
Objects.requireNonNull(gauge);
}
}
return gauge;
}
}
// -- Invocations ---------------------------------------------------------
enum InvocationResult implements Supplier {
VALUE_RETURNED("valueReturned"),
EXCEPTION_THROWN("exceptionThrown");
private final Tag metricTag;
InvocationResult(String value) {
metricTag = new Tag("result", value);
}
@Override
public Tag get() {
return metricTag;
}
}
enum InvocationFallback implements Supplier {
APPLIED("applied"),
NOT_APPLIED("notApplied"),
NOT_DEFINED("notDefined");
private final Tag metricTag;
InvocationFallback(String value) {
metricTag = new Tag("fallback", value);
}
@Override
public Tag get() {
return metricTag;
}
}
/**
* Class for "ft.invocations.total" counters.
*/
static class InvocationsTotal extends FaultToleranceMetric {
static final InvocationsTotal INSTANCE = new InvocationsTotal();
private InvocationsTotal() {
}
@Override
String name() {
return "ft.invocations.total";
}
@Override
String description() {
return "The number of times the method was called";
}
@Override
Class extends Metric> metricType() {
return Counter.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Counter get(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
static Counter register(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
}
// -- Retries -------------------------------------------------------------
enum RetryResult implements Supplier {
VALUE_RETURNED("valueReturned"),
EXCEPTION_NOT_RETRYABLE("exceptionNotRetryable"),
MAX_RETRIES_REACHED("maxRetriesReached"),
MAX_DURATION_REACHED("maxDurationReached");
private final Tag metricTag;
RetryResult(String value) {
metricTag = new Tag("retryResult", value);
}
@Override
public Tag get() {
return metricTag;
}
}
enum RetryRetried implements Supplier {
TRUE("true"),
FALSE("false");
private final Tag metricTag;
RetryRetried(String value) {
metricTag = new Tag("retried", value);
}
@Override
public Tag get() {
return metricTag;
}
}
/**
* Class for "ft.retry.calls.total" counters.
*/
static class RetryCallsTotal extends FaultToleranceMetric {
static final RetryCallsTotal INSTANCE = new RetryCallsTotal();
private RetryCallsTotal() {
}
@Override
String name() {
return "ft.retry.calls.total";
}
@Override
String description() {
return "The number of times the retry logic was run. This will always be once per method call.";
}
@Override
Class extends Metric> metricType() {
return Counter.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Counter get(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
static Counter register(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
}
/**
* Class for "ft.retry.retries.total" counters.
*/
static class RetryRetriesTotal extends FaultToleranceMetric {
static final RetryRetriesTotal INSTANCE = new RetryRetriesTotal();
private RetryRetriesTotal() {
}
@Override
String name() {
return "ft.retry.retries.total";
}
@Override
String description() {
return "The number of times the method was retried";
}
@Override
Class extends Metric> metricType() {
return Counter.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Counter get(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
static Counter register(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
}
// -- Timeouts ------------------------------------------------------------
enum TimeoutTimedOut implements Supplier {
TRUE("true"),
FALSE("false");
private final Tag metricTag;
TimeoutTimedOut(String value) {
this.metricTag = new Tag("timedOut", value);
}
public Tag get() {
return metricTag;
}
}
/**
* Class for "ft.timeout.calls.total" counters.
*/
static class TimeoutCallsTotal extends FaultToleranceMetric {
static final TimeoutCallsTotal INSTANCE = new TimeoutCallsTotal();
private TimeoutCallsTotal() {
}
@Override
String name() {
return "ft.timeout.calls.total";
}
@Override
String description() {
return "The number of times the timeout logic was run. This will usually be once "
+ "per method call, but may be zero times if the circuit breaker prevents "
+ "execution or more than once if the method is retried.";
}
@Override
Class extends Metric> metricType() {
return Counter.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Counter get(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
static Counter register(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
}
/**
* Class for "ft.timeout.executionDuration" histograms.
*/
static class TimeoutExecutionDuration extends FaultToleranceMetric {
static final TimeoutExecutionDuration INSTANCE = new TimeoutExecutionDuration();
private TimeoutExecutionDuration() {
}
@Override
String name() {
return "ft.timeout.executionDuration";
}
@Override
String description() {
return "Histogram of execution times for the method";
}
@Override
Class extends Metric> metricType() {
return Histogram.class;
}
@Override
String unit() {
return MetricUnits.NANOSECONDS;
}
static Histogram get(Tag... tags) {
return INSTANCE.registerHistogram(tags);
}
static Histogram register(Tag... tags) {
return INSTANCE.registerHistogram(tags);
}
}
// --- CircuitBreakers ----------------------------------------------------
enum CircuitBreakerResult implements Supplier {
SUCCESS("success"),
FAILURE("failure"),
CIRCUIT_BREAKER_OPEN("circuitBreakerOpen");
private final Tag metricTag;
CircuitBreakerResult(String value) {
metricTag = new Tag("circuitBreakerResult", value);
}
@Override
public Tag get() {
return metricTag;
}
}
enum CircuitBreakerState implements Supplier {
OPEN("open"),
CLOSED("closed"),
HALF_OPEN("halfOpen");
private final Tag metricTag;
CircuitBreakerState(String value) {
metricTag = new Tag("state", value);
}
@Override
public Tag get() {
return metricTag;
}
}
/**
* Class for "ft.circuitbreaker.calls.total" counters.
*/
@SuppressWarnings("unchecked")
static Gauge registerGauge(Method method, String metricName, String description, Gauge gauge) {
LOCK.lock();
try {
MetricID metricID = new MetricID(String.format(METRIC_NAME_TEMPLATE,
method.getDeclaringClass().getName(),
method.getName(),
metricName));
Gauge existing = getMetricRegistry().getGauges().get(metricID);
if (existing == null) {
getMetricRegistry().gauge(Metadata.builder()
.withName(metricID.getName())
.withDescription(description)
.withUnit(MetricUnits.NANOSECONDS).build(),
gauge::getValue);
}
return existing;
} finally {
LOCK.unlock();
}
}
static class CircuitBreakerCallsTotal extends FaultToleranceMetric {
static final CircuitBreakerCallsTotal INSTANCE = new CircuitBreakerCallsTotal();
private CircuitBreakerCallsTotal() {
}
@Override
String name() {
return "ft.circuitbreaker.calls.total";
}
@Override
String description() {
return "The number of times the circuit breaker logic was run. This will usually be once "
+ "per method call, but may be more than once if the method call is retried.";
}
@Override
Class extends Metric> metricType() {
return Counter.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Counter get(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
static Counter register(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
}
/**
* Class for "ft.circuitbreaker.state.total" gauges.
*/
static class CircuitBreakerStateTotal extends FaultToleranceMetric {
static final CircuitBreakerStateTotal INSTANCE = new CircuitBreakerStateTotal();
private CircuitBreakerStateTotal() {
}
@Override
String name() {
return "ft.circuitbreaker.state.total";
}
@Override
String description() {
return "Amount of time the circuit breaker has spent in each state";
}
@Override
Class extends Metric> metricType() {
return Gauge.class;
}
@Override
String unit() {
return MetricUnits.NANOSECONDS;
}
static Gauge get(Tag... tags) {
return INSTANCE.getGauge(tags);
}
static Gauge register(Gauge gauge, Tag... tags) {
return INSTANCE.registerGauge(gauge, tags);
}
}
/**
* Class for "ft.circuitbreaker.opened.total" counters.
*/
static class CircuitBreakerOpenedTotal extends FaultToleranceMetric {
static final CircuitBreakerOpenedTotal INSTANCE = new CircuitBreakerOpenedTotal();
private CircuitBreakerOpenedTotal() {
}
@Override
String name() {
return "ft.circuitbreaker.opened.total";
}
@Override
String description() {
return "Number of times the circuit breaker has moved from closed state to open state";
}
@Override
Class extends Metric> metricType() {
return Counter.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Counter get(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
static Counter register(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
}
// --- Bulkheads ----------------------------------------------------------
enum BulkheadResult implements Supplier {
ACCEPTED("accepted"),
REJECTED("rejected");
private final Tag metricTag;
BulkheadResult(String value) {
metricTag = new Tag("bulkheadResult", value);
}
@Override
public Tag get() {
return metricTag;
}
}
/**
* Class for "ft.bulkhead.calls.total" counters.
*/
static class BulkheadCallsTotal extends FaultToleranceMetric {
static final BulkheadCallsTotal INSTANCE = new BulkheadCallsTotal();
private BulkheadCallsTotal() {
}
@Override
String name() {
return "ft.bulkhead.calls.total";
}
@Override
String description() {
return "The number of times the bulkhead logic was run. This will usually be once per "
+ "method call, but may be zero times if the circuit breaker prevented execution "
+ "or more than once if the method call is retried.";
}
@Override
Class extends Metric> metricType() {
return Counter.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Counter get(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
static Counter register(Tag... tags) {
return INSTANCE.registerCounter(tags);
}
}
/**
* Class for "ft.bulkhead.executionsRunning" gauges.
*/
static class BulkheadExecutionsRunning extends FaultToleranceMetric {
static final BulkheadExecutionsRunning INSTANCE = new BulkheadExecutionsRunning();
private BulkheadExecutionsRunning() {
}
@Override
String name() {
return "ft.bulkhead.executionsRunning";
}
@Override
String description() {
return "Number of currently running executions";
}
@Override
Class extends Metric> metricType() {
return Gauge.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Gauge get(Tag... tags) {
return INSTANCE.getGauge(tags);
}
static Gauge register(Gauge gauge, Tag... tags) {
return INSTANCE.registerGauge(gauge, tags);
}
}
/**
* Class for "ft.bulkhead.executionsWaiting" gauges.
*/
static class BulkheadExecutionsWaiting extends FaultToleranceMetric {
static final BulkheadExecutionsWaiting INSTANCE = new BulkheadExecutionsWaiting();
private BulkheadExecutionsWaiting() {
}
@Override
String name() {
return "ft.bulkhead.executionsWaiting";
}
@Override
String description() {
return "Number of executions currently waiting in the queue";
}
@Override
Class extends Metric> metricType() {
return Gauge.class;
}
@Override
String unit() {
return MetricUnits.NONE;
}
static Gauge get(Tag... tags) {
return INSTANCE.getGauge(tags);
}
static Gauge register(Gauge gauge, Tag... tags) {
return INSTANCE.registerGauge(gauge, tags);
}
}
/**
* Class for "ft.bulkhead.runningDuration" histograms.
*/
static class BulkheadRunningDuration extends FaultToleranceMetric {
static final BulkheadRunningDuration INSTANCE = new BulkheadRunningDuration();
private BulkheadRunningDuration() {
}
@Override
String name() {
return "ft.bulkhead.runningDuration";
}
@Override
String description() {
return "Histogram of the time that method executions spent running";
}
@Override
Class extends Metric> metricType() {
return Histogram.class;
}
@Override
String unit() {
return MetricUnits.NANOSECONDS;
}
static Histogram get(Tag... tags) {
return INSTANCE.registerHistogram(tags);
}
static Histogram register(Tag... tags) {
return INSTANCE.registerHistogram(tags);
}
}
/**
* Class for "ft.bulkhead.waitingDuration" histograms.
*/
static class BulkheadWaitingDuration extends FaultToleranceMetric {
static final BulkheadWaitingDuration INSTANCE = new BulkheadWaitingDuration();
private BulkheadWaitingDuration() {
}
@Override
String name() {
return "ft.bulkhead.waitingDuration";
}
@Override
String description() {
return "Histogram of the time that method executions spent waiting in the queue";
}
@Override
Class extends Metric> metricType() {
return Histogram.class;
}
@Override
String unit() {
return MetricUnits.NANOSECONDS;
}
static Histogram get(Tag... tags) {
return INSTANCE.registerHistogram(tags);
}
static Histogram register(Tag... tags) {
return INSTANCE.registerHistogram(tags);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy