Skip to content

Commit 56501b3

Browse files
goldobinRobWin
authored andcommitted
Issue ReactiveX#71: Implementation of Prometheus exporters for CircuitBreaker and RateLimiter (ReactiveX#81)
1 parent e84a012 commit 56501b3

File tree

11 files changed

+710
-1
lines changed

11 files changed

+710
-1
lines changed

libraries.gradle

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ ext {
1515
vertxVersion = '3.4.1'
1616
springBootVersion = '1.4.3.RELEASE'
1717
retrofitVersion = '2.1.0'
18+
prometheusSimpleClientVersion = '0.0.21'
1819

1920
libraries = [
2021
// compile
@@ -52,6 +53,9 @@ ext {
5253
metrics: "io.dropwizard.metrics:metrics-core:${metricsVersion}",
5354

5455
// CircuitBreaker documentation
55-
metrics_healthcheck: "io.dropwizard.metrics:metrics-healthchecks:${metricsVersion}"
56+
metrics_healthcheck: "io.dropwizard.metrics:metrics-healthchecks:${metricsVersion}",
57+
58+
// Prometheus addon
59+
prometheus_simpleclient: "io.prometheus:simpleclient_common:${prometheusSimpleClientVersion}"
5660
]
5761
}

resilience4j-prometheus/README.adoc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
= resilience4j-prometheus
2+
3+
Direct integration of circuit breaker and rate limiter metrics with
4+
https://github.com/prometheus/client_java[Prometheus simple client]
5+
6+
For the circuit breaker library exports 2 metrics:
7+
8+
1. By state with default metric name `circuit_breaker_states` and label `state`:
9+
10+
- `closed`
11+
- `open`
12+
- `half_open`
13+
14+
2. By call result with default metric name `circuit_breaker_calls` and label `call_result`:
15+
16+
- `successful`
17+
- `failed`
18+
- `not_permitted`
19+
- `buffered`
20+
- `buffered_max`
21+
22+
For the rate limiter following metric with default name `rate_limiter` and label `param` exported:
23+
24+
- `available_permissions`
25+
- `waiting_threads`
26+
27+
The names of the rate limiters and circuit breakers are exposed using label `name`.
28+
29+
== Usage
30+
31+
=== CircuitBreaker
32+
33+
[source,java]
34+
--
35+
final CircuitBreakerRegistry circuitBreakerRegistry = new InMemoryCircuitBreakerRegistry();
36+
37+
final CircuitBreaker foo = circuitBreakerRegistry.circuitBreaker("foo");
38+
final CircuitBreaker boo = circuitBreakerRegistry.circuitBreaker("boo");
39+
40+
// Registering metrics in default prometeus CollectorRegistry
41+
new CircuitBreakerExports(circuitBreakerRegistry).register();
42+
--
43+
44+
=== RateLimiter
45+
46+
[source,java]
47+
--
48+
final RateLimiterRegistry rateLimiterRegistry = new InMemoryRateLimiterRegistry();
49+
50+
final RateLimiter foo = rateLimiterRegistry.rateLimiter("foo");
51+
final RateLimiter boo = rateLimiterRegistry.rateLimiter("boo");
52+
53+
// Registering metrics in default prometeus CollectorRegistry
54+
new RateLimiterExports(rateLimiterRegistry).register();
55+
--
56+
57+
For both it is possible to use just a collection of breakers and limiters instead of registry.
58+
59+
== License
60+
61+
Copyright 2017 Oleksandr Goldobin
62+
63+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
64+
License. You may obtain a copy of the License at
65+
66+
http://www.apache.org/licenses/LICENSE-2.0
67+
68+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
69+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
70+
specific language governing permissions and limitations under the License.

resilience4j-prometheus/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dependencies {
2+
compile (libraries.prometheus_simpleclient)
3+
compile project(':resilience4j-circuitbreaker')
4+
compile project(':resilience4j-ratelimiter')
5+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
*
3+
* Copyright 2017 Oleksandr Goldobin
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*
18+
*/
19+
package io.github.resilience4j.prometheus;
20+
21+
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
22+
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
23+
import io.prometheus.client.Collector;
24+
import io.prometheus.client.GaugeMetricFamily;
25+
import javaslang.Tuple;
26+
import javaslang.Tuple2;
27+
import javaslang.collection.Array;
28+
29+
import java.util.List;
30+
import java.util.function.Supplier;
31+
32+
import static java.util.Arrays.asList;
33+
import static java.util.Objects.requireNonNull;
34+
35+
/**
36+
* An adapter from builtin {@link CircuitBreaker.Metrics} to prometheus
37+
* {@link io.prometheus.client.CollectorRegistry}.
38+
*
39+
* Also exports {@link CircuitBreaker} state as a labeled metric
40+
*/
41+
public class CircuitBreakerExports extends Collector {
42+
43+
private static final String DEFAULT_NAME = "circuit_breaker";
44+
private static Array<Tuple2<CircuitBreaker.State, String>> STATE_NAME_MAP =
45+
Array.ofAll(asList(CircuitBreaker.State.values()))
46+
.map(state -> Tuple.of(state, state.name().toLowerCase()));
47+
48+
private final String prefix;
49+
private final Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier;
50+
51+
/**
52+
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
53+
* {@link CircuitBreakerRegistry} as a source of circuit breakers.
54+
55+
* @param circuitBreakerRegistry the registry of circuit breakers
56+
*/
57+
public CircuitBreakerExports(CircuitBreakerRegistry circuitBreakerRegistry) {
58+
this(circuitBreakerRegistry::getAllCircuitBreakers);
59+
}
60+
61+
/**
62+
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
63+
* {@link Iterable} of circuit breakers.
64+
*
65+
* @param circuitBreakers the circuit breakers
66+
*/
67+
public CircuitBreakerExports(Iterable<CircuitBreaker> circuitBreakers) {
68+
this(() -> circuitBreakers);
69+
}
70+
71+
72+
/**
73+
* Creates a new instance of {@link CircuitBreakerExports} with default metrics names prefix and
74+
* {@link Supplier} of circuit breakers
75+
*
76+
* @param circuitBreakersSupplier the supplier of circuit breakers
77+
*/
78+
public CircuitBreakerExports(Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier) {
79+
this(DEFAULT_NAME, circuitBreakersSupplier);
80+
}
81+
82+
/**
83+
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
84+
* {@link CircuitBreakerRegistry} as a source of circuit breakers.
85+
*
86+
* @param prefix the prefix of metrics names
87+
* @param circuitBreakerRegistry the registry of circuit breakers
88+
*/
89+
public CircuitBreakerExports(String prefix, CircuitBreakerRegistry circuitBreakerRegistry) {
90+
this(prefix, circuitBreakerRegistry::getAllCircuitBreakers);
91+
}
92+
93+
/**
94+
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
95+
* {@link Iterable} of circuit breakers.
96+
*
97+
* @param prefix the prefix of metrics names
98+
* @param circuitBreakers the circuit breakers
99+
*/
100+
public CircuitBreakerExports(String prefix, Iterable<CircuitBreaker> circuitBreakers) {
101+
this(prefix, () -> circuitBreakers);
102+
}
103+
104+
/**
105+
* Creates a new instance of {@link CircuitBreakerExports} with specified metrics names prefix and
106+
* {@link Supplier} of circuit breakers
107+
*
108+
* @param prefix the prefix of metrics names
109+
* @param circuitBreakersSupplier the supplier of circuit breakers
110+
*/
111+
public CircuitBreakerExports(String prefix, Supplier<Iterable<CircuitBreaker>> circuitBreakersSupplier) {
112+
requireNonNull(prefix);
113+
requireNonNull(circuitBreakersSupplier);
114+
115+
this.prefix = prefix;
116+
this.circuitBreakersSupplier = circuitBreakersSupplier;
117+
}
118+
119+
/**
120+
* {@inheritDoc}
121+
*/
122+
@Override
123+
public List<MetricFamilySamples> collect() {
124+
125+
final GaugeMetricFamily states = new GaugeMetricFamily(
126+
prefix + "_states",
127+
"Circuit Breaker States",
128+
asList("name","state"));
129+
130+
final GaugeMetricFamily calls = new GaugeMetricFamily(
131+
prefix + "_calls",
132+
"Circuit Breaker Call Stats",
133+
asList("name", "call_result"));
134+
135+
for (CircuitBreaker circuitBreaker : circuitBreakersSupplier.get()) {
136+
137+
STATE_NAME_MAP.forEach(e -> {
138+
final CircuitBreaker.State state = e._1;
139+
final String name = e._2;
140+
final double value = state == circuitBreaker.getState() ? 1.0 : 0.0;
141+
142+
states.addMetric(asList(circuitBreaker.getName(), name), value);
143+
});
144+
145+
final CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
146+
147+
calls.addMetric(
148+
asList(circuitBreaker.getName(), "successful"),
149+
metrics.getNumberOfSuccessfulCalls());
150+
151+
calls.addMetric(
152+
asList(circuitBreaker.getName(), "failed"),
153+
metrics.getNumberOfFailedCalls());
154+
155+
calls.addMetric(
156+
asList(circuitBreaker.getName(), "not_permitted"),
157+
metrics.getNumberOfNotPermittedCalls());
158+
159+
calls.addMetric(
160+
asList(circuitBreaker.getName(), "buffered"),
161+
metrics.getNumberOfBufferedCalls());
162+
163+
calls.addMetric(
164+
asList(circuitBreaker.getName(), "buffered_max"),
165+
metrics.getMaxNumberOfBufferedCalls());
166+
}
167+
168+
return asList(calls, states);
169+
}
170+
}

0 commit comments

Comments
 (0)