From b02a537e2226d96736cfde3d8ad426d32c7b3324 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 26 Jan 2024 12:05:08 -0600 Subject: [PATCH] Remove clear-out of registries from extension; do it in TCK-only shutdown observer; update doc (#8292) Signed-off-by: Tim Quinn --- .../src/main/asciidoc/mp/metrics/metrics.adoc | 34 +++++++++++++++++++ .../metrics/MetricsCdiExtension.java | 5 +-- .../microprofile/metrics/RegistryFactory.java | 12 ++++++- .../metrics/tck/MetricsTckCdiExtension.java | 20 ++++++++++- 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/docs/src/main/asciidoc/mp/metrics/metrics.adoc b/docs/src/main/asciidoc/mp/metrics/metrics.adoc index 46046db683b..555afb10717 100644 --- a/docs/src/main/asciidoc/mp/metrics/metrics.adoc +++ b/docs/src/main/asciidoc/mp/metrics/metrics.adoc @@ -109,6 +109,12 @@ Helidon automatically looks up the metric referenced from any injection site and include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=metric-registry-api] +=== Working with Metrics in CDI Extensions +You can work with metrics inside your own CDI extensions, but be careful to do so at the correct point in the CDI lifecycle. +Configuration can influence how the metrics system behaves, as the <> section below explains. +Your code should work with metrics only after the Helidon metrics system has initialized itself using configuration. +One way to accomplish this is to deal with metrics in a method that observes the Helidon `RuntimeStart` CDI event, which the xref:extension_example[extension example below] illustrates. + // Here's Configuration. include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-intro] @@ -574,6 +580,34 @@ curl -H "Accept: application/json" 'http://localhost:8080/metrics?scope=applica ---- <1> The application has been running for 23 seconds. +[[extension_example]] +==== Working with Metrics in CDI Extensions +You can work with metrics from your own CDI extension by observing the `RuntimeStart` event. +[source,java] +.CDI Extension that works correctly with metrics +---- +import io.helidon.microprofile.cdi.RuntimeStart; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.Extension; +import org.eclipse.microprofile.metrics.Counter; +import org.eclipse.microprofile.metrics.MetricRegistry; +public class MyExtension implements Extension { + void startup(@Observes @RuntimeStart Object event, // <1> + MetricRegistry metricRegistry) { // <2> + metricRegistry.counter("myCounter"); // <3> + } +} +---- +<1> Declares that your observer method responds to the `RuntimeStart` event. By this time, Helidon has initialized the metrics system. +<2> Injects a `MetricRegistry` (the application registry by default). +<3> Uses the injected registry to register a metric (a counter in this case). +[NOTE] +==== +Helidon does not prevent you from working with metrics earlier than the `RuntimeStart` event, but, if you do so, then Helidon might ignore certain configuration settings that would otherwise control how metrics behaves. +Instead, consider writing your extension to use earlier lifecycle events (such as `ProcessAnnotatedType`) to gather and store information about metrics that you want to register. +Then your extension's `RuntimeStart` observer method would use that stored information to register the metrics you need. +==== + // Config examples include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-examples] diff --git a/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java b/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java index 60150aa312a..2c40ce15eca 100644 --- a/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java +++ b/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/MetricsCdiExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -320,9 +320,6 @@ public void registerService(@Observes @Priority(LIBRARY_BEFORE + 10) @Initialize // this needs to be done early on, so the registry is configured before accessed MetricsObserver observer = configure(); - // Initialize our implementation - RegistryProducer.clearApplicationRegistry(); - registerMetricsForAnnotatedSites(); registerAnnotatedGauges(bm); registerRestRequestMetrics(); diff --git a/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/RegistryFactory.java b/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/RegistryFactory.java index 6ee95053c3c..8ea05087dcd 100644 --- a/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/RegistryFactory.java +++ b/microprofile/metrics/src/main/java/io/helidon/microprofile/metrics/RegistryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -18,6 +18,7 @@ import java.lang.System.Logger.Level; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -120,6 +121,15 @@ public MetricRegistry getRegistry(String scope) { return registry(scope); } + /** + * Report the scopes of all existing registries. + * + * @return set of scope names + */ + public Set scopes() { + return Collections.unmodifiableSet(registries.keySet()); + } + Registry registry(String scope) { return accessMetricsSettings(() -> registries.computeIfAbsent(scope, s -> Registry.create(s, meterRegistry))); diff --git a/microprofile/tests/tck/tck-metrics/src/test/java/io/helidon/microprofile/metrics/tck/MetricsTckCdiExtension.java b/microprofile/tests/tck/tck-metrics/src/test/java/io/helidon/microprofile/metrics/tck/MetricsTckCdiExtension.java index 381c02b800c..8e04f0dabbe 100644 --- a/microprofile/tests/tck/tck-metrics/src/test/java/io/helidon/microprofile/metrics/tck/MetricsTckCdiExtension.java +++ b/microprofile/tests/tck/tck-metrics/src/test/java/io/helidon/microprofile/metrics/tck/MetricsTckCdiExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023 Oracle and/or its affiliates. + * Copyright (c) 2020, 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. @@ -15,11 +15,19 @@ */ package io.helidon.microprofile.metrics.tck; +import java.util.Set; + +import io.helidon.microprofile.metrics.RegistryFactory; import io.helidon.microprofile.server.CatchAllExceptionMapper; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; +import jakarta.enterprise.inject.spi.BeforeShutdown; import jakarta.enterprise.inject.spi.Extension; +import org.eclipse.microprofile.metrics.MetricRegistry; public class MetricsTckCdiExtension implements Extension { @@ -27,4 +35,14 @@ void before(@Observes BeforeBeanDiscovery discovery) { discovery.addAnnotatedType(ArrayParamConverterProvider.class, ArrayParamConverterProvider.class.getSimpleName()); discovery.addAnnotatedType(CatchAllExceptionMapper.class, CatchAllExceptionMapper.class.getSimpleName()); } + + void clear(@Observes BeforeShutdown shutdown) { + // Erase the metric registries for all scopes so they are clear at the start of the next TCK test. + + RegistryFactory.getInstance().scopes().stream() + .map(RegistryFactory.getInstance()::getRegistry) + .forEach(metricRegistry -> + metricRegistry.getNames().forEach(metricRegistry::remove) + ); + } }