micrometer-metrics/micrometer

Differences between monitoring ThreadPoolExecutor and ForkJoinPool

Ouverte

#7 089 ouverte le 16 janv. 2026

 (5 commentaires) (0 réaction) (0 personne assignée)Java (1 133 forks)batch import
enhancementhelp wanted

Métriques du dépôt

Stars
 (4 882 étoiles)
Métriques de merge PR
 (Merge moyen 1j 21h) (100 PRs mergées en 30 j)

Description

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
        }
    }
}

Guide contributeur