From eb553e2a3658581baeeef5836f64bdd9775c0efc Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Tue, 3 Sep 2024 10:34:56 +0200 Subject: [PATCH] Support using CXF TagsCustomizers and deprecate dynamic usage of MeterFilter #1492 --- .../ROOT/examples/hc5/application.properties | 3 +- .../quarkus-cxf-rt-features-metrics.adoc | 11 +++++ .../cxf/metrics/CxfMetricsConfig.java | 13 ++++++ .../cxf/metrics/MetricsCustomizer.java | 2 +- .../cxf/metrics/QuarkusCxfMetricsFeature.java | 28 +++++++++++-- .../cxf/hc5/it/FactorHeaderRequestFilter.java | 39 ------------------ ...a => HeaderToMetricsTagRequestFilter.java} | 18 +++++++- .../hc5/it/HeaderToMetricsTagsCustomizer.java | 29 +++++++++++++ .../cxf/hc5/it/MeterFilterProducer.java | 31 -------------- .../cxf/hc5/it/RequestScopedHeader.java | 18 -------- .../src/main/resources/application.properties | 3 +- .../io/quarkiverse/cxf/hc5/it/Hc5Test.java | 41 ++++++++++++------- 12 files changed, 126 insertions(+), 110 deletions(-) delete mode 100644 integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/FactorHeaderRequestFilter.java rename integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/{MyContainerRequestFilter.java => HeaderToMetricsTagRequestFilter.java} (52%) create mode 100644 integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/HeaderToMetricsTagsCustomizer.java delete mode 100644 integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/MeterFilterProducer.java delete mode 100644 integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/RequestScopedHeader.java diff --git a/docs/modules/ROOT/examples/hc5/application.properties b/docs/modules/ROOT/examples/hc5/application.properties index 9ee823a30..03e4351c9 100644 --- a/docs/modules/ROOT/examples/hc5/application.properties +++ b/docs/modules/ROOT/examples/hc5/application.properties @@ -6,7 +6,8 @@ quarkus.cxf.client.myCalculator.metrics.enabled = false quarkus.cxf.client.observableCalculator.wsdl = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.observableCalculator.client-endpoint-url = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService quarkus.cxf.client.observableCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService -quarkus.cxf.client.observableCalculator.features=io.quarkiverse.cxf.metrics.QuarkusCxfMetricsFeature +quarkus.cxf.client.observableCalculator.metrics.enabled = true +quarkus.cxf.metrics.tags-customizers = #headerToMetricsTagsCustomizer quarkus.cxf.client.contextPropagationCalculator.wsdl = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.contextPropagationCalculator.client-endpoint-url = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService diff --git a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf-rt-features-metrics.adoc b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf-rt-features-metrics.adoc index 8bb4b154f..17f5fd157 100644 --- a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf-rt-features-metrics.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf-rt-features-metrics.adoc @@ -145,6 +145,17 @@ be overridden per client or service endpoint using the `quarkus.cxf.client."clie *Environment variable*: `+++QUARKUS_CXF_METRICS_ENABLED_FOR+++` + *Since Quarkus CXF*: 2.7.0 +.<| [[quarkus-cxf_quarkus-cxf-metrics-tags-customizers]]`link:#quarkus-cxf_quarkus-cxf-metrics-tags-customizers[quarkus.cxf.metrics.tags-customizers]` +.<| List of ``string`` +.<| + +3+a|A list of xref:user-guide/configuration.adoc#beanRefs[references] to +`org.apache.cxf.metrics.micrometer.provider.TagsCustomizer` beans +that will be attached to the global metrics feature. + +*Environment variable*: `+++QUARKUS_CXF_METRICS_TAGS_CUSTOMIZERS+++` + +*Since Quarkus CXF*: 3.15.0 + .<| [[quarkus-cxf_quarkus-cxf-client-client-name-metrics-enabled]]`link:#quarkus-cxf_quarkus-cxf-client-client-name-metrics-enabled[quarkus.cxf.client."client-name".metrics.enabled]` .<| `boolean` .<| `true` diff --git a/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/CxfMetricsConfig.java b/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/CxfMetricsConfig.java index bbefc1414..e318eef5d 100644 --- a/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/CxfMetricsConfig.java +++ b/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/CxfMetricsConfig.java @@ -1,6 +1,8 @@ package io.quarkiverse.cxf.metrics; +import java.util.List; import java.util.Map; +import java.util.Optional; import io.quarkiverse.cxf.EnabledFor; import io.quarkiverse.cxf.EnabledFor.EnabledForConverter; @@ -124,5 +126,16 @@ public interface GlobalMetricsConfig { @WithDefault("both") @WithConverter(EnabledForConverter.class) EnabledFor enabledFor(); + + /** + * A list of xref:user-guide/configuration.adoc#beanRefs[references] to + * `org.apache.cxf.metrics.micrometer.provider.TagsCustomizer` beans + * that will be attached to the global metrics feature. + * + * @since 3.15.0 + * @asciidoclet + */ + public Optional> tagsCustomizers(); + } } diff --git a/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/MetricsCustomizer.java b/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/MetricsCustomizer.java index a88e40f51..3d5e82926 100644 --- a/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/MetricsCustomizer.java +++ b/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/MetricsCustomizer.java @@ -29,7 +29,7 @@ public class MetricsCustomizer implements ClientFactoryCustomizer, EndpointFacto @PostConstruct void init() { - this.feature = new QuarkusCxfMetricsFeature(); + this.feature = new QuarkusCxfMetricsFeature(config.metrics().tagsCustomizers()); } @Override diff --git a/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/QuarkusCxfMetricsFeature.java b/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/QuarkusCxfMetricsFeature.java index ba481cb1f..09ea63ad6 100644 --- a/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/QuarkusCxfMetricsFeature.java +++ b/extensions/features-metrics/runtime/src/main/java/io/quarkiverse/cxf/metrics/QuarkusCxfMetricsFeature.java @@ -1,7 +1,8 @@ package io.quarkiverse.cxf.metrics; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.apache.cxf.metrics.MetricsFeature; import org.apache.cxf.metrics.micrometer.MicrometerMetricsProperties; @@ -20,6 +21,7 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Metrics; +import io.quarkiverse.cxf.CXFRuntimeUtils; public class QuarkusCxfMetricsFeature extends MetricsFeature { @@ -29,14 +31,32 @@ public class QuarkusCxfMetricsFeature extends MetricsFeature { private static final TagsCustomizer faultsCustomizer = new JaxwsFaultCodeTagsCustomizer(jaxwsTags, new JaxwsFaultCodeProvider()); private static final TimedAnnotationProvider timedAnnotationProvider = new DefaultTimedAnnotationProvider(); - private static final List tagsCustomizers = Arrays.asList(operationsCustomizer, faultsCustomizer); private static final TagsProvider tagsProvider = new StandardTagsProvider(new DefaultExceptionClassProvider(), new StandardTags()); private static final MicrometerMetricsProperties micrometerMetricsProperties = new MicrometerMetricsProperties(); - public QuarkusCxfMetricsFeature() { - super(new MicrometerMetricsProvider(meterRegistry, tagsProvider, tagsCustomizers, timedAnnotationProvider, + public QuarkusCxfMetricsFeature(Optional> tagsCustomizersRefs) { + super(new MicrometerMetricsProvider(meterRegistry, tagsProvider, + joinTagsCustomizers(tagsCustomizersRefs, operationsCustomizer, faultsCustomizer), timedAnnotationProvider, micrometerMetricsProperties)); } + private static List joinTagsCustomizers(Optional> tagsCustomizersRefsOptional, + TagsCustomizer operationsCustomizer, + TagsCustomizer faultsCustomizer) { + List tagsCustomizersRefs = null; + if (tagsCustomizersRefsOptional.isPresent() && (tagsCustomizersRefs = tagsCustomizersRefsOptional.get()) != null + && !tagsCustomizersRefs.isEmpty()) { + final List result = new ArrayList<>(tagsCustomizersRefs.size() + 2); + result.add(operationsCustomizer); + result.add(faultsCustomizer); + for (String ref : tagsCustomizersRefs) { + result.add(CXFRuntimeUtils.getInstance(ref, true)); + } + return result; + } else { + return List.of(operationsCustomizer, faultsCustomizer); + } + } + } diff --git a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/FactorHeaderRequestFilter.java b/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/FactorHeaderRequestFilter.java deleted file mode 100644 index 6ce925a77..000000000 --- a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/FactorHeaderRequestFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.quarkiverse.cxf.hc5.it; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.context.RequestScoped; -import jakarta.inject.Inject; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.ContainerRequestFilter; -import jakarta.ws.rs.ext.Provider; - -@ApplicationScoped -@Provider -public class FactorHeaderRequestFilter implements ContainerRequestFilter { - @Inject - RequestScopedFactorHeader factorHeader; - - @Override - public void filter(ContainerRequestContext requestContext) { - final String rawValue = requestContext.getHeaderString(RequestScopedFactorHeader.header); - if (rawValue != null) { - factorHeader.setHeaderValue(Integer.parseInt(rawValue)); - } - } - - @RequestScoped - public static class RequestScopedFactorHeader { - public static final String header = "my-factor-header"; - - public int getHeaderValue() { - return headerValue; - } - - public void setHeaderValue(int headerValue) { - this.headerValue = headerValue; - } - - private int headerValue; - } - -} diff --git a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/MyContainerRequestFilter.java b/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/HeaderToMetricsTagRequestFilter.java similarity index 52% rename from integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/MyContainerRequestFilter.java rename to integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/HeaderToMetricsTagRequestFilter.java index 18a0c4659..6e94fa2ae 100644 --- a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/MyContainerRequestFilter.java +++ b/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/HeaderToMetricsTagRequestFilter.java @@ -1,6 +1,7 @@ package io.quarkiverse.cxf.hc5.it; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; @@ -8,7 +9,7 @@ @ApplicationScoped @Provider -public class MyContainerRequestFilter implements ContainerRequestFilter { +public class HeaderToMetricsTagRequestFilter implements ContainerRequestFilter { @Inject RequestScopedHeader requestScopedHeader; @@ -16,4 +17,19 @@ public class MyContainerRequestFilter implements ContainerRequestFilter { public void filter(ContainerRequestContext requestContext) { requestScopedHeader.setHeaderValue(requestContext.getHeaderString(RequestScopedHeader.header)); } + + @RequestScoped + public static class RequestScopedHeader { + public static final String header = "my-header"; + + public String getHeaderValue() { + return headerValue; + } + + public void setHeaderValue(String headerValue) { + this.headerValue = headerValue; + } + + private String headerValue; + } } diff --git a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/HeaderToMetricsTagsCustomizer.java b/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/HeaderToMetricsTagsCustomizer.java new file mode 100644 index 000000000..4830f6879 --- /dev/null +++ b/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/HeaderToMetricsTagsCustomizer.java @@ -0,0 +1,29 @@ +package io.quarkiverse.cxf.hc5.it; + +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.inject.Singleton; + +import org.apache.cxf.message.Exchange; +import org.apache.cxf.metrics.micrometer.provider.TagsCustomizer; + +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.quarkiverse.cxf.hc5.it.HeaderToMetricsTagRequestFilter.RequestScopedHeader; + +@Singleton +@Named("headerToMetricsTagsCustomizer") +public class HeaderToMetricsTagsCustomizer implements TagsCustomizer { + + @Inject + RequestScopedHeader requestScopedHeader; + + @Override + public Iterable getAdditionalTags(Exchange ex, boolean client) { + final String val = requestScopedHeader.getHeaderValue(); + if (val != null) { + return Tags.of(Tag.of("my-header", val)); + } + return Tags.empty(); + } +} diff --git a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/MeterFilterProducer.java b/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/MeterFilterProducer.java deleted file mode 100644 index c45e7a914..000000000 --- a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/MeterFilterProducer.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.quarkiverse.cxf.hc5.it; - -import jakarta.enterprise.inject.Produces; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -import io.micrometer.core.instrument.Meter; -import io.micrometer.core.instrument.Tag; -import io.micrometer.core.instrument.config.MeterFilter; - -@Singleton -public class MeterFilterProducer { - - @Inject - RequestScopedHeader requestScopedHeader; - - @Produces - @Singleton - public MeterFilter addTags() { - return new MeterFilter() { - @Override - public Meter.Id map(Meter.Id id) { - if (id.getName().startsWith("http.client") || id.getName().startsWith("cxf.client")) { - return id.withTag(Tag.of("my-header", requestScopedHeader.getHeaderValue())); - } else { - return id; - } - } - }; - } -} diff --git a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/RequestScopedHeader.java b/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/RequestScopedHeader.java deleted file mode 100644 index f7c01e7d8..000000000 --- a/integration-tests/hc5/src/main/java/io/quarkiverse/cxf/hc5/it/RequestScopedHeader.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.quarkiverse.cxf.hc5.it; - -import jakarta.enterprise.context.RequestScoped; - -@RequestScoped -public class RequestScopedHeader { - public static final String header = "my-header"; - - public String getHeaderValue() { - return headerValue; - } - - public void setHeaderValue(String headerValue) { - this.headerValue = headerValue; - } - - private String headerValue; -} diff --git a/integration-tests/hc5/src/main/resources/application.properties b/integration-tests/hc5/src/main/resources/application.properties index 9ee823a30..03e4351c9 100644 --- a/integration-tests/hc5/src/main/resources/application.properties +++ b/integration-tests/hc5/src/main/resources/application.properties @@ -6,7 +6,8 @@ quarkus.cxf.client.myCalculator.metrics.enabled = false quarkus.cxf.client.observableCalculator.wsdl = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.observableCalculator.client-endpoint-url = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService quarkus.cxf.client.observableCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService -quarkus.cxf.client.observableCalculator.features=io.quarkiverse.cxf.metrics.QuarkusCxfMetricsFeature +quarkus.cxf.client.observableCalculator.metrics.enabled = true +quarkus.cxf.metrics.tags-customizers = #headerToMetricsTagsCustomizer quarkus.cxf.client.contextPropagationCalculator.wsdl = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.contextPropagationCalculator.client-endpoint-url = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService diff --git a/integration-tests/hc5/src/test/java/io/quarkiverse/cxf/hc5/it/Hc5Test.java b/integration-tests/hc5/src/test/java/io/quarkiverse/cxf/hc5/it/Hc5Test.java index ce30afb48..5a713f56b 100644 --- a/integration-tests/hc5/src/test/java/io/quarkiverse/cxf/hc5/it/Hc5Test.java +++ b/integration-tests/hc5/src/test/java/io/quarkiverse/cxf/hc5/it/Hc5Test.java @@ -18,6 +18,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import io.quarkiverse.cxf.hc5.it.HeaderToMetricsTagRequestFilter.RequestScopedHeader; import io.quarkiverse.cxf.hc5.it.MultiplyingAddInterceptor.RequestScopedFactorHeader; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; @@ -29,7 +30,7 @@ class Hc5Test { @ParameterizedTest - @ValueSource(strings = { "sync", "async", "sync-observable", "async-observable" }) + @ValueSource(strings = { "sync", "async" }) void add(String syncMode) { RestAssured.given() .header(RequestScopedHeader.header, syncMode + "-header-value") @@ -40,21 +41,33 @@ void add(String syncMode) { .statusCode(200) .body(is("11")); - if (syncMode.endsWith("-observable")) { - /* Make sure that the tagging done in MeterFilterProducer actually works */ + } - final Config config = ConfigProvider.getConfig(); - final String baseUri = config.getValue("cxf.it.calculator.baseUri", String.class); - final Map metrics = getMetrics(); + @ParameterizedTest + @ValueSource(strings = { "sync-observable", "async-observable" }) + void addObservable(String syncMode) { + RestAssured.given() + .header(RequestScopedHeader.header, syncMode + "-header-value") + .queryParam("a", 7) + .queryParam("b", 4) + .get("/hc5/add-" + syncMode) + .then() + .statusCode(200) + .body(is("11")); - @SuppressWarnings("unchecked") - Map clientRequests = (Map) metrics.get("cxf.client.requests"); - Assertions.assertThat(clientRequests).isNotNull(); - String key = "count;exception=None;faultCode=None;method=POST;my-header=" + syncMode - + "-header-value;operation=add;outcome=SUCCESS;status=200;uri=" - + baseUri + "/calculator-ws/CalculatorService"; - Assertions.assertThat((Integer) clientRequests.get(key)).isGreaterThan(0); - } + /* Make sure that the tagging done in MeterFilterProducer actually works */ + + final Config config = ConfigProvider.getConfig(); + final String baseUri = config.getValue("cxf.it.calculator.baseUri", String.class); + final Map metrics = getMetrics(); + + @SuppressWarnings("unchecked") + Map clientRequests = (Map) metrics.get("cxf.client.requests"); + Assertions.assertThat(clientRequests).isNotNull(); + String key = "count;exception=None;faultCode=None;method=POST;my-header=" + syncMode + + "-header-value;operation=add;outcome=SUCCESS;status=200;uri=" + + baseUri + "/calculator-ws/CalculatorService"; + Assertions.assertThat((Integer) clientRequests.get(key)).isGreaterThan(0); } @ParameterizedTest