Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unwrap support to metrics builders #8588

Merged
merged 3 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/src/main/asciidoc/se/metrics/metrics.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ Your code can then operate on the returned meter as needed to record new measure

The example code in the <<Examples>> section below illustrates how to register, retrieve, and update meters.

=== Accessing the Underlying Implementation: `unwrap`
The neutral Helidon metrics API is an abstraction of common metrics behavior independent from any given implementation. As such, we intentionally excluded some implementation-specific behavior from the API.

Sometimes you might want access to methods that are present in a particular metrics implementation but not in the Helidon API. Helidon allows that via the `unwrap` method on the meter types and on their builders. Each full implementation of the Helidon meter types and their builders refers to a delegate meter or delegate builder internally. The `unwrap` method lets you obtain the delegate, cast to the type you want.

Of course, using this technique binds your code to a particular metrics implementation.

The link:{metrics-javadoc-base-url}/io/helidon/metrics/api/Wrapper.html[`Wrapper`] interface declares the `unwrap` method which accepts a class parameter to which the delegate is cast. You can then invoke any method declared on the implementation-specific type.

// Here's Configuration.
include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-intro]

Expand Down
4 changes: 3 additions & 1 deletion metrics/api/src/main/java/io/helidon/metrics/api/Meter.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public String typeName() {
* @param <B> type of the builder
* @param <M> type of the meter the builder creates
*/
interface Builder<B extends Builder<B, M>, M extends Meter> {
interface Builder<B extends Builder<B, M>, M extends Meter> extends Wrapper {

/**
* Returns the type-correct "this".
Expand Down Expand Up @@ -202,6 +202,8 @@ default B identity() {
* @return the assigned scope if set; empty otherwise
*/
Optional<String> scope();


}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 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 @@ -151,7 +151,7 @@ public int hashCode() {
}
}

abstract static class Builder<B extends Builder<B, M>, M extends Meter> {
abstract static class Builder<B extends Builder<B, M>, M extends Meter> implements Wrapper {

private final String name;
private final Map<String, String> tags = new TreeMap<>(); // tree map for ordering by tag name
Expand Down Expand Up @@ -215,6 +215,11 @@ public Optional<String> description() {
public Optional<String> scope() {
return Optional.ofNullable(scope);
}

@Override
public <R> R unwrap(Class<? extends R> c) {
return c.cast(this);
}
}

static class Counter extends NoOpMeter implements io.helidon.metrics.api.Counter {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 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 @@ -25,6 +25,7 @@

import io.helidon.metrics.api.Meter;
import io.helidon.metrics.api.Tag;
import io.helidon.metrics.api.Wrapper;

import io.micrometer.core.instrument.Timer;

Expand Down Expand Up @@ -164,7 +165,8 @@ protected StringJoiner stringJoiner() {
abstract static class Builder<B,
M extends io.micrometer.core.instrument.Meter,
HB extends Builder<B, M, HB, HM>,
HM extends MMeter<M>> {
HM extends MMeter<M>>
implements Wrapper {

private final String name;
private final B delegate;
Expand Down Expand Up @@ -248,6 +250,11 @@ public Map<String, String> tags() {
return new TreeMap<>(tags);
}

@Override
public <R> R unwrap(Class<? extends R> c) {
return c.cast(delegate);
}

protected B delegate() {
return delegate;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 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 @@ -17,6 +17,7 @@

import java.util.List;

import io.helidon.common.testing.junit5.OptionalMatcher;
import io.helidon.metrics.api.Counter;
import io.helidon.metrics.api.MeterRegistry;
import io.helidon.metrics.api.Metrics;
Expand All @@ -40,10 +41,14 @@ static void prep() {

@Test
void testUnwrap() {
Counter c = meterRegistry.getOrCreate(Counter.builder("c4"));
Counter.Builder builder = Counter.builder("c4");
String descr = "Example counter";
builder.unwrap(io.micrometer.core.instrument.Counter.Builder.class).description(descr);
Counter c = meterRegistry.getOrCreate(builder);
io.micrometer.core.instrument.Counter mCounter = c.unwrap(io.micrometer.core.instrument.Counter.class);
assertThat("Initial value", c.count(), is(0L));
mCounter.increment();
assertThat("Updated value", c.count(), is(1L));
assertThat("Description", c.description(), OptionalMatcher.optionalValue(is(descr)));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 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 @@ -15,12 +15,12 @@
*/
package io.helidon.metrics.providers.micrometer;

import java.time.Duration;
import java.util.List;

import io.helidon.metrics.api.DistributionSummary;
import io.helidon.metrics.api.MeterRegistry;
import io.helidon.metrics.api.Metrics;
import io.helidon.metrics.api.MetricsConfig;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand All @@ -39,12 +39,14 @@ static void prep() {

@Test
void testUnwrap() {
DistributionSummary summary = meterRegistry.getOrCreate(DistributionSummary.builder("a"));
DistributionSummary.Builder builder = DistributionSummary.builder("a");
builder.unwrap(io.micrometer.core.instrument.DistributionSummary.Builder.class)
.distributionStatisticExpiry(Duration.ofMinutes(10));
DistributionSummary summary = meterRegistry.getOrCreate(builder);
List.of(1D, 3D, 5D)
.forEach(summary::record);
io.micrometer.core.instrument.DistributionSummary mSummary =
summary.unwrap(io.micrometer.core.instrument.DistributionSummary.class);

mSummary.record(7D);

assertThat("Mean", summary.mean(), is(4D));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 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 @@ -42,9 +42,9 @@ void testUnwrap() {
long initialValue = 4L;
long incr = 2L;
AtomicLong value = new AtomicLong(initialValue);
Gauge g = meterRegistry.getOrCreate(Gauge.builder("a",
value,
v -> (double) v.get()));
Gauge.Builder<Double> builder = Gauge.builder("a", value, v -> (double) v.get());
builder.unwrap(io.micrometer.core.instrument.Gauge.Builder.class).strongReference(true);
Gauge<Double> g = meterRegistry.getOrCreate(builder);

io.micrometer.core.instrument.Gauge mGauge = g.unwrap(io.micrometer.core.instrument.Gauge.class);
assertThat("Initial value", mGauge.value(), is((double) initialValue));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 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 @@ -15,6 +15,7 @@
*/
package io.helidon.metrics.providers.micrometer;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

import io.helidon.metrics.api.MeterRegistry;
Expand Down Expand Up @@ -43,7 +44,11 @@ void testUnwrap() {
long initialValue = 0L;
long incrA = 2L;
long incrB = 7L;
Timer t = meterRegistry.getOrCreate(Timer.builder("a"));

Timer.Builder builder = Timer.builder("a");
io.micrometer.core.instrument.Timer.Builder mBuilder = builder.unwrap(io.micrometer.core.instrument.Timer.Builder.class);
mBuilder.distributionStatisticExpiry(Duration.ofMinutes(10));
Timer t = meterRegistry.getOrCreate(builder);

io.micrometer.core.instrument.Timer mTimer = t.unwrap(io.micrometer.core.instrument.Timer.class);
assertThat("Initial value", mTimer.count(), is(initialValue));
Expand Down