micrometer-metrics/micrometer

Differences between monitoring ThreadPoolExecutor and ForkJoinPool

Open

#7089 aperta il 16 gen 2026

Vedi su GitHub
 (5 commenti) (0 reazioni) (0 assegnatari)Java (935 fork)batch import
enhancementhelp wanted

Metriche repository

Star
 (4220 star)
Metriche merge PR
 (Merge medio 1g 21h) (100 PR mergiate in 30 g)

Descrizione

While I was reviewing https://github.com/micrometer-metrics/micrometer/pull/7083, I saw that if I use ExecutorServiceMetrics.monitor for a ThreadPoolExecutor (Executors.newFixedThreadPool) and a ForkJoinPool I get a few unexpected differences in the outputs.

executor.active and executor.queued have units (threads and tasks) for ThreadPoolExecutor while for ForkJoinPool they do not, this means that changing this:

- ExecutorServiceMetrics.monitor(registry, Executors.newFixedThreadPool(2), name, tags);
+ ExecutorServiceMetrics.monitor(registry, new ForkJoinPool(2), name, tags);

will result in different time series (executor_active_threads vs. executor_active) in the Prometheus output:

- executor_active_threads{name="test"} 0.0
+ executor_active{name="test"} 0.0
- executor_queued_tasks{name="test"} 0.0
+ executor_queued{name="test"} 0.0

(Fixing this is a breaking change.)

public class Demo {
    public static void main(String[] args) {
        generateAndPrintMetricsFor(Executors.newFixedThreadPool(2));
        generateAndPrintMetricsFor(new ForkJoinPool(2));
    }

    private static void generateAndPrintMetricsFor(ExecutorService executorServiceToMonitor) {
        PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
        ExecutorService executorService = ExecutorServiceMetrics.monitor(registry, executorServiceToMonitor, "test", Tags.empty());
        List<Future<?>> futures = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            futures.add(executorService.submit(Demo::sleep));
        }
        for (Future<?> future : futures) {
            try {
                future.get(1_000, TimeUnit.MILLISECONDS);
            }
            catch (Exception e) {
                System.out.println(e.getMessage());
            }
            finally {
                future.cancel(true);
            }
        }

        System.out.println("===== Metrics for " + executorService.getClass().getSimpleName());
        System.out.println(registry.scrape());
        executorService.shutdown();
    }

    private static void sleep() {
        try {
            Thread.sleep(100);
        }
        catch (InterruptedException e) {
            // noop
        }
    }
}

Guida contributor