Skip to content

Commit 6eff53c

Browse files
committed
Add support verification and documentation for native histograms with custom buckets (NHCB)
Implements issue #1838 by verifying and documenting that client_java fully supports native histograms with custom buckets (NHCB, schema -53). According to the Prometheus specification, NHCB is handled by exposing classic histograms with custom bucket boundaries, which Prometheus servers convert to native histograms (schema -53) when configured with convert_classic_histograms_to_nhcb. Changes: - Add comprehensive test suite (11 tests) verifying custom bucket support for arbitrary, linear, and exponential boundaries - Add documentation section on custom buckets and NHCB to metric-types.md - Create complete working example with Docker Compose (Prometheus + Grafana) - Add verification report documenting findings Test coverage: - Arbitrary custom boundaries - Linear boundaries (equal-width buckets) - Exponential boundaries - Classic-only and dual-mode histograms - Text and protobuf format serialization - Labeled histograms and edge cases All 11 tests pass successfully.
1 parent baf8b76 commit 6eff53c

File tree

12 files changed

+1440
-0
lines changed

12 files changed

+1440
-0
lines changed

CUSTOM_BUCKETS_VERIFICATION.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Native Histograms with Custom Buckets - Verification Report
2+
3+
## Issue #1838: Verify that client_java supports native histograms with custom buckets
4+
5+
### Summary
6+
7+
This report documents the verification that the Prometheus Java client library properly supports native histograms with custom bucket configurations (NHCB - Native Histograms with Custom Buckets).
8+
9+
### Background
10+
11+
According to the [Prometheus Native Histograms specification](https://prometheus.io/docs/specs/native_histograms/), native histograms with custom buckets (schema -53) are a feature that allows representing classic histograms as native histograms with explicit bucket boundaries.
12+
13+
**Key findings from the specification:**
14+
- Schema -53 is used for custom bucket boundaries
15+
- There is currently no dedicated protobuf field for custom bucket boundaries
16+
- Custom-bucket histograms are exposed as **classic histograms** with custom boundaries
17+
- Prometheus servers convert these to NHCB upon ingestion when configured with `convert_classic_histograms_to_nhcb`
18+
19+
### Verification Approach
20+
21+
The Java client library already supports custom bucket configurations through the `classicUpperBounds()`, `classicLinearUpperBounds()`, and `classicExponentialUpperBounds()` builder methods. These methods allow users to define custom bucket boundaries for histograms.
22+
23+
Since NHCB is handled by Prometheus servers during ingestion (not by client libraries), our verification focuses on ensuring that:
24+
25+
1. Histograms with custom bucket boundaries can be created
26+
2. Custom buckets are correctly exposed in both text and protobuf formats
27+
3. Both classic-only and dual (classic+native) histograms work with custom buckets
28+
4. Various custom bucket configurations work correctly
29+
30+
### Test Implementation
31+
32+
Created comprehensive test suite: `CustomBucketsHistogramTest.java`
33+
34+
The test suite includes 11 tests covering:
35+
36+
#### 1. **Custom Buckets with Arbitrary Boundaries** (`testCustomBucketsWithArbitraryBoundaries`)
37+
- Tests histogram with arbitrary custom bucket boundaries
38+
- Verifies observations are distributed correctly across buckets
39+
- Validates count and sum calculations
40+
41+
#### 2. **Custom Buckets with Linear Boundaries** (`testCustomBucketsWithLinearBoundaries`)
42+
- Tests histogram with linear custom bucket boundaries (equal-width buckets)
43+
- Use case: Queue size monitoring with fixed intervals
44+
45+
#### 3. **Custom Buckets with Exponential Boundaries** (`testCustomBucketsWithExponentialBoundaries`)
46+
- Tests histogram with exponential custom bucket boundaries
47+
- Use case: Metrics spanning multiple orders of magnitude (e.g., response sizes)
48+
49+
#### 4. **Classic-Only Histogram with Custom Buckets** (`testCustomBucketsClassicOnlyHistogram`)
50+
- Verifies custom buckets work when using `.classicOnly()`
51+
- Confirms no native histogram representation is maintained
52+
53+
#### 5. **Dual-Mode Histogram with Custom Buckets** (`testCustomBucketsDualModeHistogram`)
54+
- Tests the default mode (both classic and native representations)
55+
- Verifies custom classic buckets coexist with native histogram representation
56+
- **This is the most relevant test for NHCB support**
57+
58+
#### 6. **Text Format Output** (`testCustomBucketsTextFormatOutput`)
59+
- Verifies custom buckets are correctly serialized in Prometheus text format
60+
- Validates bucket labels (le) and counts
61+
62+
#### 7. **Protobuf Format Output** (`testCustomBucketsProtobufFormatOutput`)
63+
- Verifies custom buckets are correctly serialized in Prometheus protobuf format
64+
- Validates bucket upper bounds and cumulative counts
65+
- Confirms native histogram fields are present (for dual-mode)
66+
67+
#### 8. **Custom Buckets with Negative Values** (`testCustomBucketsWithNegativeValues`)
68+
- Tests custom buckets with negative boundary values
69+
- Use case: Temperature or other metrics with negative ranges
70+
71+
#### 9. **Custom Buckets with Labels** (`testCustomBucketsWithLabels`)
72+
- Verifies custom buckets work correctly with labeled histograms
73+
- Tests multiple label combinations
74+
75+
#### 10. **Boundary Edge Cases** (`testCustomBucketsBoundaryEdgeCases`)
76+
- Tests observations exactly on bucket boundaries
77+
- Verifies buckets are inclusive of their upper bound
78+
79+
#### 11. **Fine-Grained Custom Buckets** (`testCustomBucketsFineBoundaries`)
80+
- Tests with very precise custom bucket boundaries
81+
- Use case: High-precision measurements
82+
83+
### Test Results
84+
85+
All 11 tests pass successfully:
86+
87+
```
88+
[INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0
89+
[INFO] BUILD SUCCESS
90+
```
91+
92+
### Key Findings
93+
94+
1. **Custom bucket support is fully functional**: The Java client library correctly handles histograms with custom bucket boundaries.
95+
96+
2. **Dual-mode operation**: By default, histograms maintain both classic (with custom buckets) and native representations, which is ideal for NHCB support.
97+
98+
3. **Correct serialization**: Custom buckets are properly serialized in both:
99+
- Text format (with `le` labels)
100+
- Protobuf format (with bucket upper bounds and cumulative counts)
101+
102+
4. **Native histogram fields present**: When using dual-mode (default), the protobuf output includes native histogram fields (schema, zero_count, etc.) alongside the classic buckets.
103+
104+
5. **Flexible bucket configurations**: The library supports:
105+
- Arbitrary custom boundaries
106+
- Linear boundaries (equal-width)
107+
- Exponential boundaries
108+
- Negative values
109+
- Very fine-grained precision
110+
111+
### Conclusion
112+
113+
**The Prometheus Java client library (client_java) fully supports native histograms with custom buckets.**
114+
115+
The library correctly:
116+
- Allows users to define custom bucket boundaries
117+
- Maintains both classic and native histogram representations by default
118+
- Exposes custom buckets in the classic histogram format
119+
- Serializes correctly in both text and protobuf formats
120+
121+
Prometheus servers can convert these histograms to NHCB (schema -53) upon ingestion when configured with the `convert_classic_histograms_to_nhcb` option.
122+
123+
### Recommendations
124+
125+
1. **Documentation**: Consider documenting the NHCB support in the user-facing documentation, explaining that:
126+
- Custom buckets are supported via the existing `classicUpperBounds()` API
127+
- Prometheus servers handle the conversion to NHCB (schema -53)
128+
- The default dual-mode is recommended for NHCB compatibility
129+
130+
2. **Example**: Consider adding an example demonstrating custom bucket usage for NHCB scenarios.
131+
132+
3. **Close issue #1838**: This verification confirms that custom bucket support is working correctly.
133+
134+
### Test File Location
135+
136+
- `prometheus-metrics-core/src/test/java/io/prometheus/metrics/core/metrics/CustomBucketsHistogramTest.java`
137+
138+
### References
139+
140+
- [Prometheus Native Histograms Specification](https://prometheus.io/docs/specs/native_histograms/)
141+
- [GitHub Issue #1838](https://github.com/prometheus/client_java/issues/1838)
142+
- [Prometheus client_model Repository](https://github.com/prometheus/client_model)

docs/content/getting-started/metric-types.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,94 @@ for [Histogram.Builder](/client_java/api/io/prometheus/metrics/core/metrics/Hist
121121
for a complete list of options. Some options can be configured at runtime,
122122
see [config]({{< relref "../config/config.md" >}}).
123123

124+
### Custom Bucket Boundaries
125+
126+
The default bucket boundaries are designed for measuring request durations in seconds. For other
127+
use cases, you may want to define custom bucket boundaries. The histogram builder provides three
128+
methods for this:
129+
130+
**1. Arbitrary Custom Boundaries**
131+
132+
Use `classicUpperBounds(...)` to specify arbitrary bucket boundaries:
133+
134+
```java
135+
Histogram responseSize = Histogram.builder()
136+
.name("http_response_size_bytes")
137+
.help("HTTP response size in bytes")
138+
.classicUpperBounds(100, 1000, 10000, 100000, 1000000) // bytes
139+
.register();
140+
```
141+
142+
**2. Linear Boundaries**
143+
144+
Use `classicLinearUpperBounds(start, width, count)` for equal-width buckets:
145+
146+
```java
147+
Histogram queueSize = Histogram.builder()
148+
.name("queue_size")
149+
.help("Number of items in queue")
150+
.classicLinearUpperBounds(10, 10, 10) // 10, 20, 30, ..., 100
151+
.register();
152+
```
153+
154+
**3. Exponential Boundaries**
155+
156+
Use `classicExponentialUpperBounds(start, factor, count)` for exponential growth:
157+
158+
```java
159+
Histogram dataSize = Histogram.builder()
160+
.name("data_size_bytes")
161+
.help("Data size in bytes")
162+
.classicExponentialUpperBounds(100, 10, 5) // 100, 1k, 10k, 100k, 1M
163+
.register();
164+
```
165+
166+
### Native Histograms with Custom Buckets (NHCB)
167+
168+
Prometheus supports a special mode called Native Histograms with Custom Buckets (NHCB) that uses
169+
schema -53. In this mode, custom bucket boundaries from classic histograms are preserved when
170+
converting to native histograms.
171+
172+
The Java client library automatically supports NHCB:
173+
174+
1. By default, histograms maintain both classic (with custom buckets) and native representations
175+
2. The classic representation with custom buckets is exposed to Prometheus
176+
3. Prometheus servers can convert these to NHCB upon ingestion when configured with the
177+
`convert_classic_histograms_to_nhcb` scrape option
178+
179+
Example:
180+
181+
```java
182+
// This histogram will work seamlessly with NHCB
183+
Histogram apiLatency = Histogram.builder()
184+
.name("api_request_duration_seconds")
185+
.help("API request duration")
186+
.classicUpperBounds(0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0) // custom boundaries
187+
.register();
188+
```
189+
190+
On the Prometheus side, configure the scrape job:
191+
192+
```yaml
193+
scrape_configs:
194+
- job_name: "my-app"
195+
scrape_protocols: ["PrometheusProto"]
196+
convert_classic_histograms_to_nhcb: true
197+
static_configs:
198+
- targets: ["localhost:9400"]
199+
```
200+
201+
{{< hint type=note >}}
202+
NHCB is useful when:
203+
204+
- You need precise bucket boundaries for your specific use case
205+
- You're migrating from classic histograms and want to preserve bucket boundaries
206+
- Exponential bucketing from standard native histograms isn't a good fit for your distribution
207+
{{< /hint >}}
208+
209+
See [examples/example-custom-buckets](https://github.com/prometheus/client_java/tree/main/examples/example-custom-buckets) <!-- editorconfig-checker-disable-line -->
210+
for a complete example with Prometheus and Grafana.
211+
124212
Histograms and summaries are both used for observing distributions. Therefore, the both implement
125213
the `DistributionDataPoint` interface. Using the `DistributionDataPoint` interface directly gives
126214
you the option to switch between histograms and summaries later with minimal code changes.

0 commit comments

Comments
 (0)