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