001package io.prometheus.metrics.instrumentation.jvm;
002
003import io.prometheus.metrics.config.PrometheusProperties;
004import io.prometheus.metrics.core.metrics.CounterWithCallback;
005import io.prometheus.metrics.core.metrics.GaugeWithCallback;
006import io.prometheus.metrics.model.registry.PrometheusRegistry;
007
008import java.lang.management.ClassLoadingMXBean;
009import java.lang.management.ManagementFactory;
010
011/**
012 * JVM Class Loading metrics. The {@link JvmClassLoadingMetrics} are registered as part of the {@link JvmMetrics} like this:
013 * <pre>{@code
014 *   JvmMetrics.builder().register();
015 * }</pre>
016 * However, if you want only the {@link JvmClassLoadingMetrics} you can also register them directly:
017 * <pre>{@code
018 *   JvmClassLoadingMetrics.builder().register();
019 * }</pre>
020 * Example metrics being exported:
021 * <pre>
022 * # HELP jvm_classes_currently_loaded The number of classes that are currently loaded in the JVM
023 * # TYPE jvm_classes_currently_loaded gauge
024 * jvm_classes_currently_loaded 1109.0
025 * # HELP jvm_classes_loaded_total The total number of classes that have been loaded since the JVM has started execution
026 * # TYPE jvm_classes_loaded_total counter
027 * jvm_classes_loaded_total 1109.0
028 * # HELP jvm_classes_unloaded_total The total number of classes that have been unloaded since the JVM has started execution
029 * # TYPE jvm_classes_unloaded_total counter
030 * jvm_classes_unloaded_total 0.0
031 * </pre>
032 */
033public class JvmClassLoadingMetrics {
034
035    private static final String JVM_CLASSES_CURRENTLY_LOADED = "jvm_classes_currently_loaded";
036    private static final String JVM_CLASSES_LOADED_TOTAL = "jvm_classes_loaded_total";
037    private static final String JVM_CLASSES_UNLOADED_TOTAL = "jvm_classes_unloaded_total";
038
039    private final PrometheusProperties config;
040    private final ClassLoadingMXBean classLoadingBean;
041
042    private JvmClassLoadingMetrics(ClassLoadingMXBean classLoadingBean, PrometheusProperties config) {
043        this.classLoadingBean = classLoadingBean;
044        this.config = config;
045    }
046
047    private void register(PrometheusRegistry registry) {
048
049        GaugeWithCallback.builder(config)
050                .name(JVM_CLASSES_CURRENTLY_LOADED)
051                .help("The number of classes that are currently loaded in the JVM")
052                .callback(callback -> callback.call(classLoadingBean.getLoadedClassCount()))
053                .register(registry);
054
055        CounterWithCallback.builder(config)
056                .name(JVM_CLASSES_LOADED_TOTAL)
057                .help("The total number of classes that have been loaded since the JVM has started execution")
058                .callback(callback -> callback.call(classLoadingBean.getTotalLoadedClassCount()))
059                .register(registry);
060
061        CounterWithCallback.builder(config)
062                .name(JVM_CLASSES_UNLOADED_TOTAL)
063                .help("The total number of classes that have been unloaded since the JVM has started execution")
064                .callback(callback -> callback.call(classLoadingBean.getUnloadedClassCount()))
065                .register(registry);
066    }
067
068    public static Builder builder() {
069        return new Builder(PrometheusProperties.get());
070    }
071
072    public static Builder builder(PrometheusProperties config) {
073        return new Builder(config);
074    }
075
076    public static class Builder {
077
078        private final PrometheusProperties config;
079        private ClassLoadingMXBean classLoadingBean;
080
081        private Builder(PrometheusProperties config) {
082            this.config = config;
083        }
084
085        /**
086         * Package private. For testing only.
087         */
088        Builder classLoadingBean(ClassLoadingMXBean classLoadingBean) {
089            this.classLoadingBean = classLoadingBean;
090            return this;
091        }
092
093        public void register() {
094            register(PrometheusRegistry.defaultRegistry);
095        }
096
097        public void register(PrometheusRegistry registry) {
098            ClassLoadingMXBean classLoadingBean = this.classLoadingBean != null ? this.classLoadingBean : ManagementFactory.getClassLoadingMXBean();
099            new JvmClassLoadingMetrics(classLoadingBean, config).register(registry);
100        }
101    }
102}