11package io .prometheus .client .hotspot ;
22
3- import com .sun .management .UnixOperatingSystemMXBean ;
43import io .prometheus .client .Collector ;
54import io .prometheus .client .CounterMetricFamily ;
65import io .prometheus .client .GaugeMetricFamily ;
1211import java .lang .management .ManagementFactory ;
1312import java .lang .management .OperatingSystemMXBean ;
1413import java .lang .management .RuntimeMXBean ;
14+ import java .lang .reflect .InvocationTargetException ;
1515import java .lang .reflect .Method ;
1616import java .util .ArrayList ;
1717import java .util .List ;
@@ -36,7 +36,6 @@ public class StandardExports extends Collector {
3636 private final StatusReader statusReader ;
3737 private final OperatingSystemMXBean osBean ;
3838 private final RuntimeMXBean runtimeBean ;
39- private final boolean unix ;
4039 private final boolean linux ;
4140
4241 public StandardExports () {
@@ -49,43 +48,42 @@ public StandardExports() {
4948 this .statusReader = statusReader ;
5049 this .osBean = osBean ;
5150 this .runtimeBean = runtimeBean ;
52- this .unix = (osBean instanceof UnixOperatingSystemMXBean );
5351 this .linux = (osBean .getName ().indexOf ("Linux" ) == 0 );
5452 }
5553
5654 private final static double KB = 1024 ;
5755
56+ @ Override
5857 public List <MetricFamilySamples > collect () {
5958 List <MetricFamilySamples > mfs = new ArrayList <MetricFamilySamples >();
6059
6160 try {
62- /*
63- There are two interfaces com.ibm.lang.management.OperatingSystemMXBean and com.sun.management.OperatingSystemMXBean,
64- but no common interface which exposes getProcessCpuTime. Hence call on the implementing class, which is default access,
65- so use reflection hack to set it accessible.
66- */
67- Method method = osBean .getClass ().getMethod ("getProcessCpuTime" , null );
68- if (!method .isAccessible ()) {
69- method .setAccessible (true );
70- }
71- Long l = (Long ) method .invoke (osBean );
61+ // There exist at least 2 similar but unrelated UnixOperatingSystemMXBean interfaces, in
62+ // com.sun.management and com.ibm.lang.management. Hence use reflection and recursively go
63+ // through implemented interfaces until the method can be made accessible and invoked.
64+ Long processCpuTime = callLongGetter ("getProcessCpuTime" , osBean );
7265 mfs .add (new CounterMetricFamily ("process_cpu_seconds_total" , "Total user and system CPU time spent in seconds." ,
73- l / NANOSECONDS_PER_SECOND ));
66+ processCpuTime / NANOSECONDS_PER_SECOND ));
7467 }
7568 catch (Exception e ) {
7669 LOGGER .log (Level .FINE ,"Could not access process cpu time" , e );
7770 }
7871
79-
8072 mfs .add (new GaugeMetricFamily ("process_start_time_seconds" , "Start time of the process since unix epoch in seconds." ,
8173 runtimeBean .getStartTime () / MILLISECONDS_PER_SECOND ));
8274
83- if (unix ) {
84- UnixOperatingSystemMXBean unixBean = (UnixOperatingSystemMXBean ) osBean ;
85- mfs .add (new GaugeMetricFamily ("process_open_fds" , "Number of open file descriptors." ,
86- unixBean .getOpenFileDescriptorCount ()));
87- mfs .add (new GaugeMetricFamily ("process_max_fds" , "Maximum number of open file descriptors." ,
88- unixBean .getMaxFileDescriptorCount ()));
75+ // There exist at least 2 similar but unrelated UnixOperatingSystemMXBean interfaces, in
76+ // com.sun.management and com.ibm.lang.management. Hence use reflection and recursively go
77+ // through implemented interfaces until the method can be made accessible and invoked.
78+ try {
79+ Long openFdCount = callLongGetter ("getOpenFileDescriptorCount" , osBean );
80+ mfs .add (new GaugeMetricFamily (
81+ "process_open_fds" , "Number of open file descriptors." , openFdCount ));
82+ Long maxFdCount = callLongGetter ("getMaxFileDescriptorCount" , osBean );
83+ mfs .add (new GaugeMetricFamily (
84+ "process_max_fds" , "Maximum number of open file descriptors." , maxFdCount ));
85+ } catch (Exception e ) {
86+ // Ignore, expected on non-Unix OSs.
8987 }
9088
9189 // There's no standard Java or POSIX way to get memory stats,
@@ -101,6 +99,49 @@ but no common interface which exposes getProcessCpuTime. Hence call on the imple
10199 return mfs ;
102100 }
103101
102+ static Long callLongGetter (String getterName , Object obj )
103+ throws NoSuchMethodException , InvocationTargetException {
104+ return callLongGetter (obj .getClass ().getMethod (getterName ), obj );
105+ }
106+
107+ /**
108+ * Attempts to call a method either directly or via one of the implemented interfaces.
109+ * <p>
110+ * A Method object refers to a specific method declared in a specific class. The first invocation
111+ * might happen with method == SomeConcreteClass.publicLongGetter() and will fail if
112+ * SomeConcreteClass is not public. We then recurse over all interfaces implemented by
113+ * SomeConcreteClass (or extended by those interfaces and so on) until we eventually invoke
114+ * callMethod() with method == SomePublicInterface.publicLongGetter(), which will then succeed.
115+ * <p>
116+ * There is a built-in assumption that the method will never return null (or, equivalently, that
117+ * it returns the primitive data type, i.e. {@code long} rather than {@code Long}). If this
118+ * assumption doesn't hold, the method might be called repeatedly and the returned value will be
119+ * the one produced by the last call.
120+ */
121+ static Long callLongGetter (Method method , Object obj ) throws InvocationTargetException {
122+ try {
123+ return (Long ) method .invoke (obj );
124+ } catch (IllegalAccessException e ) {
125+ // Expected, the declaring class or interface might not be public.
126+ }
127+
128+ // Iterate over all implemented/extended interfaces and attempt invoking the method with the
129+ // same name and parameters on each.
130+ for (Class <?> clazz : method .getDeclaringClass ().getInterfaces ()) {
131+ try {
132+ Method interfaceMethod = clazz .getMethod (method .getName (), method .getParameterTypes ());
133+ Long result = callLongGetter (interfaceMethod , obj );
134+ if (result != null ) {
135+ return result ;
136+ }
137+ } catch (NoSuchMethodException e ) {
138+ // Expected, class might implement multiple, unrelated interfaces.
139+ }
140+ }
141+
142+ return null ;
143+ }
144+
104145 void collectMemoryMetricsLinux (List <MetricFamilySamples > mfs ) {
105146 // statm/stat report in pages, and it's non-trivial to get pagesize from Java
106147 // so we parse status instead.
0 commit comments