-
Notifications
You must be signed in to change notification settings - Fork 1
fix: resolve NoClassDefFoundError when Micrometer is absent (issue #94) #95
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
Changes from all commits
928569d
f934a36
23e6a3b
63d1c10
6a7a40d
a8d89bf
445374f
0b8f213
b002029
b22f75c
db09a03
8974419
62a8855
077f300
dfafe6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,14 @@ | ||
| package com.digitalsanctuary.cf.turnstile.config; | ||
|
|
||
| import org.springframework.boot.micrometer.metrics.autoconfigure.MeterRegistryCustomizer; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
|
|
||
| import com.digitalsanctuary.cf.turnstile.metrics.MicrometerTurnstileMetrics; | ||
| import com.digitalsanctuary.cf.turnstile.metrics.TurnstileMetrics; | ||
| import io.micrometer.core.instrument.MeterRegistry; | ||
| import io.micrometer.core.instrument.Tag; | ||
| import io.micrometer.core.instrument.config.MeterFilter; | ||
|
|
@@ -14,10 +17,7 @@ | |
|
|
||
| /** | ||
| * Configuration for Turnstile metrics and monitoring. | ||
| * <p> | ||
| * This class configures the metrics for Cloudflare Turnstile service. It sets up common tags and filters for all metrics related to the Turnstile | ||
| * service. The metrics are only configured if micrometer-core is on the classpath and metrics are enabled in the configuration. | ||
| * </p> | ||
| * Only loaded when Micrometer is on the classpath and metrics are enabled. | ||
| */ | ||
| @Slf4j | ||
| @Configuration | ||
|
|
@@ -26,20 +26,28 @@ | |
| public class TurnstileMetricsConfig { | ||
|
|
||
| /** | ||
| * Customizes the meter registry for Turnstile metrics. | ||
| * <p> | ||
| * Adds a common tag 'component:turnstile' to all Turnstile-related metrics and configures a prefix for all Turnstile metrics. | ||
| * </p> | ||
| * Registers the Micrometer-backed TurnstileMetrics bean. | ||
| * | ||
| * @param registry the MeterRegistry to use for metrics | ||
| * @return a MicrometerTurnstileMetrics instance | ||
| */ | ||
| @Bean | ||
| @ConditionalOnBean(MeterRegistry.class) | ||
| @ConditionalOnMissingBean(TurnstileMetrics.class) | ||
| public TurnstileMetrics micrometerTurnstileMetrics(MeterRegistry registry) { | ||
| return new MicrometerTurnstileMetrics(registry); | ||
| } | ||
|
Comment on lines
+34
to
+39
|
||
|
|
||
| /** | ||
| * Customizes the meter registry with Turnstile-specific tags and filters. | ||
| * | ||
| * @return a MeterRegistryCustomizer to customize the MeterRegistry | ||
| * @return a MeterRegistryCustomizer for the MeterRegistry | ||
| */ | ||
| @Bean | ||
| public MeterRegistryCustomizer<MeterRegistry> turnstileMeterRegistryCustomizer() { | ||
| log.info("Configuring Turnstile metrics"); | ||
| return registry -> { | ||
| // Add a common tag to all turnstile metrics | ||
| registry.config().meterFilter(MeterFilter.acceptNameStartsWith("turnstile")) | ||
| .meterFilter(MeterFilter.commonTags(Collections.singletonList(Tag.of("component", "turnstile")))); | ||
| }; | ||
| return registry -> registry.config() | ||
| .meterFilter(MeterFilter.acceptNameStartsWith("turnstile")) | ||
| .meterFilter(MeterFilter.commonTags(Collections.singletonList(Tag.of("component", "turnstile")))); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| package com.digitalsanctuary.cf.turnstile.metrics; | ||
|
|
||
| import com.digitalsanctuary.cf.turnstile.dto.ValidationResult.ValidationResultType; | ||
| import io.micrometer.core.instrument.Counter; | ||
| import io.micrometer.core.instrument.MeterRegistry; | ||
| import io.micrometer.core.instrument.Timer; | ||
| import java.util.Objects; | ||
| import java.util.concurrent.TimeUnit; | ||
| import lombok.extern.slf4j.Slf4j; | ||
|
|
||
| /** | ||
| * Micrometer-backed implementation of {@link TurnstileMetrics}. | ||
| * <p> | ||
| * This class is only instantiated when Micrometer is present on the classpath, via | ||
| * {@code TurnstileMetricsConfig} which is guarded by {@code @ConditionalOnClass(MeterRegistry.class)}. | ||
| * </p> | ||
| * <p> | ||
| * All eight meters ({@code errorCounter} is the aggregate; the four type-specific counters are | ||
| * sub-categories whose sum equals the aggregate) are eagerly registered at construction time so | ||
| * they appear in monitoring dashboards before the first validation event occurs. A single instance | ||
| * should be registered per application context to avoid duplicate meter registration errors. | ||
| * </p> | ||
| * | ||
| * @see NoOpTurnstileMetrics | ||
| */ | ||
| @Slf4j | ||
| public class MicrometerTurnstileMetrics implements TurnstileMetrics { | ||
|
|
||
| private final Counter validationCounter; | ||
| private final Counter successCounter; | ||
| private final Counter errorCounter; | ||
| private final Counter networkErrorCounter; | ||
| private final Counter configErrorCounter; | ||
| private final Counter validationErrorCounter; | ||
| private final Counter inputErrorCounter; | ||
| private final Timer responseTimer; | ||
|
|
||
| /** | ||
| * Creates a {@code MicrometerTurnstileMetrics} instance and eagerly registers all Turnstile | ||
| * meters with the provided registry. The registered meters are: | ||
| * <ul> | ||
| * <li>{@code turnstile.validation.requests} — total attempts</li> | ||
| * <li>{@code turnstile.validation.success} — successful validations</li> | ||
| * <li>{@code turnstile.validation.errors} — all errors (aggregate)</li> | ||
| * <li>{@code turnstile.validation.errors.network} — network errors</li> | ||
| * <li>{@code turnstile.validation.errors.config} — configuration errors</li> | ||
| * <li>{@code turnstile.validation.errors.token} — invalid token errors</li> | ||
| * <li>{@code turnstile.validation.errors.input} — input validation errors</li> | ||
| * <li>{@code turnstile.validation.response.time} — response time timer</li> | ||
| * </ul> | ||
| * | ||
| * @param registry the Micrometer {@link MeterRegistry} to register meters with; must not be null | ||
| */ | ||
| public MicrometerTurnstileMetrics(MeterRegistry registry) { | ||
| Objects.requireNonNull(registry, "registry must not be null"); | ||
| log.info("Initializing Turnstile metrics with MeterRegistry"); | ||
| validationCounter = Counter.builder("turnstile.validation.requests") | ||
| .description("Total number of Turnstile validation requests").register(registry); | ||
| successCounter = Counter.builder("turnstile.validation.success") | ||
| .description("Number of successful Turnstile validations").register(registry); | ||
| errorCounter = Counter.builder("turnstile.validation.errors") | ||
| .description("Number of failed Turnstile validations").register(registry); | ||
| networkErrorCounter = Counter.builder("turnstile.validation.errors.network") | ||
| .description("Number of Turnstile validation network errors").register(registry); | ||
| configErrorCounter = Counter.builder("turnstile.validation.errors.config") | ||
| .description("Number of Turnstile validation configuration errors").register(registry); | ||
| validationErrorCounter = Counter.builder("turnstile.validation.errors.token") | ||
| .description("Number of Turnstile validation token errors").register(registry); | ||
| inputErrorCounter = Counter.builder("turnstile.validation.errors.input") | ||
| .description("Number of Turnstile validation input errors").register(registry); | ||
| responseTimer = Timer.builder("turnstile.validation.response.time") | ||
| .description("Response time for Turnstile validation requests").register(registry); | ||
| } | ||
|
|
||
| @Override | ||
| public void recordValidation() { | ||
| validationCounter.increment(); | ||
| } | ||
|
|
||
| @Override | ||
| public void recordSuccess() { | ||
| successCounter.increment(); | ||
| } | ||
|
|
||
| @Override | ||
| public void recordError(ValidationResultType type) { | ||
| errorCounter.increment(); | ||
| switch (type) { | ||
| case NETWORK_ERROR -> networkErrorCounter.increment(); | ||
| case CONFIGURATION_ERROR -> configErrorCounter.increment(); | ||
| case INVALID_TOKEN -> validationErrorCounter.increment(); | ||
| case INPUT_ERROR -> inputErrorCounter.increment(); | ||
| default -> { } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void recordResponseTime(long milliseconds) { | ||
| responseTimer.record(milliseconds, TimeUnit.MILLISECONDS); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.digitalsanctuary.cf.turnstile.metrics; | ||
|
|
||
| import com.digitalsanctuary.cf.turnstile.dto.ValidationResult.ValidationResultType; | ||
|
|
||
| /** | ||
| * No-op implementation of TurnstileMetrics used when Micrometer is not on the classpath. | ||
| */ | ||
| public class NoOpTurnstileMetrics implements TurnstileMetrics { | ||
|
|
||
| @Override | ||
| public void recordValidation() { // no-op | ||
| } | ||
|
|
||
| @Override | ||
| public void recordSuccess() { // no-op | ||
| } | ||
|
|
||
| @Override | ||
| public void recordError(ValidationResultType type) { // no-op | ||
| } | ||
|
|
||
| @Override | ||
| public void recordResponseTime(long milliseconds) { // no-op | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description states that
MicrometerTurnstileMetricsis “the only class in the library that references Micrometer types”, butTurnstileMetricsConfigalso imports and references Micrometer types (MeterRegistry,Tag,MeterFilter). Either update the PR description to match the implementation, or move Micrometer-specific registry customization into the Micrometer-only section (so the claim remains accurate).