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.reflect.InvocationTargetException;
009import java.lang.reflect.Method;
010import java.util.ArrayList;
011import java.util.Collections;
012import java.util.List;
013import java.util.logging.Logger;
014
015import static io.prometheus.client.SampleNameFilter.ALLOW_ALL;
016
017/**
018 * Exports metrics about JVM buffers.
019 *
020 * Can be replaced with a simple access once JDK 1.7 compatibility is baseline.
021 *
022 */
023public class BufferPoolsExports extends Collector {
024
025    private static final String JVM_BUFFER_POOL_USED_BYTES = "jvm_buffer_pool_used_bytes";
026    private static final String JVM_BUFFER_POOL_CAPACITY_BYTES = "jvm_buffer_pool_capacity_bytes";
027    private static final String JVM_BUFFER_POOL_USED_BUFFERS = "jvm_buffer_pool_used_buffers";
028
029    private static final Logger LOGGER = Logger.getLogger(BufferPoolsExports.class.getName());
030
031    private final List<Object> bufferPoolMXBeans = new ArrayList<Object>();
032    private Method getName;
033    private Method getMemoryUsed;
034    private Method getTotalCapacity;
035    private Method getCount;
036
037    public BufferPoolsExports() {
038        try {
039            final Class<?> bufferPoolMXBeanClass = Class.forName("java.lang.management.BufferPoolMXBean");
040            bufferPoolMXBeans.addAll(accessBufferPoolMXBeans(bufferPoolMXBeanClass));
041
042            getName = bufferPoolMXBeanClass.getMethod("getName");
043            getMemoryUsed = bufferPoolMXBeanClass.getMethod("getMemoryUsed");
044            getTotalCapacity = bufferPoolMXBeanClass.getMethod("getTotalCapacity");
045            getCount = bufferPoolMXBeanClass.getMethod("getCount");
046
047        } catch (ClassNotFoundException e) {
048            LOGGER.fine("BufferPoolMXBean not available, no metrics for buffer pools will be exported");
049        } catch (NoSuchMethodException e) {
050            LOGGER.fine("Can not get necessary accessor from BufferPoolMXBean: " + e.getMessage());
051        }
052    }
053
054    private static List<Object> accessBufferPoolMXBeans(final Class<?> bufferPoolMXBeanClass) {
055        try {
056            final Method getPlatformMXBeansMethod = ManagementFactory.class.getMethod("getPlatformMXBeans", Class.class);
057            final Object listOfBufferPoolMXBeanInstances = getPlatformMXBeansMethod.invoke(null, bufferPoolMXBeanClass);
058
059            return (List<Object>) listOfBufferPoolMXBeanInstances;
060
061        } catch (NoSuchMethodException e) {
062            LOGGER.fine("ManagementFactory.getPlatformMXBeans not available, no metrics for buffer pools will be exported");
063            return Collections.emptyList();
064        } catch (IllegalAccessException e) {
065            LOGGER.fine("ManagementFactory.getPlatformMXBeans not accessible, no metrics for buffer pools will be exported");
066            return Collections.emptyList();
067        } catch (InvocationTargetException e) {
068            LOGGER.warning("ManagementFactory.getPlatformMXBeans could not be invoked, no metrics for buffer pools will be exported");
069            return Collections.emptyList();
070        }
071    }
072
073    @Override
074    public List<MetricFamilySamples> collect() {
075        return collect(null);
076    }
077
078    @Override
079    public List<MetricFamilySamples> collect(Predicate<String> nameFilter) {
080        List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
081        if (nameFilter == null) {
082            nameFilter = ALLOW_ALL;
083        }
084        GaugeMetricFamily used = null;
085        if (nameFilter.test(JVM_BUFFER_POOL_USED_BYTES)) {
086            used = new GaugeMetricFamily(
087                    JVM_BUFFER_POOL_USED_BYTES,
088                    "Used bytes of a given JVM buffer pool.",
089                    Collections.singletonList("pool"));
090            mfs.add(used);
091        }
092        GaugeMetricFamily capacity = null;
093        if (nameFilter.test(JVM_BUFFER_POOL_CAPACITY_BYTES)) {
094            capacity = new GaugeMetricFamily(
095                    JVM_BUFFER_POOL_CAPACITY_BYTES,
096                    "Bytes capacity of a given JVM buffer pool.",
097                    Collections.singletonList("pool"));
098            mfs.add(capacity);
099        }
100        GaugeMetricFamily buffers = null;
101        if (nameFilter.test(JVM_BUFFER_POOL_USED_BUFFERS)) {
102            buffers = new GaugeMetricFamily(
103                    JVM_BUFFER_POOL_USED_BUFFERS,
104                    "Used buffers of a given JVM buffer pool.",
105                    Collections.singletonList("pool"));
106            mfs.add(buffers);
107        }
108        for (final Object pool : bufferPoolMXBeans) {
109            if (used != null) {
110                used.addMetric(
111                        Collections.singletonList(getName(pool)),
112                        callLongMethod(getMemoryUsed, pool));
113            }
114            if (capacity != null) {
115                capacity.addMetric(
116                        Collections.singletonList(getName(pool)),
117                        callLongMethod(getTotalCapacity, pool));
118            }
119            if (buffers != null) {
120                buffers.addMetric(
121                        Collections.singletonList(getName(pool)),
122                        callLongMethod(getCount, pool));
123            }
124        }
125        return mfs;
126    }
127
128    private long callLongMethod(final Method method, final Object pool) {
129        try {
130            return (Long)method.invoke(pool);
131        } catch (IllegalAccessException e) {
132            LOGGER.fine("Couldn't call " + method.getName() + ": " + e.getMessage());
133        } catch (InvocationTargetException e) {
134            LOGGER.fine("Couldn't call " + method.getName() + ": " + e.getMessage());
135        }
136        return 0L;
137    }
138
139    private String getName(final Object pool) {
140        try {
141            return (String)getName.invoke(pool);
142        } catch (IllegalAccessException e) {
143            LOGGER.fine("Couldn't call getName " + e.getMessage());
144        } catch (InvocationTargetException e) {
145            LOGGER.fine("Couldn't call getName " + e.getMessage());
146        }
147        return "<unknown>";
148    }
149}