Skip to content

Commit

Permalink
Apply config setting for built-in meter name case to extended KPI met…
Browse files Browse the repository at this point in the history
…er names

Signed-off-by: Tim Quinn <[email protected]>
  • Loading branch information
tjquinno committed Feb 13, 2025
1 parent 8893f62 commit 37ea78b
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2023 Oracle and/or its affiliates.
* Copyright (c) 2021, 2025 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import io.helidon.metrics.api.BuiltInMeterNameFormat;
import io.helidon.metrics.api.Counter;
import io.helidon.metrics.api.Gauge;
import io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig;
Expand Down Expand Up @@ -55,24 +56,31 @@ class KeyPerformanceIndicatorMetricsImpls {

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

// Maps camelCase names to snake_case, but only for those names that are actually different in the two cases.
private static final Map<String, String> CAMEL_TO_SNAKE_CASE_METER_NAMES = Map.of("inFlight", "in_flight",
"longRunning", "long_running");


private KeyPerformanceIndicatorMetricsImpls() {
}

/**
* Provides a KPI metrics instance.
*
* @param kpiMeterRegistry meter registry holding the KPI metrics
* @param meterNamePrefix prefix to use for the created metrics (e.g., "requests")
* @param kpiConfig KPI metrics config which may influence the construction of the metrics
* @param kpiMeterRegistry meter registry holding the KPI metrics
* @param meterNamePrefix prefix to use for the created metrics (e.g., "requests")
* @param kpiConfig KPI metrics config which may influence the construction of the metrics
* @param builtInMeterNameFormat format to use for meter names
* @return properly prepared new KPI metrics instance
*/
static KeyPerformanceIndicatorSupport.Metrics get(MeterRegistry kpiMeterRegistry,
String meterNamePrefix,
KeyPerformanceIndicatorMetricsConfig kpiConfig) {
KeyPerformanceIndicatorMetricsConfig kpiConfig,
BuiltInMeterNameFormat builtInMeterNameFormat) {
return KPI_METRICS.computeIfAbsent(meterNamePrefix, prefix ->
kpiConfig.extended()
? new Extended(kpiMeterRegistry, meterNamePrefix, kpiConfig)
: new Basic(kpiMeterRegistry, meterNamePrefix));
? new Extended(kpiMeterRegistry, meterNamePrefix, kpiConfig, builtInMeterNameFormat)
: new Basic(kpiMeterRegistry, meterNamePrefix, builtInMeterNameFormat));
}

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

protected Basic(MeterRegistry kpiMeterRegistry, String meterNamePrefix) {
protected Basic(MeterRegistry kpiMeterRegistry, String meterNamePrefix, BuiltInMeterNameFormat builtInMeterNameFormat) {
meterRegistry = kpiMeterRegistry;
this.builtInMeterNameFormat = builtInMeterNameFormat;
totalCount = add(kpiMeterRegistry.getOrCreate(
Counter.builder(meterNamePrefix + REQUESTS_COUNT_NAME)
Counter.builder(meterNamePrefix + meterName(REQUESTS_COUNT_NAME))
.description(
"Each request (regardless of HTTP method) will increase this counter")
.scope(KPI_METERS_SCOPE)));
Expand All @@ -116,6 +126,12 @@ protected <M extends Meter> M add(M meter) {
protected Counter totalCount() {
return totalCount;
}

protected String meterName(String camelCaseMeterName){
return builtInMeterNameFormat == BuiltInMeterNameFormat.CAMEL
? camelCaseMeterName
: CAMEL_TO_SNAKE_CASE_METER_NAMES.getOrDefault(camelCaseMeterName, camelCaseMeterName);
}
}

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

protected Extended(MeterRegistry kpiMeterRegistry,
String meterNamePrefix,
KeyPerformanceIndicatorMetricsConfig kpiConfig) {
this(kpiMeterRegistry, meterNamePrefix, kpiConfig.longRunningRequestThreshold());
KeyPerformanceIndicatorMetricsConfig kpiConfig,
BuiltInMeterNameFormat builtInMeterNameFormat) {
this(kpiMeterRegistry, meterNamePrefix, kpiConfig.longRunningRequestThreshold(), builtInMeterNameFormat);
}

private Extended(MeterRegistry kpiMeterRegistry, String meterNamePrefix, Duration longRunningRequestThreshold) {
super(kpiMeterRegistry, meterNamePrefix);
private Extended(MeterRegistry kpiMeterRegistry,
String meterNamePrefix,
Duration longRunningRequestThreshold,
BuiltInMeterNameFormat builtInMeterNameFormat) {
super(kpiMeterRegistry, meterNamePrefix, builtInMeterNameFormat);
this.longRunningRequestThresdholdMs = longRunningRequestThreshold.toMillis();

inflightRequests = kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix + INFLIGHT_REQUESTS_NAME,
inflightRequests = kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix
+ meterName(INFLIGHT_REQUESTS_NAME),
inflightRequestsCount,
AtomicInteger::get)
.scope(KPI_METERS_SCOPE));
Expand All @@ -155,12 +176,12 @@ private Extended(MeterRegistry kpiMeterRegistry, String meterNamePrefix, Duratio
.scope(KPI_METERS_SCOPE)
);

load = kpiMeterRegistry.getOrCreate(Counter.builder(meterNamePrefix + LOAD_NAME)
load = kpiMeterRegistry.getOrCreate(Counter.builder(meterNamePrefix + meterName(LOAD_NAME))
.description(LOAD_DESCRIPTION)
.scope(KPI_METERS_SCOPE));

deferredRequests = new DeferredRequests();
kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix + DEFERRED_NAME,
kpiMeterRegistry.getOrCreate(Gauge.builder(meterNamePrefix + meterName(DEFERRED_NAME),
deferredRequests,
DeferredRequests::value)
.description("Measures deferred requests")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -78,7 +78,8 @@ void configureVendorMetrics(HttpRouting.Builder rules) {
KeyPerformanceIndicatorMetricsImpls.get(meterRegistry,
KPI_METER_NAME_PREFIX_WITH_DOT,
metricsConfig
.keyPerformanceIndicatorMetricsConfig());
.keyPerformanceIndicatorMetricsConfig(),
metricsConfig.builtInMeterNameFormat());

rules.addFilter((chain, req, res) -> {
KeyPerformanceIndicatorSupport.Context kpiContext = kpiContext(req);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2025 Oracle and/or its affiliates.
*
* 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.helidon.webserver.observe.metrics;

import io.helidon.metrics.api.BuiltInMeterNameFormat;
import io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig;
import io.helidon.metrics.api.MeterRegistry;
import io.helidon.metrics.api.Metrics;
import io.helidon.metrics.api.MetricsConfig;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;

class TestKpiMeterNameCase {

@BeforeEach
void clear() {
KeyPerformanceIndicatorMetricsImpls.close();
}

@Test
void testExtendedWithNoCaseSetting() {
MetricsConfig metricsConfig = MetricsConfig.builder()
.keyPerformanceIndicatorMetricsConfig(() -> KeyPerformanceIndicatorMetricsConfig.builder()
.extended(true)
.build())
.build();
MeterRegistry meterRegistry = Metrics.createMeterRegistry(metricsConfig);

// As a side-effect, the following line registers the KPI metrics in the meter registry.
KeyPerformanceIndicatorMetricsImpls.get(meterRegistry,
"requests.",
KeyPerformanceIndicatorMetricsConfig.builder().extended(true).build(),
BuiltInMeterNameFormat.CAMEL);

assertThat("In-flight KPI",
meterRegistry.meters().stream()
.map(m -> m.id().name())
.toList(),
hasItem("requests.inFlight"));
}

@Test
void testExtendedWithSnakeCaseSetting() {
MetricsConfig metricsConfig = MetricsConfig.builder()
.keyPerformanceIndicatorMetricsConfig(() -> KeyPerformanceIndicatorMetricsConfig.builder()
.extended(true)
.build())
.builtInMeterNameFormat(BuiltInMeterNameFormat.SNAKE)
.build();
MeterRegistry meterRegistry = Metrics.createMeterRegistry(metricsConfig);

// As a side-effect, the following line registers the KPI metrics in the meter registry.
KeyPerformanceIndicatorMetricsImpls.get(meterRegistry,
"requests.",
KeyPerformanceIndicatorMetricsConfig.builder().extended(true).build(),
BuiltInMeterNameFormat.SNAKE);

assertThat("In-flight KPI",
meterRegistry.meters().stream()
.map(m -> m.id().name())
.toList(),
hasItem("requests.in_flight"));
}
}

0 comments on commit 37ea78b

Please sign in to comment.