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/FileDescriptorMetrics.java
031 */
032public class ProcessFilesGaugeSet implements MetricSet {
033
034
035    /**
036     * List of public, exported interface class names from supported JVM implementations.
037     */
038    private static final List<String> UNIX_OPERATING_SYSTEM_BEAN_CLASS_NAMES = Arrays.asList(
039            "com.sun.management.UnixOperatingSystemMXBean", // HotSpot
040            "com.ibm.lang.management.UnixOperatingSystemMXBean" // J9
041    );
042
043    private final OperatingSystemMXBean osBean;
044
045    private final Class<?> osBeanClass;
046
047    private final Method openFilesMethod;
048
049    private final Method maxFilesMethod;
050
051
052    /**
053     * Creates a new set of gauges.
054     */
055    public ProcessFilesGaugeSet() {
056        this.osBean = ManagementFactory.getOperatingSystemMXBean();
057        this.osBeanClass = getFirstClassFound(UNIX_OPERATING_SYSTEM_BEAN_CLASS_NAMES);
058        this.openFilesMethod = detectMethod("getOpenFileDescriptorCount");
059        this.maxFilesMethod = detectMethod("getMaxFileDescriptorCount");
060    }
061
062
063    @Override
064    public Map<String, Metric> getMetrics() {
065        final Map<String, Metric> gauges = new HashMap<>();
066
067
068        if (openFilesMethod != null) {
069            gauges.put("process.files.open", (Gauge<Double>) () -> invoke(openFilesMethod));
070        }
071
072        if (maxFilesMethod != null) {
073            gauges.put("process.files.max", (Gauge<Double>) () -> invoke(maxFilesMethod));
074        }
075
076        return Collections.unmodifiableMap(gauges);
077    }
078
079
080    private double invoke(Method method) {
081        try {
082            return method != null ? (double) (long) method.invoke(osBean) : Double.NaN;
083        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
084            return Double.NaN;
085        }
086    }
087
088
089    private Method detectMethod(String name) {
090        if (osBeanClass == null) {
091            return null;
092        }
093        try {
094            // ensure the Bean we have is actually an instance of the interface
095            osBeanClass.cast(osBean);
096            return osBeanClass.getDeclaredMethod(name);
097        } catch (ClassCastException | NoSuchMethodException | SecurityException e) {
098            return null;
099        }
100    }
101
102
103    private Class<?> getFirstClassFound(List<String> classNames) {
104        for (String className : classNames) {
105            try {
106                return Class.forName(className);
107            } catch (ClassNotFoundException ignore) {
108            }
109        }
110        return null;
111    }
112
113
114}