001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.support.metric; 017 018import com.codahale.metrics.Gauge; 019import com.codahale.metrics.Metric; 020import com.codahale.metrics.MetricSet; 021 022import java.lang.management.ManagementFactory; 023import java.lang.management.OperatingSystemMXBean; 024import java.lang.reflect.InvocationTargetException; 025import java.lang.reflect.Method; 026import java.util.*; 027 028/** 029 * 参考 https://github.com/micrometer-metrics/micrometer/ 030 * blob/master/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/system/ProcessorMetrics.java 031 */ 032public class JvmCpuGaugeSet implements MetricSet { 033 034 035 /** List of public, exported interface class names from supported JVM implementations. */ 036 private static final List<String> OPERATING_SYSTEM_BEAN_CLASS_NAMES = Arrays.asList( 037 "com.ibm.lang.management.OperatingSystemMXBean", // J9 038 "com.sun.management.OperatingSystemMXBean" // HotSpot 039 ); 040 041 042 private final OperatingSystemMXBean operatingSystemBean; 043 044 private final Class<?> operatingSystemBeanClass; 045 046 private final Method systemCpuUsage; 047 048 private final Method processCpuUsage; 049 050 /** 051 * Creates a new set of gauges. 052 */ 053 public JvmCpuGaugeSet() { 054 this.operatingSystemBean = ManagementFactory.getOperatingSystemMXBean(); 055 this.operatingSystemBeanClass = getFirstClassFound(OPERATING_SYSTEM_BEAN_CLASS_NAMES); 056 Method getCpuLoad = detectMethod("getCpuLoad"); 057 this.systemCpuUsage = getCpuLoad != null ? getCpuLoad : detectMethod("getSystemCpuLoad"); 058 this.processCpuUsage = detectMethod("getProcessCpuLoad"); 059 } 060 061 062 @Override 063 public Map<String, Metric> getMetrics() { 064 final Map<String, Metric> gauges = new HashMap<>(); 065 066 Runtime runtime = Runtime.getRuntime(); 067 gauges.put("system.cpu.count", (Gauge<Integer>) () -> runtime.availableProcessors()); 068 069 if (operatingSystemBean.getSystemLoadAverage() >= 0) { 070 gauges.put("system.load.average.1m", (Gauge<Double>) () -> operatingSystemBean.getSystemLoadAverage()); 071 } 072 073 if (systemCpuUsage != null) { 074 gauges.put("system.cpu.usage", (Gauge<Double>) () -> invoke(systemCpuUsage)); 075 } 076 077 if (processCpuUsage != null) { 078 gauges.put("process.cpu.usage", (Gauge<Double>) () -> invoke(processCpuUsage)); 079 } 080 081 return Collections.unmodifiableMap(gauges); 082 } 083 084 085 086 087 private double invoke(Method method) { 088 try { 089 return method != null ? (double) method.invoke(operatingSystemBean) : Double.NaN; 090 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 091 return Double.NaN; 092 } 093 } 094 095 096 private Method detectMethod(String name) { 097 if (operatingSystemBeanClass == null) { 098 return null; 099 } 100 try { 101 // ensure the Bean we have is actually an instance of the interface 102 operatingSystemBeanClass.cast(operatingSystemBean); 103 return operatingSystemBeanClass.getDeclaredMethod(name); 104 } catch (ClassCastException | NoSuchMethodException | SecurityException e) { 105 return null; 106 } 107 } 108 109 private Class<?> getFirstClassFound(List<String> classNames) { 110 for (String className : classNames) { 111 try { 112 return Class.forName(className); 113 } catch (ClassNotFoundException ignore) { 114 } 115 } 116 return null; 117 } 118}