001package io.prometheus.metrics.instrumentation.jvm;
002
003import io.prometheus.metrics.config.PrometheusProperties;
004import io.prometheus.metrics.core.metrics.SummaryWithCallback;
005import io.prometheus.metrics.model.registry.PrometheusRegistry;
006import io.prometheus.metrics.model.snapshots.Quantiles;
007import io.prometheus.metrics.model.snapshots.Unit;
008
009import java.lang.management.GarbageCollectorMXBean;
010import java.lang.management.ManagementFactory;
011import java.util.List;
012
013/**
014 * JVM Garbage Collector metrics. The {@link JvmGarbageCollectorMetrics} are registered as part of the {@link JvmMetrics} like this:
015 * <pre>{@code
016 *   JvmMetrics.builder().register();
017 * }</pre>
018 * However, if you want only the {@link JvmGarbageCollectorMetrics} you can also register them directly:
019 * <pre>{@code
020 *   JvmGarbageCollectorMetrics.builder().register();
021 * }</pre>
022 * Example metrics being exported:
023 * <pre>
024 * # HELP jvm_gc_collection_seconds Time spent in a given JVM garbage collector in seconds.
025 * # TYPE jvm_gc_collection_seconds summary
026 * jvm_gc_collection_seconds_count{gc="PS MarkSweep"} 0
027 * jvm_gc_collection_seconds_sum{gc="PS MarkSweep"} 0.0
028 * jvm_gc_collection_seconds_count{gc="PS Scavenge"} 0
029 * jvm_gc_collection_seconds_sum{gc="PS Scavenge"} 0.0
030 * </pre>
031 */
032public class JvmGarbageCollectorMetrics {
033
034    private static final String JVM_GC_COLLECTION_SECONDS = "jvm_gc_collection_seconds";
035
036    private final PrometheusProperties config;
037    private final List<GarbageCollectorMXBean> garbageCollectorBeans;
038
039    private JvmGarbageCollectorMetrics(List<GarbageCollectorMXBean> garbageCollectorBeans, PrometheusProperties config) {
040        this.config = config;
041        this.garbageCollectorBeans = garbageCollectorBeans;
042    }
043
044    private void register(PrometheusRegistry registry) {
045
046        SummaryWithCallback.builder(config)
047                .name(JVM_GC_COLLECTION_SECONDS)
048                .help("Time spent in a given JVM garbage collector in seconds.")
049                .unit(Unit.SECONDS)
050                .labelNames("gc")
051                .callback(callback -> {
052                    for (GarbageCollectorMXBean gc : garbageCollectorBeans) {
053                        callback.call(gc.getCollectionCount(), Unit.millisToSeconds(gc.getCollectionTime()), Quantiles.EMPTY, gc.getName());
054                    }
055                })
056                .register(registry);
057    }
058
059    public static Builder builder() {
060        return new Builder(PrometheusProperties.get());
061    }
062
063    public static Builder builder(PrometheusProperties config) {
064        return new Builder(config);
065    }
066
067    public static class Builder {
068
069        private final PrometheusProperties config;
070        private List<GarbageCollectorMXBean> garbageCollectorBeans;
071
072        private Builder(PrometheusProperties config) {
073            this.config = config;
074        }
075
076        /**
077         * Package private. For testing only.
078         */
079        Builder garbageCollectorBeans(List<GarbageCollectorMXBean> garbageCollectorBeans) {
080            this.garbageCollectorBeans = garbageCollectorBeans;
081            return this;
082        }
083
084        public void register() {
085            register(PrometheusRegistry.defaultRegistry);
086        }
087
088        public void register(PrometheusRegistry registry) {
089            List<GarbageCollectorMXBean> garbageCollectorBeans = this.garbageCollectorBeans;
090            if (garbageCollectorBeans == null) {
091                garbageCollectorBeans = ManagementFactory.getGarbageCollectorMXBeans();
092            }
093            new JvmGarbageCollectorMetrics(garbageCollectorBeans, config).register(registry);
094        }
095    }
096}