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}