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