/* * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.actuate.endpoint; import java.lang.management.ClassLoadingMXBean; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryUsage; import java.lang.management.ThreadMXBean; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; /** * A {@link PublicMetrics} implementation that provides various system-related metrics. * * @author Dave Syer * @author Christian Dupuis * @author Stephane Nicoll * @author Johannes Edmeier * @since 1.2.0 */ public class SystemPublicMetrics implements PublicMetrics, Ordered { private long timestamp; public SystemPublicMetrics() { this.timestamp = System.currentTimeMillis(); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE + 10; } @Override public Collection<Metric<?>> metrics() { Collection<Metric<?>> result = new LinkedHashSet<>(); addBasicMetrics(result); addManagementMetrics(result); return result; } /** * Add basic system metrics. * @param result the result */ protected void addBasicMetrics(Collection<Metric<?>> result) { // NOTE: ManagementFactory must not be used here since it fails on GAE Runtime runtime = Runtime.getRuntime(); result.add(newMemoryMetric("mem", runtime.totalMemory() + getTotalNonHeapMemoryIfPossible())); result.add(newMemoryMetric("mem.free", runtime.freeMemory())); result.add(new Metric<>("processors", runtime.availableProcessors())); result.add(new Metric<>("instance.uptime", System.currentTimeMillis() - this.timestamp)); } private long getTotalNonHeapMemoryIfPossible() { try { return ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed(); } catch (Throwable ex) { return 0; } } /** * Add metrics from ManagementFactory if possible. Note that ManagementFactory is not * available on Google App Engine. * @param result the result */ private void addManagementMetrics(Collection<Metric<?>> result) { try { // Add JVM up time in ms result.add(new Metric<>("uptime", ManagementFactory.getRuntimeMXBean().getUptime())); result.add(new Metric<>("systemload.average", ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage())); addHeapMetrics(result); addNonHeapMetrics(result); addThreadMetrics(result); addClassLoadingMetrics(result); addGarbageCollectionMetrics(result); } catch (NoClassDefFoundError ex) { // Expected on Google App Engine } } /** * Add JVM heap metrics. * @param result the result */ protected void addHeapMetrics(Collection<Metric<?>> result) { MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean() .getHeapMemoryUsage(); result.add(newMemoryMetric("heap.committed", memoryUsage.getCommitted())); result.add(newMemoryMetric("heap.init", memoryUsage.getInit())); result.add(newMemoryMetric("heap.used", memoryUsage.getUsed())); result.add(newMemoryMetric("heap", memoryUsage.getMax())); } /** * Add JVM non-heap metrics. * @param result the result */ private void addNonHeapMetrics(Collection<Metric<?>> result) { MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean() .getNonHeapMemoryUsage(); result.add(newMemoryMetric("nonheap.committed", memoryUsage.getCommitted())); result.add(newMemoryMetric("nonheap.init", memoryUsage.getInit())); result.add(newMemoryMetric("nonheap.used", memoryUsage.getUsed())); result.add(newMemoryMetric("nonheap", memoryUsage.getMax())); } private Metric<Long> newMemoryMetric(String name, long bytes) { return new Metric<>(name, bytes / 1024); } /** * Add thread metrics. * @param result the result */ protected void addThreadMetrics(Collection<Metric<?>> result) { ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); result.add( new Metric<>("threads.peak", (long) threadMxBean.getPeakThreadCount())); result.add(new Metric<>("threads.daemon", (long) threadMxBean.getDaemonThreadCount())); result.add(new Metric<>("threads.totalStarted", threadMxBean.getTotalStartedThreadCount())); result.add(new Metric<>("threads", (long) threadMxBean.getThreadCount())); } /** * Add class loading metrics. * @param result the result */ protected void addClassLoadingMetrics(Collection<Metric<?>> result) { ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean(); result.add( new Metric<>("classes", (long) classLoadingMxBean.getLoadedClassCount())); result.add(new Metric<>("classes.loaded", classLoadingMxBean.getTotalLoadedClassCount())); result.add(new Metric<>("classes.unloaded", classLoadingMxBean.getUnloadedClassCount())); } /** * Add garbage collection metrics. * @param result the result */ protected void addGarbageCollectionMetrics(Collection<Metric<?>> result) { List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory .getGarbageCollectorMXBeans(); for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) { String name = beautifyGcName(garbageCollectorMXBean.getName()); result.add(new Metric<>("gc." + name + ".count", garbageCollectorMXBean.getCollectionCount())); result.add(new Metric<>("gc." + name + ".time", garbageCollectorMXBean.getCollectionTime())); } } /** * Turn GC names like 'PS Scavenge' or 'PS MarkSweep' into something that is more * metrics friendly. * @param name the source name * @return a metric friendly name */ private String beautifyGcName(String name) { return StringUtils.replace(name, " ", "_").toLowerCase(); } }