Skip to content

Commit f951996

Browse files
voievodinRobWin
authored andcommitted
Issue ReactiveX#354: Adapt prometheus metrics to the new format (ReactiveX#375)
* Introduce new prometheus metrics collector for bulkheads * Introduce new prometheus metrics collector for circuit breakers * Introduce new prometheus metrics collector for rate limiters * Avoid label list creation on every collect * Adapt/extend prometheus documentation * Improve metrics descriptions
1 parent e4cdf2c commit f951996

File tree

11 files changed

+969
-19
lines changed

11 files changed

+969
-19
lines changed

resilience4j-documentation/src/docs/asciidoc/addon_guides/prometheus.adoc

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,43 @@
44

55
Integration with https://github.com/prometheus/client_java[Prometheus simple client]
66

7-
Module provides exporters for `CircuitBreaker` and `RateLimiter` metrics.
7+
Module provides exporters for `CircuitBreaker`, `RateLimiter` and `Bulkhead` metrics.
8+
Every metric has `name` label indicating the relation to certain object, object category
9+
such as `bulkhead` is inlined to the metric name, so that the combination of metric name + `name` label
10+
uniquely identifies metric per backend. For example:
811

9-
For the circuit breaker library exports 2 metrics:
12+
```
13+
resilience4j_bulkhead_available_concurrent_calls{name="Backend1"}
14+
resilience4j_circuitbreaker_buffered_calls{name="Backend1"}
15+
resilience4j_ratelimiter_waiting_threads{name=Backend1"}
16+
```
1017

11-
1. By state with default metric name `circuit_breaker_states` and label `state`:
18+
For every `CircuitBreaker` the library exports the following metrics:
1219

13-
- `closed`
14-
- `open`
15-
- `half_open`
20+
1. `resilience4j_circuitbreaker_state` - the state of the circuit breaker, possible values:
1621

17-
2. By call result with default metric name `circuit_breaker_calls` and label `call_result`:
22+
- 0 - CLOSED
23+
- 1 - OPEN
24+
- 2 - HALF_OPEN
1825

19-
- `successful`
20-
- `failed`
21-
- `not_permitted`
22-
- `buffered`
23-
- `buffered_max`
26+
2. `resilience4j_circuitbreaker_calls` - the number of recorded calls,
27+
the additional `kind` label indicates type of calls being recorded, the possible
28+
`kind` values are `successful`, `failed` or `not_permitted`.
2429

25-
For the rate limiter following metric with default name `rate_limiter` and label `param` exported:
30+
3. `resilience4j_circuitbreaker_buffered_calls` - the number of buffered calls
2631

27-
- `available_permissions`
28-
- `waiting_threads`
32+
4. `resilience4j_circuitbreaker_max_buffered_calls` - the maximum number of buffered calls
33+
34+
For every `RateLimiter` the library exports the following metrics:
35+
36+
1. `resilience4j_ratelimiter_available_permissions` - the number of available permissions
37+
2. `resilience4j_ratelimiter_waiting_threads` - the number of waiting threads
38+
39+
For every `Bulkhead` the library exports the following metrics:
40+
41+
1. `resilience4j_bulkhead_available_concurrent_calls` - the number of available concurrent calls
42+
2. `resilience4j_bulkhead_max_allowed_concurrent_calls` - the maximum number of allowed concurrent calls
2943

30-
The names of the rate limiters and circuit breakers are exposed using label `name`.
3144

3245
This module also provides `CallMeter` -- a composite metric to measure single call/request metrics such as:
3346
- execution time distribution,
@@ -56,7 +69,7 @@ final CircuitBreaker foo = circuitBreakerRegistry.circuitBreaker("foo");
5669
final CircuitBreaker boo = circuitBreakerRegistry.circuitBreaker("boo");
5770

5871
// Registering metrics in prometeus CollectorRegistry
59-
collectorRegistry.register(CircuitBreakerExports.ofCircuitBreakerRegistry(circuitBreakerRegistry));
72+
collectorRegistry.register(CircuitBreakerMetricsCollector.ofCircuitBreakerRegistry(circuitBreakerRegistry));
6073
--
6174

6275
===== RateLimiter
@@ -71,10 +84,26 @@ final RateLimiter foo = rateLimiterRegistry.rateLimiter("foo");
7184
final RateLimiter boo = rateLimiterRegistry.rateLimiter("boo");
7285

7386
// Registering metrics in prometeus CollectorRegistry
74-
collectorRegistry.register(RateLimiterExports.ofRateLimiterRegistry(rateLimiterRegistry));
87+
collectorRegistry.register(RateLimiterMetricsCollector.ofRateLimiterRegistry(rateLimiterRegistry));
88+
--
89+
90+
===== Bulkhead
91+
92+
[source,java]
93+
--
94+
final CollectorRegistry collectorRegistry = CollectorRegistry.defaultRegistry;
95+
96+
final BulkheadRegistry bulkheadRegistry = BulkheadRegistry.ofDefaults();
97+
98+
final Bulkhead foo = bulkheadRegistry.bulkhead("foo");
99+
final Bulkhead boo = bulkheadRegistry.bulkhead("boo");
100+
101+
// Registering metrics in prometeus CollectorRegistry
102+
collectorRegistry.register(BulkheadMetricsCollector.ofBulkheadRegistry(rateLimiterRegistry));
75103
--
76104

77-
For both it is possible to use just a collection of breakers and limiters instead of registry.
105+
Every collector exposes more methods for binding corresponding objects, for example,
106+
you can use suppliers, collections or even collect metrics for a single object.
78107

79108
===== Call Meter
80109

resilience4j-prometheus/src/main/java/io/github/resilience4j/prometheus/BulkheadExports.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@
3434
/**
3535
* An adapter from builtin {@link Bulkhead.Metrics} to prometheus
3636
* {@link io.prometheus.client.CollectorRegistry}.
37+
*
38+
* @deprecated use {@link io.github.resilience4j.prometheus.collectors.BulkheadMetricsCollector} instead
3739
*/
40+
@Deprecated
3841
public class BulkheadExports extends Collector {
3942
private static final String DEFAULT_NAME = "resilience4j_bulkhead";
4043

resilience4j-prometheus/src/main/java/io/github/resilience4j/prometheus/CircuitBreakerExports.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@
3737
* {@link io.prometheus.client.CollectorRegistry}.
3838
*
3939
* Also exports {@link CircuitBreaker} state as a labeled metric
40+
*
41+
* @deprecated use {@link io.github.resilience4j.prometheus.collectors.CircuitBreakerMetricsCollector} instead.
4042
*/
43+
@Deprecated
4144
public class CircuitBreakerExports extends Collector {
4245

4346
private static final String DEFAULT_NAME = "resilience4j_circuitbreaker";

resilience4j-prometheus/src/main/java/io/github/resilience4j/prometheus/RateLimiterExports.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@
3434
/**
3535
* An adapter from builtin {@link RateLimiter.Metrics} to prometheus
3636
* {@link io.prometheus.client.CollectorRegistry}.
37+
*
38+
* @deprecated use {@link io.github.resilience4j.prometheus.collectors.RateLimiterMetricsCollector} instead.
3739
*/
40+
@Deprecated
3841
public class RateLimiterExports extends Collector {
3942
private static final String DEFAULT_NAME = "resilience4j_ratelimiter";
4043

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright 2019 Yevhenii Voievodin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.github.resilience4j.prometheus.collectors;
17+
18+
import io.github.resilience4j.bulkhead.Bulkhead;
19+
import io.github.resilience4j.bulkhead.Bulkhead.Metrics;
20+
import io.github.resilience4j.bulkhead.BulkheadRegistry;
21+
import io.prometheus.client.Collector;
22+
import io.prometheus.client.GaugeMetricFamily;
23+
24+
import java.util.List;
25+
import java.util.Objects;
26+
import java.util.function.Supplier;
27+
28+
import static java.util.Arrays.asList;
29+
import static java.util.Collections.singletonList;
30+
import static java.util.Objects.requireNonNull;
31+
32+
/** Collects bulkhead exposed {@link Metrics}. */
33+
public class BulkheadMetricsCollector extends Collector {
34+
35+
/**
36+
* Creates a new collector with custom metric names and
37+
* using given {@code supplier} as source of bulkheads.
38+
*
39+
* @param names the custom metric names
40+
* @param supplier the supplier of bulkheads, note that supplier will be called one every {@link #collect()}
41+
*/
42+
public static BulkheadMetricsCollector ofSupplier(MetricNames names, Supplier<? extends Iterable<? extends Bulkhead>> supplier) {
43+
return new BulkheadMetricsCollector(names, supplier);
44+
}
45+
46+
/**
47+
* Creates a new collector using given {@code supplier} as source of bulkheads.
48+
*
49+
* @param supplier the supplier of bulkheads, note that supplier will be called one very {@link #collect()}
50+
*/
51+
public static BulkheadMetricsCollector ofSupplier(Supplier<? extends Iterable<? extends Bulkhead>> supplier) {
52+
return new BulkheadMetricsCollector(MetricNames.ofDefaults(), supplier);
53+
}
54+
55+
/**
56+
* Creates a new collector using given {@code registry} as source of bulkheads.
57+
*
58+
* @param registry the source of bulkheads
59+
*/
60+
public static BulkheadMetricsCollector ofBulkheadRegistry(BulkheadRegistry registry) {
61+
return new BulkheadMetricsCollector(MetricNames.ofDefaults(), registry::getAllBulkheads);
62+
}
63+
64+
/**
65+
* Creates a new collector using given {@code bulkheads} iterable as source of bulkheads.
66+
*
67+
* @param bulkheads the source of bulkheads
68+
*/
69+
public static BulkheadMetricsCollector ofIterable(Iterable<? extends Bulkhead> bulkheads) {
70+
return new BulkheadMetricsCollector(MetricNames.ofDefaults(), () -> bulkheads);
71+
}
72+
73+
/**
74+
* Creates a new collector for a given {@code bulkhead}.
75+
*
76+
* @param bulkhead the bulkhead to collect metrics for
77+
*/
78+
public static BulkheadMetricsCollector ofBulkhead(Bulkhead bulkhead) {
79+
return ofIterable(singletonList(bulkhead));
80+
}
81+
82+
private final MetricNames names;
83+
private final Supplier<? extends Iterable<? extends Bulkhead>> bulkheadsSupplier;
84+
85+
private BulkheadMetricsCollector(MetricNames names, Supplier<? extends Iterable<? extends Bulkhead>> bulkheadsSupplier) {
86+
this.names = Objects.requireNonNull(names);
87+
this.bulkheadsSupplier = Objects.requireNonNull(bulkheadsSupplier);
88+
}
89+
90+
@Override
91+
public List<MetricFamilySamples> collect() {
92+
GaugeMetricFamily availableCallsFamily = new GaugeMetricFamily(
93+
names.getAvailableConcurrentCallsMetricName(),
94+
"The number of available concurrent calls",
95+
LabelNames.NAME
96+
);
97+
GaugeMetricFamily maxAllowedCallsFamily = new GaugeMetricFamily(
98+
names.getMaxAllowedConcurrentCallsMetricName(),
99+
"The maximum number of allowed concurrent calls",
100+
LabelNames.NAME
101+
);
102+
103+
for (Bulkhead bulkhead: bulkheadsSupplier.get()) {
104+
List<String> labelValues = singletonList(bulkhead.getName());
105+
availableCallsFamily.addMetric(labelValues, bulkhead.getMetrics().getAvailableConcurrentCalls());
106+
maxAllowedCallsFamily.addMetric(labelValues, bulkhead.getMetrics().getMaxAllowedConcurrentCalls());
107+
}
108+
109+
return asList(availableCallsFamily, maxAllowedCallsFamily);
110+
}
111+
112+
/** Defines possible configuration for metric names. */
113+
public static class MetricNames {
114+
115+
public static final String DEFAULT_BULKHEAD_AVAILABLE_CONCURRENT_CALLS_METRIC_NAME = "resilience4j_bulkhead_available_concurrent_calls";
116+
public static final String DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME = "resilience4j_bulkhead_max_allowed_concurrent_calls";
117+
118+
/**
119+
* Returns a builder for creating custom metric names.
120+
* Note that names have default values, so only desired metrics can be renamed.
121+
*/
122+
public static Builder custom() {
123+
return new Builder();
124+
}
125+
126+
/** Returns default metric names. */
127+
public static MetricNames ofDefaults() {
128+
return new MetricNames();
129+
}
130+
131+
private String availableConcurrentCallsMetricName = DEFAULT_BULKHEAD_AVAILABLE_CONCURRENT_CALLS_METRIC_NAME;
132+
private String maxAllowedConcurrentCallsMetricName = DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME;
133+
134+
private MetricNames() {}
135+
136+
/**
137+
* Returns the metric name for bulkhead concurrent calls,
138+
* defaults to {@value DEFAULT_BULKHEAD_AVAILABLE_CONCURRENT_CALLS_METRIC_NAME}.
139+
*/
140+
public String getAvailableConcurrentCallsMetricName() {
141+
return availableConcurrentCallsMetricName;
142+
}
143+
144+
/**
145+
* Returns the metric name for bulkhead max available concurrent calls,
146+
* defaults to {@value DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME}.
147+
*/
148+
public String getMaxAllowedConcurrentCallsMetricName() {
149+
return maxAllowedConcurrentCallsMetricName;
150+
}
151+
152+
/** Helps building custom instance of {@link MetricNames}. */
153+
public static class Builder {
154+
155+
private final MetricNames metricNames = new MetricNames();
156+
157+
/** Overrides the default metric name {@value MetricNames#DEFAULT_BULKHEAD_AVAILABLE_CONCURRENT_CALLS_METRIC_NAME} with a given one. */
158+
public Builder availableConcurrentCallsMetricName(String availableConcurrentCallsMetricNames) {
159+
metricNames.availableConcurrentCallsMetricName = requireNonNull(availableConcurrentCallsMetricNames);
160+
return this;
161+
}
162+
163+
/** Overrides the default metric name {@value MetricNames#DEFAULT_BULKHEAD_MAX_ALLOWED_CONCURRENT_CALLS_METRIC_NAME} with a given one. */
164+
public Builder maxAllowedConcurrentCallsMetricName(String maxAllowedConcurrentCallsMetricName) {
165+
metricNames.maxAllowedConcurrentCallsMetricName = requireNonNull(maxAllowedConcurrentCallsMetricName);
166+
return this;
167+
}
168+
169+
/** Builds {@link MetricNames} instance. */
170+
public MetricNames build() {
171+
return metricNames;
172+
}
173+
}
174+
}
175+
}

0 commit comments

Comments
 (0)