Skip to content

Commit ef1de53

Browse files
committed
Remove usage of Micrometer's global static meter registry
This commit replaces the usage of the global static MeterRegistry of Micrometer with a configurable ObservationRegistry. Resolves #4968
1 parent e74e928 commit ef1de53

32 files changed

+467
-1061
lines changed

spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchObservabilityBeanPostProcessor.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 the original author or authors.
2+
* Copyright 2022-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020
import org.apache.commons.logging.LogFactory;
2121

2222
import org.springframework.batch.core.job.AbstractJob;
23+
import org.springframework.batch.core.launch.support.TaskExecutorJobOperator;
2324
import org.springframework.batch.core.step.AbstractStep;
2425
import org.springframework.beans.BeansException;
2526
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -28,8 +29,8 @@
2829
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2930

3031
/**
31-
* Bean post processor that configures observable batch artifacts (jobs and steps) with
32-
* Micrometer's observation registry.
32+
* Bean post processor that configures observable batch artifacts (typically jobs and
33+
* steps) with a Micrometer's observation registry.
3334
*
3435
* @author Mahmoud Ben Hassine
3536
* @since 5.0
@@ -48,14 +49,18 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
4849
@Override
4950
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
5051
try {
51-
if (bean instanceof AbstractJob || bean instanceof AbstractStep) {
52+
if (bean instanceof AbstractJob || bean instanceof AbstractStep
53+
|| bean instanceof TaskExecutorJobOperator) {
5254
ObservationRegistry observationRegistry = this.beanFactory.getBean(ObservationRegistry.class);
5355
if (bean instanceof AbstractJob job) {
5456
job.setObservationRegistry(observationRegistry);
5557
}
5658
if (bean instanceof AbstractStep step) {
5759
step.setObservationRegistry(observationRegistry);
5860
}
61+
if (bean instanceof TaskExecutorJobOperator operator) {
62+
operator.setObservationRegistry(observationRegistry);
63+
}
5964
}
6065
}
6166
catch (NoSuchBeanDefinitionException e) {

spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchRegistrar.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ private void registerJobOperator(BeanDefinitionRegistry registry, EnableBatchPro
218218
beanDefinitionBuilder.addPropertyReference("jobRegistry", jobRegistryRef);
219219
}
220220

221+
String observationRegistryRef = batchAnnotation.observationRegistryRef();
222+
if (registry.containsBeanDefinition(observationRegistryRef)) {
223+
beanDefinitionBuilder.addPropertyReference("observationRegistry", observationRegistryRef);
224+
}
225+
221226
String transactionManagerRef = batchAnnotation.transactionManagerRef();
222227
if (registry.containsBeanDefinition(transactionManagerRef)) {
223228
beanDefinitionBuilder.addPropertyReference("transactionManager", transactionManagerRef);

spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@
178178
*/
179179
String jobRegistryRef() default "jobRegistry";
180180

181+
/**
182+
* Set the observation registry to use in batch artifacts.
183+
* @return the bean name of the observation registry to use. Defaults to
184+
* {@literal observationRegistry}
185+
*/
186+
String observationRegistryRef() default "observationRegistry";
187+
181188
/**
182189
* Set the transaction manager to use in the job operator.
183190
* @return the bean name of the transaction manager to use. Defaults to

spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultBatchConfiguration.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
*/
1616
package org.springframework.batch.core.configuration.support;
1717

18+
import io.micrometer.observation.ObservationRegistry;
19+
1820
import org.springframework.batch.core.configuration.DuplicateJobException;
21+
import org.springframework.batch.core.configuration.annotation.BatchObservabilityBeanPostProcessor;
1922
import org.springframework.batch.core.job.Job;
2023
import org.springframework.batch.core.configuration.BatchConfigurationException;
2124
import org.springframework.batch.core.configuration.JobRegistry;
@@ -74,7 +77,7 @@
7477
* @since 5.0
7578
*/
7679
@Configuration(proxyBeanMethods = false)
77-
@Import(ScopeConfiguration.class)
80+
@Import({ ScopeConfiguration.class, BatchObservabilityBeanPostProcessor.class })
7881
public class DefaultBatchConfiguration implements ApplicationContextAware {
7982

8083
protected ApplicationContext applicationContext;
@@ -95,6 +98,7 @@ public JobOperator jobOperator(JobRepository jobRepository) throws BatchConfigur
9598
jobOperatorFactoryBean.setJobRepository(jobRepository);
9699
jobOperatorFactoryBean.setJobRegistry(getJobRegistry());
97100
jobOperatorFactoryBean.setTransactionManager(getTransactionManager());
101+
jobOperatorFactoryBean.setObservationRegistry(getObservationRegistry());
98102
jobOperatorFactoryBean.setJobParametersConverter(getJobParametersConverter());
99103
jobOperatorFactoryBean.setTaskExecutor(getTaskExecutor());
100104
try {
@@ -119,6 +123,16 @@ protected JobRegistry getJobRegistry() {
119123
return jobRegistry;
120124
}
121125

126+
/**
127+
* Return the {@link ObservationRegistry} to use for the job operator. Defaults to
128+
* {@link ObservationRegistry#NOOP}.
129+
* @return The ObservationRegistry to use for the job operator
130+
* @since 6.0
131+
*/
132+
protected ObservationRegistry getObservationRegistry() {
133+
return ObservationRegistry.NOOP;
134+
}
135+
122136
/**
123137
* Return the transaction manager to use for the job operator. Defaults to
124138
* {@link ResourcelessTransactionManager}.

spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@
2121
import java.util.List;
2222
import java.util.stream.Collectors;
2323

24-
import io.micrometer.core.instrument.LongTaskTimer;
25-
import io.micrometer.core.instrument.MeterRegistry;
26-
import io.micrometer.core.instrument.Metrics;
27-
import io.micrometer.core.instrument.Tag;
2824
import io.micrometer.observation.Observation;
2925
import io.micrometer.observation.ObservationRegistry;
3026
import org.apache.commons.logging.Log;
@@ -38,16 +34,12 @@
3834
import org.springframework.batch.core.listener.JobExecutionListener;
3935
import org.springframework.batch.core.SpringBatchVersion;
4036
import org.springframework.batch.core.observability.jfr.events.job.JobExecutionEvent;
37+
import org.springframework.batch.core.observability.micrometer.MicrometerMetrics;
4138
import org.springframework.batch.core.step.Step;
4239
import org.springframework.batch.core.step.StepExecution;
4340
import org.springframework.batch.core.launch.NoSuchJobException;
4441
import org.springframework.batch.core.launch.support.ExitCodeMapper;
4542
import org.springframework.batch.core.listener.CompositeJobExecutionListener;
46-
import org.springframework.batch.core.observability.BatchJobContext;
47-
import org.springframework.batch.core.observability.BatchJobObservation;
48-
import org.springframework.batch.core.observability.BatchJobObservationConvention;
49-
import org.springframework.batch.core.observability.BatchMetrics;
50-
import org.springframework.batch.core.observability.DefaultBatchJobObservationConvention;
5143
import org.springframework.batch.core.repository.JobRepository;
5244
import org.springframework.batch.core.repository.JobRestartException;
5345
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
@@ -87,11 +79,7 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
8779

8880
private StepHandler stepHandler;
8981

90-
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
91-
92-
private MeterRegistry meterRegistry = Metrics.globalRegistry;
93-
94-
private BatchJobObservationConvention observationConvention = new DefaultBatchJobObservationConvention();
82+
private ObservationRegistry observationRegistry;
9583

9684
/**
9785
* Default constructor.
@@ -126,6 +114,10 @@ public void setJobParametersValidator(JobParametersValidator jobParametersValida
126114
@Override
127115
public void afterPropertiesSet() throws Exception {
128116
Assert.state(jobRepository != null, "JobRepository must be set");
117+
if (this.observationRegistry == null) {
118+
logger.info("No ObservationRegistry has been set, defaulting to ObservationRegistry NOOP");
119+
this.observationRegistry = ObservationRegistry.NOOP;
120+
}
129121
}
130122

131123
/**
@@ -281,16 +273,10 @@ public final void execute(JobExecution execution) {
281273
JobExecutionEvent jobExecutionEvent = new JobExecutionEvent(execution.getJobInstance().getJobName(),
282274
execution.getJobInstance().getId(), execution.getId());
283275
jobExecutionEvent.begin();
284-
String activeJobMeterName = "job.active";
285-
LongTaskTimer longTaskTimer = BatchMetrics.createLongTaskTimer(this.meterRegistry, activeJobMeterName,
286-
"Active jobs", Tag.of(BatchMetrics.METRICS_PREFIX + activeJobMeterName + ".name",
287-
execution.getJobInstance().getJobName()));
288-
LongTaskTimer.Sample longTaskTimerSample = longTaskTimer.start();
289-
Observation observation = BatchMetrics
290-
.createObservation(BatchJobObservation.BATCH_JOB_OBSERVATION.getName(), new BatchJobContext(execution),
291-
this.observationRegistry)
292-
.contextualName(execution.getJobInstance().getJobName())
293-
.observationConvention(this.observationConvention)
276+
Observation observation = MicrometerMetrics.createObservation("spring.batch.job", this.observationRegistry)
277+
.highCardinalityKeyValue("spring.batch.job.instanceId", execution.getJobInstance().getId().toString())
278+
.highCardinalityKeyValue("spring.batch.job.executionId", execution.getId().toString())
279+
.lowCardinalityKeyValue("spring.batch.job.name", execution.getJobInstance().getJobName())
294280
.start();
295281
try (Observation.Scope scope = observation.openScope()) {
296282

@@ -355,7 +341,6 @@ public final void execute(JobExecution execution) {
355341
stopObservation(execution, observation);
356342
jobExecutionEvent.exitStatus = execution.getExitStatus().getExitCode();
357343
jobExecutionEvent.commit();
358-
longTaskTimerSample.stop();
359344
execution.setEndTime(LocalDateTime.now());
360345

361346
try {
@@ -380,6 +365,7 @@ private void stopObservation(JobExecution execution, Observation observation) {
380365
if (!throwables.isEmpty()) {
381366
observation.error(mergedThrowables(throwables));
382367
}
368+
observation.lowCardinalityKeyValue("spring.batch.job.status", execution.getExitStatus().getExitCode());
383369
observation.stop();
384370
}
385371

@@ -437,18 +423,10 @@ private void updateStatus(JobExecution jobExecution, BatchStatus status) {
437423
jobRepository.update(jobExecution);
438424
}
439425

440-
public void setObservationConvention(BatchJobObservationConvention observationConvention) {
441-
this.observationConvention = observationConvention;
442-
}
443-
444426
public void setObservationRegistry(ObservationRegistry observationRegistry) {
445427
this.observationRegistry = observationRegistry;
446428
}
447429

448-
public void setMeterRegistry(MeterRegistry meterRegistry) {
449-
this.meterRegistry = meterRegistry;
450-
}
451-
452430
@Override
453431
public String toString() {
454432
return ClassUtils.getShortName(getClass()) + ": [name=" + name + "]";

spring-batch-core/src/main/java/org/springframework/batch/core/job/builder/JobBuilderHelper.java

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.util.List;
2323
import java.util.Set;
2424

25-
import io.micrometer.core.instrument.MeterRegistry;
2625
import io.micrometer.observation.ObservationRegistry;
2726
import org.apache.commons.logging.Log;
2827
import org.apache.commons.logging.LogFactory;
@@ -34,8 +33,6 @@
3433
import org.springframework.batch.core.annotation.BeforeJob;
3534
import org.springframework.batch.core.job.AbstractJob;
3635
import org.springframework.batch.core.listener.JobListenerFactoryBean;
37-
import org.springframework.batch.core.observability.BatchJobObservationConvention;
38-
import org.springframework.batch.core.observability.DefaultBatchJobObservationConvention;
3936
import org.springframework.batch.core.repository.JobRepository;
4037
import org.springframework.batch.support.ReflectionUtils;
4138

@@ -109,19 +106,6 @@ public B incrementer(JobParametersIncrementer jobParametersIncrementer) {
109106
return result;
110107
}
111108

112-
/**
113-
* Sets the job observation convention.
114-
* @param observationConvention the job observation convention (optional)
115-
* @return this to enable fluent chaining
116-
* @since 5.1
117-
*/
118-
public B observationConvention(BatchJobObservationConvention observationConvention) {
119-
properties.observationConvention = observationConvention;
120-
@SuppressWarnings("unchecked")
121-
B result = (B) this;
122-
return result;
123-
}
124-
125109
/**
126110
* Sets the observation registry for the job.
127111
* @param observationRegistry the observation registry (optional)
@@ -134,18 +118,6 @@ public B observationRegistry(ObservationRegistry observationRegistry) {
134118
return result;
135119
}
136120

137-
/**
138-
* Sets the meter registry for the job.
139-
* @param meterRegistry the meter registry (optional)
140-
* @return this to enable fluent chaining
141-
*/
142-
public B meterRegistry(MeterRegistry meterRegistry) {
143-
properties.meterRegistry = meterRegistry;
144-
@SuppressWarnings("unchecked")
145-
B result = (B) this;
146-
return result;
147-
}
148-
149121
/**
150122
* Registers objects using the annotation based listener configuration.
151123
* @param listener the object that has a method configured with listener annotation
@@ -213,18 +185,10 @@ protected void enhance(AbstractJob job) {
213185
if (jobParametersValidator != null) {
214186
job.setJobParametersValidator(jobParametersValidator);
215187
}
216-
BatchJobObservationConvention observationConvention = properties.getObservationConvention();
217-
if (observationConvention != null) {
218-
job.setObservationConvention(observationConvention);
219-
}
220188
ObservationRegistry observationRegistry = properties.getObservationRegistry();
221189
if (observationRegistry != null) {
222190
job.setObservationRegistry(observationRegistry);
223191
}
224-
MeterRegistry meterRegistry = properties.getMeterRegistry();
225-
if (meterRegistry != null) {
226-
job.setMeterRegistry(meterRegistry);
227-
}
228192

229193
Boolean restartable = properties.getRestartable();
230194
if (restartable != null) {
@@ -247,12 +211,8 @@ public static class CommonJobProperties {
247211

248212
private JobRepository jobRepository;
249213

250-
private BatchJobObservationConvention observationConvention = new DefaultBatchJobObservationConvention();
251-
252214
private ObservationRegistry observationRegistry;
253215

254-
private MeterRegistry meterRegistry;
255-
256216
private JobParametersIncrementer jobParametersIncrementer;
257217

258218
private JobParametersValidator jobParametersValidator;
@@ -264,9 +224,7 @@ public CommonJobProperties(CommonJobProperties properties) {
264224
this.name = properties.name;
265225
this.restartable = properties.restartable;
266226
this.jobRepository = properties.jobRepository;
267-
this.observationConvention = properties.observationConvention;
268227
this.observationRegistry = properties.observationRegistry;
269-
this.meterRegistry = properties.meterRegistry;
270228
this.jobExecutionListeners = new LinkedHashSet<>(properties.jobExecutionListeners);
271229
this.jobParametersIncrementer = properties.jobParametersIncrementer;
272230
this.jobParametersValidator = properties.jobParametersValidator;
@@ -296,14 +254,6 @@ public void setJobRepository(JobRepository jobRepository) {
296254
this.jobRepository = jobRepository;
297255
}
298256

299-
public BatchJobObservationConvention getObservationConvention() {
300-
return observationConvention;
301-
}
302-
303-
public void setObservationConvention(BatchJobObservationConvention observationConvention) {
304-
this.observationConvention = observationConvention;
305-
}
306-
307257
public ObservationRegistry getObservationRegistry() {
308258
return observationRegistry;
309259
}
@@ -312,14 +262,6 @@ public void setObservationRegistry(ObservationRegistry observationRegistry) {
312262
this.observationRegistry = observationRegistry;
313263
}
314264

315-
public MeterRegistry getMeterRegistry() {
316-
return meterRegistry;
317-
}
318-
319-
public void setMeterRegistry(MeterRegistry meterRegistry) {
320-
this.meterRegistry = meterRegistry;
321-
}
322-
323265
public String getName() {
324266
return name;
325267
}

0 commit comments

Comments
 (0)