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