Skip to content
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

Error in the metrics registration with a meter registry customization #4759

Open
jorgerod opened this issue Feb 5, 2025 · 1 comment
Open
Labels
status: waiting-for-reporter Issues for which we are waiting for feedback from the reporter

Comments

@jorgerod
Copy link

jorgerod commented Feb 5, 2025

Bug description
When a meter registry customization is registered and the management.metrics.use-global-registry property is disabled, some spring-batch metrics are being registered in the meter registry customization and others in the meter registry global.

  • Metrics registered in meter registry customization

    • spring.batch.job
    • spring.batch.job.active
    • spring.batch.step
    • spring.batch.step.active
  • Metrics registered in the global meter registry

    • spring.batch.item.read
    • spring.batch.item.process
    • spring.batch.chunk.write

Environment
Please provide as many details as possible: Spring Batch version, Java version, which database you use if any, etc

  • Spring Batch version: 5.2.1
  • java version:
openjdk 21.0.3 2024-04-16 LTS
OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.3+9 (build 21.0.3+9-LTS, mixed mode, sharing)

Steps to reproduce

  1. Create a SimpleMeterRegistry
  2. management.metrics.use-global-registry=false
  3. Define job with step and reader, processor and writer
  4. Check metrics in the custom SimpleMeterRegistry

Expected behavior
Metrics spring.batch.item.read, spring.batch.item.process and spring.batch.chunk.write are registered the custom registry meter

Minimal Complete Reproducible example

package org.springframework.batch;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
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.transaction.PlatformTransactionManager;

import java.util.Arrays;
import java.util.List;

@Configuration
public class MyBatchJobConfiguration {

    @Bean
    public MeterRegistry myMeterRegistry() {
        return new SimpleMeterRegistry();
    }


    @Bean
    public ItemReader<String> reader() {
        return new ListItemReader<>(Arrays.asList("item1", "item2", "item3"));
    }

    @Bean
    public ItemProcessor<String, String> processor() {
        return item -> "processed " + item;
    }

    @Bean
    public ItemWriter<String> writer() {
        return items -> items.forEach(System.out::println);
    }

    @Bean
    public Job importUserJob(JobRepository jobRepository, Step step1) {
        return new JobBuilder("importUserJob", jobRepository)
            .incrementer(new RunIdIncrementer())
            .flow(step1)
            .end()
            .build();
    }

    @Bean
    public Step step(JobRepository jobRepository, PlatformTransactionManager transactionManager, ItemReader<String> reader, ItemProcessor<String, String> processor, ItemWriter<String> writer) {
        return new StepBuilder("step1", jobRepository)
            .<String, String> chunk(10, transactionManager)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    }

    /*
     * Main method to run the application and exhibit the issue
     */
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyBatchJobConfiguration.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        JobParameters jobParameters = new JobParameters();
        JobExecution jobExecution = jobLauncher.run(job, jobParameters);
        System.out.println(jobExecution.getExitStatus().getExitCode());
    }
}
@SpringBatchTest
@DirtiesContext
@SpringJUnitConfig(MyBatchJobConfiguration.class)
@PropertySource("classpath:application.properties")
@EnableAutoConfiguration
public class MyBatchJobTest {

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Autowired
    private JobRepositoryTestUtils jobRepositoryTestUtils;

    @Autowired
    private MeterRegistry myMeterRegistry;


    @Test
    public void givenCoffeeList_whenJobExecuted_thenSuccess() throws Exception {
        //when
        jobLauncherTestUtils.launchJob();

        //then
        assertThat(myMeterRegistry.getMeters()).anyMatch(meter -> meter.getId().getName().equals("spring.batch.item.read"));
        assertThat(myMeterRegistry.getMeters()).anyMatch(meter -> meter.getId().getName().equals("spring.batch.item.process"));
        assertThat(myMeterRegistry.getMeters()).anyMatch(meter -> meter.getId().getName().equals("spring.batch.chunk.write"));
    }
}

Complete sample: sb-batch.zip

Related issue

@jorgerod jorgerod added status: waiting-for-triage Issues that we did not analyse yet type: bug labels Feb 5, 2025
@fmbenhassine
Copy link
Contributor

Thank you for reporting this issue. Spring Batch reports metrics to Micrometer's global registry (there is a note about it here), so I am not sure disabling management.metrics.use-global-registry is compatible with Spring Batch.

But I believe the issue is not related to that. The issue is that by default, the global registry of Micrometer is an empty composite. So if you want to use a custom registry, you need to add it to the global one prior to running your job. For instance, you test passes if I add the registry like follows:

@Test
public void givenCustomMeterRegistry_whenJobExecution_thenSpringBatchMetricsAreRegisteredInCustomMeterRegistry() throws Exception {
++  // given
++  Metrics.globalRegistry.add(myMeterRegistry);

    //when
    jobLauncherTestUtils.launchJob();

    //then
    assertThat(myMeterRegistry.getMeters()).anyMatch(meter -> meter.getId().getName().equals("spring.batch.item.read"));
    assertThat(myMeterRegistry.getMeters()).anyMatch(meter -> meter.getId().getName().equals("spring.batch.item.process"));
    assertThat(myMeterRegistry.getMeters()).anyMatch(meter -> meter.getId().getName().equals("spring.batch.chunk.write"));
}

I remember this was confusing to other users as well (see https://stackoverflow.com/questions/64869472/spring-batch-and-boot-micrometer), so I think we need to make this clearer in the reference documentation. BTW, we have a sample that uses prometheus for metrics, and it shows how the registry is added to the global composite beforehand.

Does that make things clearer?

@fmbenhassine fmbenhassine added status: waiting-for-reporter Issues for which we are waiting for feedback from the reporter and removed status: waiting-for-triage Issues that we did not analyse yet type: bug labels Feb 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-reporter Issues for which we are waiting for feedback from the reporter
Projects
None yet
Development

No branches or pull requests

2 participants