001package io.prometheus.client.hotspot; 002 003import com.sun.management.OperatingSystemMXBean; 004import com.sun.management.UnixOperatingSystemMXBean; 005import java.io.BufferedReader; 006import java.io.FileReader; 007import java.io.IOException; 008import java.io.FileNotFoundException; 009import java.lang.management.RuntimeMXBean; 010import java.lang.management.ManagementFactory; 011import java.util.ArrayList; 012import java.util.List; 013import java.util.logging.Logger; 014 015import io.prometheus.client.Collector; 016 017/** 018 * Exports the standard exports common across all prometheus clients. 019 * <p> 020 * This includes stats like CPU time spent and memory usage. 021 * <p> 022 * Example usage: 023 * <pre> 024 * {@code 025 * new StandardExports().register(); 026 * } 027 * </pre> 028 */ 029public class StandardExports extends Collector { 030 private static final Logger LOGGER = Logger.getLogger(StandardExports.class.getName()); 031 032 private StatusReader statusReader; 033 private OperatingSystemMXBean osBean; 034 private RuntimeMXBean runtimeBean; 035 private boolean unix; 036 private boolean linux; 037 038 public StandardExports() { 039 this(new StatusReader(), 040 (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean(), 041 ManagementFactory.getRuntimeMXBean()); 042 } 043 044 StandardExports(StatusReader statusReader, OperatingSystemMXBean osBean, RuntimeMXBean runtimeBean) { 045 this.statusReader = statusReader; 046 this.osBean = osBean; 047 this.runtimeBean = runtimeBean; 048 if (osBean instanceof UnixOperatingSystemMXBean) { 049 unix = true; 050 } 051 if (osBean.getName().indexOf("Linux") == 0) { 052 linux = true; 053 } 054 } 055 056 MetricFamilySamples singleMetric(String name, Type type, String help, double value) { 057 ArrayList<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>(); 058 samples.add(new MetricFamilySamples.Sample(name, new ArrayList<String>(), new ArrayList<String>(), value)); 059 return new MetricFamilySamples(name, type, help, samples); 060 } 061 062 private final static double KB = 1024; 063 064 public List<MetricFamilySamples> collect() { 065 List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); 066 067 mfs.add(singleMetric("process_cpu_seconds_total", Type.COUNTER, "Total user and system CPU time spent in seconds.", 068 osBean.getProcessCpuTime() / NANOSECONDS_PER_SECOND)); 069 070 mfs.add(singleMetric("process_start_time_seconds", Type.GAUGE, "Start time of the process since unix epoch in seconds.", 071 runtimeBean.getStartTime() / MILLISECONDS_PER_SECOND)); 072 073 if (unix) { 074 UnixOperatingSystemMXBean unixBean = (UnixOperatingSystemMXBean) osBean; 075 mfs.add(singleMetric("process_open_fds", Type.GAUGE, "Number of open file descriptors.", 076 unixBean.getOpenFileDescriptorCount())); 077 mfs.add(singleMetric("process_max_fds", Type.GAUGE, "Maximum number of open file descriptors.", 078 unixBean.getMaxFileDescriptorCount())); 079 } 080 081 // There's no standard Java or POSIX way to get memory stats, 082 // so add support for just Linux for now. 083 if (linux) { 084 try { 085 collectMemoryMetricsLinux(mfs); 086 } catch (Exception e) { 087 // If the format changes, log a warning and return what we can. 088 LOGGER.warning(e.toString()); 089 } 090 } 091 return mfs; 092 } 093 094 void collectMemoryMetricsLinux(List<MetricFamilySamples> mfs) { 095 // statm/stat report in pages, and it's non-trivial to get pagesize from Java 096 // so we parse status instead. 097 BufferedReader br = null; 098 try { 099 br = statusReader.procSelfStatusReader(); 100 String line; 101 while ((line = br.readLine()) != null) { 102 if (line.startsWith("VmSize:")) { 103 mfs.add(singleMetric("process_virtual_memory_bytes", Type.GAUGE, 104 "Virtual memory size in bytes.", 105 Float.parseFloat(line.split("\\s+")[1]) * KB)); 106 } else if (line.startsWith("VmRSS:")) { 107 mfs.add(singleMetric("process_resident_memory_bytes", Type.GAUGE, 108 "Resident memory size in bytes.", 109 Float.parseFloat(line.split("\\s+")[1]) * KB)); 110 } 111 } 112 } catch (IOException e) { 113 LOGGER.fine(e.toString()); 114 } finally { 115 if (br != null) { 116 try { 117 br.close(); 118 } catch (IOException e) { 119 LOGGER.fine(e.toString()); 120 } 121 } 122 } 123 } 124 125 static class StatusReader { 126 BufferedReader procSelfStatusReader() throws FileNotFoundException { 127 return new BufferedReader(new FileReader("/proc/self/status")); 128 } 129 } 130}