/*
*
* Copyright 2017 Oleksandr Goldobin
*
* 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.github.resilience4j.prometheus;
import java.util.List;
import java.util.function.Supplier;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.prometheus.client.Collector;
import io.prometheus.client.GaugeMetricFamily;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import io.vavr.collection.Array;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
/**
* An adapter from builtin {@link CircuitBreaker.Metrics} to prometheus
* {@link io.prometheus.client.CollectorRegistry}.
*
* Also exports {@link CircuitBreaker} state as a labeled metric
*/
public class CircuitBreakerExports extends Collector {
private static final String DEFAULT_NAME = "resilience4j_circuitbreaker";
private static Array<Tuple2<CircuitBreaker.State, String>> STATE_NAME_MAP =
Array.ofAll(asList(CircuitBreaker.State.values()))
.map(state -> Tuple.of(state, state.name().toLowerCase()));
private final String prefix;
private final Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier;
/**
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
* {@link Supplier} of circuit breakers
*
* @param prefix the prefix of metrics names
* @param circuitBreakersSupplier the supplier of circuit breakers
*/
public static CircuitBreakerExports ofSupplier(String prefix, Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier) {
return new CircuitBreakerExports(prefix, circuitBreakersSupplier);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* {@link Supplier} of circuit breakers
*
* @param circuitBreakersSupplier the supplier of circuit breakers
*/
public static CircuitBreakerExports ofSupplier(Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier) {
return new CircuitBreakerExports(DEFAULT_NAME, circuitBreakersSupplier);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* {@link CircuitBreakerRegistry} as a source of circuit breakers.
* @param circuitBreakerRegistry the registry of circuit breakers
*/
public static CircuitBreakerExports ofCircuitBreakerRegistry(CircuitBreakerRegistry circuitBreakerRegistry) {
return new CircuitBreakerExports(circuitBreakerRegistry.getAllCircuitBreakers());
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* a circuit breaker as a source.
*
* @param circuitBreaker the circuit breaker
*/
public static CircuitBreakerExports ofCircuitBreaker(CircuitBreaker circuitBreaker) {
requireNonNull(circuitBreaker);
return new CircuitBreakerExports(Array.of(circuitBreaker));
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* {@link Iterable} of circuit breakers.
*
* @param circuitBreakers the circuit breakers
*/
public static CircuitBreakerExports ofIterable(Iterable<CircuitBreaker> circuitBreakers) {
requireNonNull(circuitBreakers);
return new CircuitBreakerExports(circuitBreakers);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
* {@link CircuitBreakerRegistry} as a source of circuit breakers.
*
* @param prefix the prefix of metrics names
* @param circuitBreakerRegistry the registry of circuit breakers
*/
public static CircuitBreakerExports ofCircuitBreakerRegistry(String prefix, CircuitBreakerRegistry circuitBreakerRegistry) {
requireNonNull(prefix);
requireNonNull(circuitBreakerRegistry);
return new CircuitBreakerExports(prefix, circuitBreakerRegistry);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
* {@link Iterable} of circuit breakers.
*
* @param prefix the prefix of metrics names
* @param circuitBreakers the circuit breakers
*/
public static CircuitBreakerExports ofIterable(String prefix, Iterable<CircuitBreaker> circuitBreakers) {
requireNonNull(prefix);
requireNonNull(circuitBreakers);
return new CircuitBreakerExports(prefix, circuitBreakers);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* a circuit breaker as a source.
*
* @param prefix the prefix of metrics names
* @param circuitBreaker the circuit breaker
*/
public static CircuitBreakerExports ofCircuitBreaker(String prefix, CircuitBreaker circuitBreaker) {
requireNonNull(prefix);
requireNonNull(circuitBreaker);
return new CircuitBreakerExports(prefix, Array.of(circuitBreaker));
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* {@link CircuitBreakerRegistry} as a source of circuit breakers.
* @param circuitBreakerRegistry the registry of circuit breakers
*/
private CircuitBreakerExports(CircuitBreakerRegistry circuitBreakerRegistry) {
this(circuitBreakerRegistry::getAllCircuitBreakers);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* {@link Iterable} of circuit breakers.
*
* @param circuitBreakers the circuit breakers
*/
private CircuitBreakerExports(Iterable<CircuitBreaker> circuitBreakers) {
this(() -> circuitBreakers);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
* {@link Supplier} of circuit breakers
*
* @param circuitBreakersSupplier the supplier of circuit breakers
*/
private CircuitBreakerExports(Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier) {
this(DEFAULT_NAME, circuitBreakersSupplier);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
* {@link CircuitBreakerRegistry} as a source of circuit breakers.
*
* @param prefix the prefix of metrics names
* @param circuitBreakerRegistry the registry of circuit breakers
*/
private CircuitBreakerExports(String prefix, CircuitBreakerRegistry circuitBreakerRegistry) {
this(prefix, circuitBreakerRegistry::getAllCircuitBreakers);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
* {@link Iterable} of circuit breakers.
*
* @param prefix the prefix of metrics names
* @param circuitBreakers the circuit breakers
*/
private CircuitBreakerExports(String prefix, Iterable<CircuitBreaker> circuitBreakers) {
this(prefix, () -> circuitBreakers);
}
/**
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
* {@link Supplier} of circuit breakers
*
* @param prefix the prefix of metrics names
* @param circuitBreakersSupplier the supplier of circuit breakers
*/
private CircuitBreakerExports(String prefix, Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier) {
this.prefix = prefix;
this.circuitBreakersSupplier = circuitBreakersSupplier;
}
/**
* {@inheritDoc}
*/
@Override
public List<MetricFamilySamples> collect() {
final GaugeMetricFamily states = new GaugeMetricFamily(
prefix + "_states",
"Circuit Breaker States",
asList("name","state"));
final GaugeMetricFamily calls = new GaugeMetricFamily(
prefix + "_calls",
"Circuit Breaker Call Stats",
asList("name", "call_result"));
for (CircuitBreaker circuitBreaker : circuitBreakersSupplier.get()) {
STATE_NAME_MAP.forEach(e -> {
final CircuitBreaker.State state = e._1;
final String name = e._2;
final double value = state == circuitBreaker.getState() ? 1.0 : 0.0;
states.addMetric(asList(circuitBreaker.getName(), name), value);
});
final CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
calls.addMetric(
asList(circuitBreaker.getName(), "successful"),
metrics.getNumberOfSuccessfulCalls());
calls.addMetric(
asList(circuitBreaker.getName(), "failed"),
metrics.getNumberOfFailedCalls());
calls.addMetric(
asList(circuitBreaker.getName(), "not_permitted"),
metrics.getNumberOfNotPermittedCalls());
calls.addMetric(
asList(circuitBreaker.getName(), "buffered"),
metrics.getNumberOfBufferedCalls());
calls.addMetric(
asList(circuitBreaker.getName(), "buffered_max"),
metrics.getMaxNumberOfBufferedCalls());
}
return asList(calls, states);
}
}