From 06d6c70edbdc382814111ea2edaeaef54d965186 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Thu, 30 May 2024 15:54:33 +0200 Subject: [PATCH 1/2] feature: Provide custom MetricTags during runtime --- README.md | 14 ++++++++++++++ .../example-spring-boot-starter-web/build.gradle | 9 ++++++++- .../api/metrics/MetricTagProvider.java | 8 ++++++++ .../api/metrics/NullMetricTagProvider.java | 11 +++++++++++ .../core}/metrics/DefaultMetricsReporter.java | 16 +++++++++++----- .../autoconfigure/LibraryAutoConfiguration.java | 10 ++++++++-- 6 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/MetricTagProvider.java create mode 100644 openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/NullMetricTagProvider.java rename {openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api => openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core}/metrics/DefaultMetricsReporter.java (80%) diff --git a/README.md b/README.md index c490ede..00fe401 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,20 @@ public class ViolationExclusionsExample implements ViolationExclusions { } ``` +### Provide metric tags at runtime +Sometimes you want to generate your own metric tags based on the violation. +This can be achieved as demonstrated in the following snippet. + +```java +@Component +public class MetricTagProviderExample implements MetricTagProvider { + @Override + public List getTagsForViolation(OpenApiViolation violation) { + return List.of(); + } +} +``` + ## Examples Run examples with `./gradlew :examples:example-spring-boot-starter-web:bootRun` or `./gradlew :examples:example-spring-boot-starter-webflux:bootRun`. diff --git a/examples/example-spring-boot-starter-web/build.gradle b/examples/example-spring-boot-starter-web/build.gradle index 40a0093..480d533 100644 --- a/examples/example-spring-boot-starter-web/build.gradle +++ b/examples/example-spring-boot-starter-web/build.gradle @@ -9,7 +9,14 @@ plugins { dependencies { implementation project(':examples:examples-common') implementation project(':spring-boot-starter:spring-boot-starter-web') - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation('org.springframework.boot:spring-boot-starter-web') { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' + exclude group: 'io.undertow', module: 'undertow-websockets-jsr' + } + + implementation 'org.springframework.boot:spring-boot-starter-undertow' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.hibernate.validator:hibernate-validator' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation(libs.openapi.tools.jacksonDatabindNullable) diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/MetricTagProvider.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/MetricTagProvider.java new file mode 100644 index 0000000..2946f85 --- /dev/null +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/MetricTagProvider.java @@ -0,0 +1,8 @@ +package com.getyourguide.openapi.validation.api.metrics; + +import com.getyourguide.openapi.validation.api.model.OpenApiViolation; +import java.util.List; + +public interface MetricTagProvider { + List getTagsForViolation(OpenApiViolation violation); +} diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/NullMetricTagProvider.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/NullMetricTagProvider.java new file mode 100644 index 0000000..6796f79 --- /dev/null +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/NullMetricTagProvider.java @@ -0,0 +1,11 @@ +package com.getyourguide.openapi.validation.api.metrics; + +import com.getyourguide.openapi.validation.api.model.OpenApiViolation; +import java.util.List; + +public class NullMetricTagProvider implements MetricTagProvider { + @Override + public List getTagsForViolation(OpenApiViolation violation) { + return List.of(); + } +} diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/DefaultMetricsReporter.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/metrics/DefaultMetricsReporter.java similarity index 80% rename from openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/DefaultMetricsReporter.java rename to openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/metrics/DefaultMetricsReporter.java index ace9286..48b016c 100644 --- a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/metrics/DefaultMetricsReporter.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/metrics/DefaultMetricsReporter.java @@ -1,6 +1,9 @@ -package com.getyourguide.openapi.validation.api.metrics; +package com.getyourguide.openapi.validation.core.metrics; import com.getyourguide.openapi.validation.api.log.LogLevel; +import com.getyourguide.openapi.validation.api.metrics.MetricTag; +import com.getyourguide.openapi.validation.api.metrics.MetricTagProvider; +import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; import com.getyourguide.openapi.validation.api.metrics.client.MetricsClient; import com.getyourguide.openapi.validation.api.model.OpenApiViolation; import java.util.ArrayList; @@ -13,6 +16,7 @@ public class DefaultMetricsReporter implements MetricsReporter { private final MetricsClient metricsClient; + private final MetricTagProvider metricTagProvider; private final Configuration configuration; @Override @@ -49,7 +53,8 @@ private MetricTag[] createTagsForViolation(OpenApiViolation violation) { violation.getResponseStatus() .ifPresent(responseStatus -> tags.add(new MetricTag("status", responseStatus.toString()))); - addAdditionalTags(tags); + tags.addAll(getMetricTagsFromConfiguration()); + tags.addAll(metricTagProvider.getTagsForViolation(violation)); return tags.toArray(MetricTag[]::new); } @@ -64,15 +69,16 @@ private MetricTag[] createTagsForStartup( tags.add(new MetricTag("validation_enabled", String.valueOf(isValidationEnabled))); tags.add(new MetricTag("sample_rate", String.valueOf(sampleRate))); tags.add(new MetricTag("throttling", String.valueOf(validationReportThrottleWaitSeconds))); - addAdditionalTags(tags); + tags.addAll(getMetricTagsFromConfiguration()); return tags.toArray(MetricTag[]::new); } - private void addAdditionalTags(ArrayList tags) { + private List getMetricTagsFromConfiguration() { if (configuration.getMetricAdditionalTags() != null) { - tags.addAll(configuration.getMetricAdditionalTags()); + return configuration.getMetricAdditionalTags(); } + return List.of(); } @Builder diff --git a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java index b7ea612..29d8aa1 100644 --- a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java +++ b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java @@ -8,8 +8,9 @@ import com.getyourguide.openapi.validation.api.log.NoOpLoggerExtension; import com.getyourguide.openapi.validation.api.log.OpenApiViolationHandler; import com.getyourguide.openapi.validation.api.log.ViolationLogger; -import com.getyourguide.openapi.validation.api.metrics.DefaultMetricsReporter; +import com.getyourguide.openapi.validation.api.metrics.MetricTagProvider; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; +import com.getyourguide.openapi.validation.api.metrics.NullMetricTagProvider; import com.getyourguide.openapi.validation.api.metrics.client.MetricsClient; import com.getyourguide.openapi.validation.api.metrics.client.NoOpMetricsClient; import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; @@ -22,6 +23,7 @@ import com.getyourguide.openapi.validation.core.log.ExclusionsOpenApiViolationHandler; import com.getyourguide.openapi.validation.core.log.ThrottlingOpenApiViolationHandler; import com.getyourguide.openapi.validation.core.mapper.ValidationReportToOpenApiViolationsMapper; +import com.getyourguide.openapi.validation.core.metrics.DefaultMetricsReporter; import java.util.Optional; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -49,12 +51,16 @@ public ViolationLogger violationLogger(Optional loggerExtension @Bean @ConditionalOnMissingBean - public MetricsReporter metricsReporter(Optional metricsClient) { + public MetricsReporter metricsReporter( + Optional metricsClient, + Optional metricTagProvider + ) { var metricName = properties.getValidationReportMetricName() != null ? properties.getValidationReportMetricName() : DEFAULT_METRIC_NAME; return new DefaultMetricsReporter( metricsClient.orElseGet(NoOpMetricsClient::new), + metricTagProvider.orElseGet(NullMetricTagProvider::new), DefaultMetricsReporter.Configuration.builder() .metricName(metricName) .metricAdditionalTags(properties.getValidationReportMetricAdditionalTags()) From f3197b2a1915fc7be106a82f2719c6b4b6c70095 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Thu, 30 May 2024 16:01:43 +0200 Subject: [PATCH 2/2] update example in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00fe401..60cb14e 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ This can be achieved as demonstrated in the following snippet. public class MetricTagProviderExample implements MetricTagProvider { @Override public List getTagsForViolation(OpenApiViolation violation) { - return List.of(); + return List.of(new MetricTag("rule", violation.getRule())); } } ```