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