001package io.prometheus.client.hotspot;
002
003import io.prometheus.client.Collector;
004import io.prometheus.client.GaugeMetricFamily;
005import io.prometheus.client.Predicate;
006
007import java.lang.management.ManagementFactory;
008import java.lang.management.MemoryMXBean;
009import java.lang.management.MemoryPoolMXBean;
010import java.lang.management.MemoryUsage;
011import java.util.ArrayList;
012import java.util.Collections;
013import java.util.List;
014
015import static io.prometheus.client.SampleNameFilter.ALLOW_ALL;
016
017/**
018 * Exports metrics about JVM memory areas.
019 * <p>
020 * Example usage:
021 * <pre>
022 * {@code
023 *   new MemoryPoolsExports().register();
024 * }
025 * </pre>
026 * Example metrics being exported:
027 * <pre>
028 *   jvm_memory_bytes_used{area="heap"} 2000000
029 *   jvm_memory_bytes_committed{area="nonheap"} 200000
030 *   jvm_memory_bytes_max{area="nonheap"} 2000000
031 *   jvm_memory_pool_bytes_used{pool="PS Eden Space"} 2000
032 * </pre>
033 */
034public class MemoryPoolsExports extends Collector {
035
036  private static final String JVM_MEMORY_OBJECTS_PENDING_FINALIZATION = "jvm_memory_objects_pending_finalization";
037  private static final String JVM_MEMORY_BYTES_USED = "jvm_memory_bytes_used";
038  private static final String JVM_MEMORY_BYTES_COMMITTED = "jvm_memory_bytes_committed";
039  private static final String JVM_MEMORY_BYTES_MAX = "jvm_memory_bytes_max";
040  private static final String JVM_MEMORY_BYTES_INIT = "jvm_memory_bytes_init";
041
042  // Note: The Prometheus naming convention is that units belong at the end of the metric name.
043  // For new metrics like jvm_memory_pool_collection_used_bytes we follow that convention.
044  // For old metrics like jvm_memory_pool_bytes_used we keep the names as they are to avoid a breaking change.
045
046  private static final String JVM_MEMORY_POOL_BYTES_USED = "jvm_memory_pool_bytes_used";
047  private static final String JVM_MEMORY_POOL_BYTES_COMMITTED = "jvm_memory_pool_bytes_committed";
048  private static final String JVM_MEMORY_POOL_BYTES_MAX = "jvm_memory_pool_bytes_max";
049  private static final String JVM_MEMORY_POOL_BYTES_INIT = "jvm_memory_pool_bytes_init";
050  private static final String JVM_MEMORY_POOL_COLLECTION_USED_BYTES = "jvm_memory_pool_collection_used_bytes";
051  private static final String JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES = "jvm_memory_pool_collection_committed_bytes";
052  private static final String JVM_MEMORY_POOL_COLLECTION_MAX_BYTES = "jvm_memory_pool_collection_max_bytes";
053  private static final String JVM_MEMORY_POOL_COLLECTION_INIT_BYTES = "jvm_memory_pool_collection_init_bytes";
054
055  private final MemoryMXBean memoryBean;
056  private final List<MemoryPoolMXBean> poolBeans;
057
058  public MemoryPoolsExports() {
059    this(
060        ManagementFactory.getMemoryMXBean(),
061        ManagementFactory.getMemoryPoolMXBeans());
062  }
063
064  public MemoryPoolsExports(MemoryMXBean memoryBean,
065                             List<MemoryPoolMXBean> poolBeans) {
066    this.memoryBean = memoryBean;
067    this.poolBeans = poolBeans;
068  }
069
070  void addMemoryAreaMetrics(List<MetricFamilySamples> sampleFamilies, Predicate<String> nameFilter) {
071    MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
072    MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
073
074    if (nameFilter.test(JVM_MEMORY_OBJECTS_PENDING_FINALIZATION)) {
075      GaugeMetricFamily finalizer = new GaugeMetricFamily(
076              JVM_MEMORY_OBJECTS_PENDING_FINALIZATION,
077              "The number of objects waiting in the finalizer queue.",
078              memoryBean.getObjectPendingFinalizationCount());
079      sampleFamilies.add(finalizer);
080    }
081
082    if (nameFilter.test(JVM_MEMORY_BYTES_USED)) {
083      GaugeMetricFamily used = new GaugeMetricFamily(
084              JVM_MEMORY_BYTES_USED,
085              "Used bytes of a given JVM memory area.",
086              Collections.singletonList("area"));
087      used.addMetric(Collections.singletonList("heap"), heapUsage.getUsed());
088      used.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getUsed());
089      sampleFamilies.add(used);
090    }
091
092    if (nameFilter.test(JVM_MEMORY_BYTES_COMMITTED)) {
093      GaugeMetricFamily committed = new GaugeMetricFamily(
094              JVM_MEMORY_BYTES_COMMITTED,
095              "Committed (bytes) of a given JVM memory area.",
096              Collections.singletonList("area"));
097      committed.addMetric(Collections.singletonList("heap"), heapUsage.getCommitted());
098      committed.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getCommitted());
099      sampleFamilies.add(committed);
100    }
101
102    if (nameFilter.test(JVM_MEMORY_BYTES_MAX)) {
103      GaugeMetricFamily max = new GaugeMetricFamily(
104              JVM_MEMORY_BYTES_MAX,
105              "Max (bytes) of a given JVM memory area.",
106              Collections.singletonList("area"));
107      max.addMetric(Collections.singletonList("heap"), heapUsage.getMax());
108      max.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getMax());
109      sampleFamilies.add(max);
110    }
111
112    if (nameFilter.test(JVM_MEMORY_BYTES_INIT)) {
113      GaugeMetricFamily init = new GaugeMetricFamily(
114              JVM_MEMORY_BYTES_INIT,
115              "Initial bytes of a given JVM memory area.",
116              Collections.singletonList("area"));
117      init.addMetric(Collections.singletonList("heap"), heapUsage.getInit());
118      init.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getInit());
119      sampleFamilies.add(init);
120    }
121  }
122
123  void addMemoryPoolMetrics(List<MetricFamilySamples> sampleFamilies, Predicate<String> nameFilter) {
124
125    boolean anyPoolMetricPassesFilter = false;
126
127    GaugeMetricFamily used = null;
128    if (nameFilter.test(JVM_MEMORY_POOL_BYTES_USED)) {
129      used = new GaugeMetricFamily(
130              JVM_MEMORY_POOL_BYTES_USED,
131              "Used bytes of a given JVM memory pool.",
132              Collections.singletonList("pool"));
133      sampleFamilies.add(used);
134      anyPoolMetricPassesFilter = true;
135    }
136    GaugeMetricFamily committed = null;
137    if (nameFilter.test(JVM_MEMORY_POOL_BYTES_COMMITTED)) {
138      committed = new GaugeMetricFamily(
139              JVM_MEMORY_POOL_BYTES_COMMITTED,
140              "Committed bytes of a given JVM memory pool.",
141              Collections.singletonList("pool"));
142      sampleFamilies.add(committed);
143      anyPoolMetricPassesFilter = true;
144    }
145    GaugeMetricFamily max = null;
146    if (nameFilter.test(JVM_MEMORY_POOL_BYTES_MAX)) {
147      max = new GaugeMetricFamily(
148              JVM_MEMORY_POOL_BYTES_MAX,
149              "Max bytes of a given JVM memory pool.",
150              Collections.singletonList("pool"));
151      sampleFamilies.add(max);
152      anyPoolMetricPassesFilter = true;
153    }
154    GaugeMetricFamily init = null;
155    if (nameFilter.test(JVM_MEMORY_POOL_BYTES_INIT)) {
156      init = new GaugeMetricFamily(
157              JVM_MEMORY_POOL_BYTES_INIT,
158              "Initial bytes of a given JVM memory pool.",
159              Collections.singletonList("pool"));
160      sampleFamilies.add(init);
161      anyPoolMetricPassesFilter = true;
162    }
163    GaugeMetricFamily collectionUsed = null;
164    if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_USED_BYTES)) {
165      collectionUsed = new GaugeMetricFamily(
166              JVM_MEMORY_POOL_COLLECTION_USED_BYTES,
167              "Used bytes after last collection of a given JVM memory pool.",
168              Collections.singletonList("pool"));
169      sampleFamilies.add(collectionUsed);
170      anyPoolMetricPassesFilter = true;
171    }
172    GaugeMetricFamily collectionCommitted = null;
173    if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES)) {
174      collectionCommitted = new GaugeMetricFamily(
175              JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES,
176              "Committed after last collection bytes of a given JVM memory pool.",
177              Collections.singletonList("pool"));
178      sampleFamilies.add(collectionCommitted);
179      anyPoolMetricPassesFilter = true;
180    }
181    GaugeMetricFamily collectionMax = null;
182    if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_MAX_BYTES)) {
183      collectionMax = new GaugeMetricFamily(
184              JVM_MEMORY_POOL_COLLECTION_MAX_BYTES,
185              "Max bytes after last collection of a given JVM memory pool.",
186              Collections.singletonList("pool"));
187      sampleFamilies.add(collectionMax);
188      anyPoolMetricPassesFilter = true;
189    }
190    GaugeMetricFamily collectionInit = null;
191    if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_INIT_BYTES)) {
192      collectionInit = new GaugeMetricFamily(
193              JVM_MEMORY_POOL_COLLECTION_INIT_BYTES,
194              "Initial after last collection bytes of a given JVM memory pool.",
195              Collections.singletonList("pool"));
196      sampleFamilies.add(collectionInit);
197      anyPoolMetricPassesFilter = true;
198    }
199    if (anyPoolMetricPassesFilter) {
200      for (final MemoryPoolMXBean pool : poolBeans) {
201        MemoryUsage poolUsage = pool.getUsage();
202        if (poolUsage != null) {
203          addPoolMetrics(used, committed, max, init, pool.getName(), poolUsage);
204        }
205        MemoryUsage collectionPoolUsage = pool.getCollectionUsage();
206        if (collectionPoolUsage != null) {
207          addPoolMetrics(collectionUsed, collectionCommitted, collectionMax, collectionInit, pool.getName(), collectionPoolUsage);
208        }
209      }
210    }
211  }
212
213  private void addPoolMetrics(GaugeMetricFamily used, GaugeMetricFamily committed, GaugeMetricFamily max, GaugeMetricFamily init, String poolName, MemoryUsage poolUsage) {
214    if (used != null) {
215      used.addMetric(Collections.singletonList(poolName), poolUsage.getUsed());
216    }
217    if (committed != null) {
218      committed.addMetric(Collections.singletonList(poolName), poolUsage.getCommitted());
219    }
220    if (max != null) {
221      max.addMetric(Collections.singletonList(poolName), poolUsage.getMax());
222    }
223    if (init != null) {
224      init.addMetric(Collections.singletonList(poolName), poolUsage.getInit());
225    }
226  }
227
228  @Override
229  public List<MetricFamilySamples> collect() {
230    return collect(null);
231  }
232
233  @Override
234  public List<MetricFamilySamples> collect(Predicate<String> nameFilter) {
235    List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
236    addMemoryAreaMetrics(mfs, nameFilter == null ? ALLOW_ALL : nameFilter);
237    addMemoryPoolMetrics(mfs, nameFilter == null ? ALLOW_ALL : nameFilter);
238    return mfs;
239  }
240}