Skip to content

Commit 37ea78b

Browse files
committed
Apply config setting for built-in meter name case to extended KPI meter names
Signed-off-by: Tim Quinn <[email protected]>
1 parent 8893f62 commit 37ea78b

File tree

3 files changed

+121
-18
lines changed

3 files changed

+121
-18
lines changed

webserver/observe/metrics/src/main/java/io/helidon/webserver/observe/metrics/KeyPerformanceIndicatorMetricsImpls.java

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2021, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323
import java.util.concurrent.atomic.AtomicInteger;
2424

25+
import io.helidon.metrics.api.BuiltInMeterNameFormat;
2526
import io.helidon.metrics.api.Counter;
2627
import io.helidon.metrics.api.Gauge;
2728
import io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig;
@@ -55,24 +56,31 @@ class KeyPerformanceIndicatorMetricsImpls {
5556

5657
private static final Map<String, KeyPerformanceIndicatorSupport.Metrics> KPI_METRICS = new HashMap<>();
5758

59+
// Maps camelCase names to snake_case, but only for those names that are actually different in the two cases.
60+
private static final Map<String, String> CAMEL_TO_SNAKE_CASE_METER_NAMES = Map.of("inFlight", "in_flight",
61+
"longRunning", "long_running");
62+
63+
5864
private KeyPerformanceIndicatorMetricsImpls() {
5965
}
6066

6167
/**
6268
* Provides a KPI metrics instance.
6369
*
64-
* @param kpiMeterRegistry meter registry holding the KPI metrics
65-
* @param meterNamePrefix prefix to use for the created metrics (e.g., "requests")
66-
* @param kpiConfig KPI metrics config which may influence the construction of the metrics
70+
* @param kpiMeterRegistry meter registry holding the KPI metrics
71+
* @param meterNamePrefix prefix to use for the created metrics (e.g., "requests")
72+
* @param kpiConfig KPI metrics config which may influence the construction of the metrics
73+
* @param builtInMeterNameFormat format to use for meter names
6774
* @return properly prepared new KPI metrics instance
6875
*/
6976
static KeyPerformanceIndicatorSupport.Metrics get(MeterRegistry kpiMeterRegistry,
7077
String meterNamePrefix,
71-
KeyPerformanceIndicatorMetricsConfig kpiConfig) {
78+
KeyPerformanceIndicatorMetricsConfig kpiConfig,
79+
BuiltInMeterNameFormat builtInMeterNameFormat) {
7280
return KPI_METRICS.computeIfAbsent(meterNamePrefix, prefix ->
7381
kpiConfig.extended()
74-
? new Extended(kpiMeterRegistry, meterNamePrefix, kpiConfig)
75-
: new Basic(kpiMeterRegistry, meterNamePrefix));
82+
? new Extended(kpiMeterRegistry, meterNamePrefix, kpiConfig, builtInMeterNameFormat)
83+
: new Basic(kpiMeterRegistry, meterNamePrefix, builtInMeterNameFormat));
7684
}
7785

7886
static void close() {
@@ -87,11 +95,13 @@ private static class Basic implements KeyPerformanceIndicatorSupport.Metrics {
8795
private final Counter totalCount;
8896
private final MeterRegistry meterRegistry;
8997
private final List<Meter> meters = new ArrayList<>();
98+
private final BuiltInMeterNameFormat builtInMeterNameFormat;
9099

91-
protected Basic(MeterRegistry kpiMeterRegistry, String meterNamePrefix) {
100+
protected Basic(MeterRegistry kpiMeterRegistry, String meterNamePrefix, BuiltInMeterNameFormat builtInMeterNameFormat) {
92101
meterRegistry = kpiMeterRegistry;
102+
this.builtInMeterNameFormat = builtInMeterNameFormat;
93103
totalCount = add(kpiMeterRegistry.getOrCreate(
94-
Counter.builder(meterNamePrefix + REQUESTS_COUNT_NAME)
104+
Counter.builder(meterNamePrefix + meterName(REQUESTS_COUNT_NAME))
95105
.description(
96106
"Each request (regardless of HTTP method) will increase this counter")
97107
.scope(KPI_METERS_SCOPE)));
@@ -116,6 +126,12 @@ protected <M extends Meter> M add(M meter) {
116126
protected Counter totalCount() {
117127
return totalCount;
118128
}
129+
130+
protected String meterName(String camelCaseMeterName){
131+
return builtInMeterNameFormat == BuiltInMeterNameFormat.CAMEL
132+
? camelCaseMeterName
133+
: CAMEL_TO_SNAKE_CASE_METER_NAMES.getOrDefault(camelCaseMeterName, camelCaseMeterName);
134+
}
119135
}
120136

121137
/**
@@ -136,15 +152,20 @@ private static class Extended extends Basic {
136152

137153
protected Extended(MeterRegistry kpiMeterRegistry,
138154
String meterNamePrefix,
139-
KeyPerformanceIndicatorMetricsConfig kpiConfig) {
140-
this(kpiMeterRegistry, meterNamePrefix, kpiConfig.longRunningRequestThreshold());
155+
KeyPerformanceIndicatorMetricsConfig kpiConfig,
156+
BuiltInMeterNameFormat builtInMeterNameFormat) {
157+
this(kpiMeterRegistry, meterNamePrefix, kpiConfig.longRunningRequestThreshold(), builtInMeterNameFormat);
141158
}
142159

143-
private Extended(MeterRegistry kpiMeterRegistry, String meterNamePrefix, Duration longRunningRequestThreshold) {
144-
super(kpiMeterRegistry, meterNamePrefix);
160+
private Extended(MeterRegistry kpiMeterRegistry,
161+
String meterNamePrefix,
162+
Duration longRunningRequestThreshold,
163+
BuiltInMeterNameFormat builtInMeterNameFormat) {
164+
super(kpiMeterRegistry, meterNamePrefix, builtInMeterNameFormat);
145165
this.longRunningRequestThresdholdMs = longRunningRequestThreshold.toMillis();
146166

147-
inflightRequests = kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix + INFLIGHT_REQUESTS_NAME,
167+
inflightRequests = kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix
168+
+ meterName(INFLIGHT_REQUESTS_NAME),
148169
inflightRequestsCount,
149170
AtomicInteger::get)
150171
.scope(KPI_METERS_SCOPE));
@@ -155,12 +176,12 @@ private Extended(MeterRegistry kpiMeterRegistry, String meterNamePrefix, Duratio
155176
.scope(KPI_METERS_SCOPE)
156177
);
157178

158-
load = kpiMeterRegistry.getOrCreate(Counter.builder(meterNamePrefix + LOAD_NAME)
179+
load = kpiMeterRegistry.getOrCreate(Counter.builder(meterNamePrefix + meterName(LOAD_NAME))
159180
.description(LOAD_DESCRIPTION)
160181
.scope(KPI_METERS_SCOPE));
161182

162183
deferredRequests = new DeferredRequests();
163-
kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix + DEFERRED_NAME,
184+
kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix + meterName(DEFERRED_NAME),
164185
deferredRequests,
165186
DeferredRequests::value)
166187
.description("Measures deferred requests")

webserver/observe/metrics/src/main/java/io/helidon/webserver/observe/metrics/MetricsFeature.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -78,7 +78,8 @@ void configureVendorMetrics(HttpRouting.Builder rules) {
7878
KeyPerformanceIndicatorMetricsImpls.get(meterRegistry,
7979
KPI_METER_NAME_PREFIX_WITH_DOT,
8080
metricsConfig
81-
.keyPerformanceIndicatorMetricsConfig());
81+
.keyPerformanceIndicatorMetricsConfig(),
82+
metricsConfig.builtInMeterNameFormat());
8283

8384
rules.addFilter((chain, req, res) -> {
8485
KeyPerformanceIndicatorSupport.Context kpiContext = kpiContext(req);
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
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.helidon.webserver.observe.metrics;
17+
18+
import io.helidon.metrics.api.BuiltInMeterNameFormat;
19+
import io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig;
20+
import io.helidon.metrics.api.MeterRegistry;
21+
import io.helidon.metrics.api.Metrics;
22+
import io.helidon.metrics.api.MetricsConfig;
23+
24+
import org.junit.jupiter.api.BeforeEach;
25+
import org.junit.jupiter.api.Test;
26+
27+
import static org.hamcrest.MatcherAssert.assertThat;
28+
import static org.hamcrest.Matchers.hasItem;
29+
30+
class TestKpiMeterNameCase {
31+
32+
@BeforeEach
33+
void clear() {
34+
KeyPerformanceIndicatorMetricsImpls.close();
35+
}
36+
37+
@Test
38+
void testExtendedWithNoCaseSetting() {
39+
MetricsConfig metricsConfig = MetricsConfig.builder()
40+
.keyPerformanceIndicatorMetricsConfig(() -> KeyPerformanceIndicatorMetricsConfig.builder()
41+
.extended(true)
42+
.build())
43+
.build();
44+
MeterRegistry meterRegistry = Metrics.createMeterRegistry(metricsConfig);
45+
46+
// As a side-effect, the following line registers the KPI metrics in the meter registry.
47+
KeyPerformanceIndicatorMetricsImpls.get(meterRegistry,
48+
"requests.",
49+
KeyPerformanceIndicatorMetricsConfig.builder().extended(true).build(),
50+
BuiltInMeterNameFormat.CAMEL);
51+
52+
assertThat("In-flight KPI",
53+
meterRegistry.meters().stream()
54+
.map(m -> m.id().name())
55+
.toList(),
56+
hasItem("requests.inFlight"));
57+
}
58+
59+
@Test
60+
void testExtendedWithSnakeCaseSetting() {
61+
MetricsConfig metricsConfig = MetricsConfig.builder()
62+
.keyPerformanceIndicatorMetricsConfig(() -> KeyPerformanceIndicatorMetricsConfig.builder()
63+
.extended(true)
64+
.build())
65+
.builtInMeterNameFormat(BuiltInMeterNameFormat.SNAKE)
66+
.build();
67+
MeterRegistry meterRegistry = Metrics.createMeterRegistry(metricsConfig);
68+
69+
// As a side-effect, the following line registers the KPI metrics in the meter registry.
70+
KeyPerformanceIndicatorMetricsImpls.get(meterRegistry,
71+
"requests.",
72+
KeyPerformanceIndicatorMetricsConfig.builder().extended(true).build(),
73+
BuiltInMeterNameFormat.SNAKE);
74+
75+
assertThat("In-flight KPI",
76+
meterRegistry.meters().stream()
77+
.map(m -> m.id().name())
78+
.toList(),
79+
hasItem("requests.in_flight"));
80+
}
81+
}

0 commit comments

Comments
 (0)