From 621ec925863e49c8b77e8e9a2953ff9d654222aa Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Tue, 8 Nov 2022 12:28:43 +0100 Subject: [PATCH] Add Micrometer counter for job launches Resolves #4226 --- .../launch/support/SimpleJobLauncher.java | 21 +++++++++++++++++++ .../core/observability/BatchMetrics.java | 15 +++++++++++++ .../core/observability/BatchMetricsTests.java | 6 ++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java index b06f2ef23..71d6bf76f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java @@ -17,6 +17,9 @@ package org.springframework.batch.core.launch.support; import java.time.Duration; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -73,6 +76,10 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean { private TaskExecutor taskExecutor; + private MeterRegistry meterRegistry = Metrics.globalRegistry; + + private Counter jobLaunchCount; // NoopCounter is still incubating + /** * Run the provided job with the given {@link JobParameters}. The * {@link JobParameters} will be used to determine if this is an execution of an @@ -96,6 +103,9 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean { Assert.notNull(job, "The Job must not be null."); Assert.notNull(jobParameters, "The JobParameters must not be null."); + if (this.jobLaunchCount != null) { + this.jobLaunchCount.increment(); + } final JobExecution jobExecution; JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters); @@ -202,6 +212,16 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean { this.taskExecutor = taskExecutor; } + /** + * Set the meter registry to use for metrics. Defaults to + * {@link Metrics#globalRegistry}. + * @param meterRegistry the meter registry + * @since 5.0 + */ + public void setMeterRegistry(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + /** * Ensure the required dependencies of a {@link JobRepository} have been set. */ @@ -212,6 +232,7 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean { logger.info("No TaskExecutor has been set, defaulting to synchronous executor."); taskExecutor = new SyncTaskExecutor(); } + this.jobLaunchCount = BatchMetrics.createCounter(this.meterRegistry, "job.launch.count", "Job launch count"); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/observability/BatchMetrics.java b/spring-batch-core/src/main/java/org/springframework/batch/core/observability/BatchMetrics.java index f0a22a739..49c59005c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/observability/BatchMetrics.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/observability/BatchMetrics.java @@ -20,6 +20,7 @@ import java.time.LocalDateTime; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; @@ -71,6 +72,20 @@ public final class BatchMetrics { .register(meterRegistry); } + /** + * Create a {@link Counter}. + * @param meterRegistry the meter registry to use + * @param name of the counter. Will be prefixed with + * {@link BatchMetrics#METRICS_PREFIX}. + * @param description of the counter + * @param tags of the counter + * @return a new timer instance + */ + public static Counter createCounter(MeterRegistry meterRegistry, String name, String description, Tag... tags) { + return Counter.builder(METRICS_PREFIX + name).description(description).tags(Arrays.asList(tags)) + .register(meterRegistry); + } + /** * Create a new {@link Observation}. It's not started, you must explicitly call * {@link Observation#start()} to start it. diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/observability/BatchMetricsTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/observability/BatchMetricsTests.java index f90e8cb87..cef41c8d7 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/observability/BatchMetricsTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/observability/BatchMetricsTests.java @@ -45,7 +45,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.support.JdbcTransactionManager; import org.springframework.transaction.PlatformTransactionManager; @@ -60,7 +59,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; */ class BatchMetricsTests { - private static final int EXPECTED_SPRING_BATCH_METRICS = 10; + private static final int EXPECTED_SPRING_BATCH_METRICS = 11; @Test void testCalculateDuration() { @@ -149,6 +148,9 @@ class BatchMetricsTests { // Job metrics + assertDoesNotThrow(() -> Metrics.globalRegistry.get("spring.batch.job.launch.count").counter(), + "There should be a meter of type COUNTER named spring.batch.job.launch.count registered in the global registry"); + assertDoesNotThrow( () -> Metrics.globalRegistry.get("spring.batch.job").tag("spring.batch.job.name", "job") .tag("spring.batch.job.status", "COMPLETED").timer(),