1. 简介
在日常开发中一些关键的业务服务,期望在高并发状态下可以正常工作,或在异常情况时可以记录当时的性能信息,所以就需要进行监控。常见的监控例如:Prometheus可以实现这个需求,如果需要更加简单方便的自主监控能力,可以引入本博客中的方案。
2. 相关博客
Promtail+Loki+Grafana搭建轻量级日志管理平台SpringBoot 2.x + Prometheus + Grafana 实现应用监控
3. 示例代码
4.0.0 com.c3stones spring-boot-monitor-demo1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent2.7.8 cn.hutool hutool-core5.8.12 com.alibaba.fastjson2 fastjson22.0.24 org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-maven-plugin
在resources下新建配置文件application.yml。
# 预警配置 monitor: warn: enabled: true cpu: stage1: 85 stage2: 95 memory: stage1: 85 stage2: 95 disk: stage1: 85 stage2: 95
import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * 预警配置 * * @author CL */ @Getter @Component public class WarnConfig { /** * 预警开关 */ @Value("${monitor.warn.enabled:true}") private Boolean enabled; /** * CPU - 阶段1 */ @Value("${monitor.warn.cpu.stage1:85}") private Double cpuStage1; /** * CPU - 阶段2 */ @Value("${monitor.warn.cpu.stage2:95}") private Double cpuStage2; /** * 内存 - 阶段1 */ @Value("${monitor.warn.memory.stage1:85}") private Double memoryStage1; /** * 内存 - 阶段2 */ @Value("${monitor.warn.memory.stage2:95}") private Double memoryStage2; /** * 磁盘 - 阶段1 */ @Value("${monitor.warn.disk.stage1:85}") private Double diskStage1; /** * 磁盘 - 阶段2 */ @Value("${monitor.warn.disk.stage2:95}") private Double diskStage2; }
import cn.hutool.core.util.NumberUtil; import lombok.AllArgsConstructor; import lombok.Getter; import java.util.Objects; /** * 预警状态枚举类 * * @author CL */ @Getter @AllArgsConstructor public enum WarnSateEnum { /** * 正常 */ GREEN(1), /** * 警告 */ YELLOW(2), /** * 紧急 */ RED(3), ; private final int value; /** * 根据值获取枚举 * * @param value 值 * @return {@link WarnSateEnum} */ public static WarnSateEnum findByValue(Integer value) { if (Objects.nonNull(value)) { for (WarnSateEnum warnState : values()) { if (NumberUtil.equals(warnState.getValue(), value)) { return warnState; } } } return GREEN; } /** * 获取高优先级 * * @param warnSateEnum 预警状态 * @return */ public WarnSateEnum max(WarnSateEnum warnSateEnum) { if (Objects.nonNull(warnSateEnum)) { return findByValue(Math.max(this.value, warnSateEnum.value)); } return this; } }
/** * 字节转换工具类 * * @author CL */ public class ByteUtil { private static final int UNIT = 1024; /** * 格式化字节大小 * * @param byteSize 字节大小 * @return {@link String} */ public static String formatByteSize(long byteSize) { if (byteSize <= -1) { return String.valueOf(byteSize); } double size = 1.0 * byteSize; String type; if ((int) Math.floor(size / UNIT) <= 0) { // 不足1KB type = "B"; return format(size, type); } size = size / UNIT; if ((int) Math.floor(size / UNIT) <= 0) { // 不足1MB type = "KB"; return format(size, type); } size = size / UNIT; if ((int) Math.floor(size / UNIT) <= 0) { // 不足1GB type = "MB"; return format(size, type); } size = size / UNIT; if ((int) Math.floor(size / UNIT) <= 0) { // 不足1TB type = "GB"; return format(size, type); } size = size / UNIT; if ((int) Math.floor(size / UNIT) <= 0) { // 不足1PB type = "TB"; return format(size, type); } size = size / UNIT; if ((int) Math.floor(size / UNIT) <= 0) { type = "PB"; return format(size, type); } return ">PB"; } /** * 格式化字节大小为指定单位 * * @param size 字节大小 * @param type 单位类型 * @return {@link String} */ private static String format(double size, String type) { int precision; if (size * 100 % 10 > 0) { precision = 2; } else if (size * 10 % 10 > 0) { precision = 1; } else { precision = 0; } String formatStr = "%." + precision + "f"; if ("KB".equals(type)) { return String.format(formatStr, (size)) + "KB"; } else if ("MB".equals(type)) { return String.format(formatStr, (size)) + "MB"; } else if ("GB".equals(type)) { return String.format(formatStr, (size)) + "GB"; } else if ("TB".equals(type)) { return String.format(formatStr, (size)) + "TB"; } else if ("PB".equals(type)) { return String.format(formatStr, (size)) + "PB"; } return String.format(formatStr, (size)) + "B"; } }
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * 打印布局 * * @author CL */ @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) public class Layout { // 横线和竖线 private final static String H_LINE = "-"; private final static String V_LINE = "┊"; /** * 大标题 */ private String headline; /** * 数据 */ private ArrayList
import com.c3stones.monitor.print.Layout; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 服务器信息 * * @author CL */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Server { /** * 主机名 */ private String hostName; /** * 操作系统名称 */ private String osName; /** * 操作系统版本 */ private String osVersion; /** * 系统架构 */ private String arch; /** * 本地IP */ private String localIp; @Override public String toString() { return Layout.Table.of( Layout.Row.of("HostName", hostName), Layout.Row.of("OS", osName + "/" + osVersion), Layout.Row.of("Arch", arch), Layout.Row.of("LocalIp", localIp) ).toString(); } }
import cn.hutool.core.date.BetweenFormatter; import cn.hutool.core.date.DateUtil; import com.c3stones.monitor.print.Layout; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.time.LocalDateTime; import java.util.List; import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; /** * JVM信息 * * @author CL */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Jvm { /** * 虚拟机名称 */ private String vmName; /** * JDK版本 */ private String jdkVersion; /** * JavaHome */ private String javaHome; /** * 进程号 */ private String pid; /** * 启动时间 */ private LocalDateTime startTime; /** * 运行时长 */ private long runtime; /** * 启动餐参数 */ private ListstartParameters; @Override public String toString() { return Layout.Table.of( Layout.Row.of("VM Name", vmName), Layout.Row.of("JDK Version", jdkVersion), Layout.Row.of("Java Home", javaHome), Layout.Row.of("PID", pid), Layout.Row.of("StartTime", NORM_DATETIME_FORMATTER.format(startTime)), Layout.Row.of("RunTime", DateUtil.formatBetween(runtime, BetweenFormatter.Level.SECOND) .replaceAll(BetweenFormatter.Level.SECOND.getName(), "s") .replaceAll(BetweenFormatter.Level.MINUTE.getName(), "m") .replaceAll(BetweenFormatter.Level.HOUR.getName(), "h") .replaceAll(BetweenFormatter.Level.DAY.getName(), "d")), Layout.Row.of("Start Parameters", startParameters.size(), startParameters) ).toString(); } }
import cn.hutool.core.util.NumberUtil; import com.c3stones.monitor.print.Layout; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.math.RoundingMode; /** * CPU信息 * * @author CL */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Cpu { /** * 可用处理器数 */ private int availableProcssors; /** * 系统CPU使用率 */ private BigDecimal systemCpuUsage; /** * 当前进程CPU使用率 */ private BigDecimal processCpuUsage; @Override public String toString() { return Layout.Table.of( Layout.Row.of("Available Processors", availableProcssors), Layout.Row.of("System CPU Usage", NumberUtil.mul(systemCpuUsage, 100).setScale(2, RoundingMode.HALF_UP) + "%"), Layout.Row.of("Process CPU Usage", NumberUtil.mul(processCpuUsage, 100).setScale(2, RoundingMode.HALF_UP) + "%") ).toString(); } }
import cn.hutool.core.util.NumberUtil; import com.c3stones.monitor.print.Layout; import com.c3stones.monitor.utils.ByteUtil; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.math.RoundingMode; /** * 内存信息 * * @author CL */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Memory { /** * 总物理内存 */ private long totalPhysicalMemory; /** * 空闲物理内存 */ private long freePhysicalMemory; /** * 已使用物理内存 */ private long usedPhysicalMemory; /** * 物理内存使用率 */ private BigDecimal physicalMemoryUsage; /** * 总内存 */ private long totalMemory; /** * 空闲内存 */ private long freeMemory; /** * 已用内存 */ private long usedMemory; /** * 最大内存 */ private long maxMemory; /** * 最大可用内存 */ private long maxUseMemory; /** * 内存使用率 */ private BigDecimal memoryUsage; @Override public String toString() { return Layout.Table.of( Layout.Row.of("Total Physical Memory", ByteUtil.formatByteSize(totalPhysicalMemory)), Layout.Row.of("Free Physical Memory", ByteUtil.formatByteSize(freePhysicalMemory)), Layout.Row.of("Used Physical Memory", ByteUtil.formatByteSize(usedPhysicalMemory)), Layout.Row.of("Physical Memory Usage", NumberUtil.mul(physicalMemoryUsage, 100).setScale(2, RoundingMode.HALF_UP) + "%"), Layout.Row.of("Total Memory", ByteUtil.formatByteSize(totalMemory)), Layout.Row.of("Free Memory", ByteUtil.formatByteSize(freeMemory)), Layout.Row.of("Used Memory", ByteUtil.formatByteSize(usedMemory)), Layout.Row.of("Max Memory", ByteUtil.formatByteSize(maxMemory)), Layout.Row.of("Max Use Memory", ByteUtil.formatByteSize(maxUseMemory)), Layout.Row.of("Memory Usage", NumberUtil.mul(memoryUsage, 100).setScale(2, RoundingMode.HALF_UP) + "%") ).toString(); } }
import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Opt; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import com.c3stones.monitor.print.Layout; import com.c3stones.monitor.utils.ByteUtil; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; import java.util.stream.Collectors; /** * 磁盘信息 * * @author CL */ @Data @NoArgsConstructor @AllArgsConstructor public class Disk { /** * 盘符 */ private Listdevices; @Override public String toString() { return Opt.ofNullable(devices).orElse(ListUtil.empty()).stream().map(Drive::toString).collect(Collectors.joining(StrUtil.LF)); } /** * 盘符 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class Drive { /** * 盘符名称 */ private String name; /** * 总大小 */ private long totalSpace; /** * 空闲大小 */ private long freeSpace; /** * 已用大小 */ private long usedSpace; /** * 盘符使用率 */ private BigDecimal driveUsage; @Override public String toString() { return Layout.Table.of(name, Layout.Row.of("Total Space", ByteUtil.formatByteSize(totalSpace)), Layout.Row.of("Free Space", ByteUtil.formatByteSize(freeSpace)), Layout.Row.of("Used Space", ByteUtil.formatByteSize(usedSpace)), Layout.Row.of("Drive Space", NumberUtil.mul(driveUsage, 100).setScale(2, RoundingMode.HALF_UP) + "%") ).toString(); } } }
import com.c3stones.monitor.print.Layout; import com.c3stones.monitor.utils.ByteUtil; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 堆/非堆信息 * * @author CL */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Heap { /** * 堆初始化大小 */ private long heapInit; /** * 堆最大大小 */ private long heapMaxMemory; /** * 堆已用大小 */ private long heapUsedMemory; /** * 堆空闲大小 */ private long heapFreeMemory; /** * 非堆初始化大小 */ private long nonHeapInit; /** * 非堆最大大小 */ private long nonHeapMaxMemory; /** * 非堆已用大小 */ private long nonHeapUsedMemory; /** * 非堆空闲大小 */ private long nonHeapFreeMemory; @Override public String toString() { return Layout.Table.of( Layout.Row.of("Heap Init Size", ByteUtil.formatByteSize(heapInit)), Layout.Row.of("Heap Max Size", ByteUtil.formatByteSize(heapMaxMemory)), Layout.Row.of("Heap Used Size", ByteUtil.formatByteSize(heapUsedMemory)), Layout.Row.of("Heap Free Size", ByteUtil.formatByteSize(heapFreeMemory)), Layout.Row.of("NonHeap Init Size", ByteUtil.formatByteSize(nonHeapInit)), Layout.Row.of("NonHeap Max Size", ByteUtil.formatByteSize(nonHeapMaxMemory)), Layout.Row.of("NonHeap Used Size", ByteUtil.formatByteSize(nonHeapUsedMemory)), Layout.Row.of("NonHeap Free Size", ByteUtil.formatByteSize(nonHeapFreeMemory)) ).toString(); } }
import com.c3stones.monitor.print.Layout; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 线程信息 * * @author CL */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Thread { /** * 活动线程数 */ private int threadCount; /** * 峰值活动线程数 */ private int peakThreadCount; /** * 守护线程数 */ private int daemonThreadCount; /** * 非守护线程数 */ private int nonDaemonThreadCount; /** * 总线程数 */ private long totalStartedThreadCount; @Override public String toString() { return Layout.Table.of( Layout.Row.of("Active Thread Count", threadCount), Layout.Row.of("Active Peak Thread Count", peakThreadCount), Layout.Row.of("Daemon Thread Count", daemonThreadCount), Layout.Row.of("NonDaemon Thread Count", nonDaemonThreadCount), Layout.Row.of("Total Thread Count", totalStartedThreadCount) ).toString(); } }
import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.BetweenFormatter; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Opt; import cn.hutool.core.util.StrUtil; import com.c3stones.monitor.print.Layout; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; import java.util.stream.Collectors; /** * 垃圾回收信息 * * @author CL */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Gc { /** * GC收集器 */ private Listcollectors; @Override public String toString() { return Opt.ofNullable(collectors).orElse(ListUtil.empty()).stream().map(Gc.GcCollector::toString).collect(Collectors.joining(StrUtil.LF)); } /** * GC收集器 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class GcCollector { /** * 收集器名称 */ private String name; /** * 收集总数 */ private long count; /** * 收集耗时 */ private long time; @Override public String toString() { return Layout.Table.of(name, Layout.Row.of("Count", count), Layout.Row.of("Time", DateUtil.formatBetween(time, BetweenFormatter.Level.MILLISECOND) .replaceAll(BetweenFormatter.Level.MILLISECOND.getName(), "ms") .replaceAll(BetweenFormatter.Level.SECOND.getName(), "s") .replaceAll(BetweenFormatter.Level.MINUTE.getName(), "m") .replaceAll(BetweenFormatter.Level.HOUR.getName(), "h") .replaceAll(BetweenFormatter.Level.DAY.getName(), "d")) ).toString(); } } }
import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.lang.Opt; import cn.hutool.core.text.CharPool; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import com.qax.ngsoc.indicator.common.config.GlobalConfigs; import com.qax.ngsoc.indicator.monitor.bo.Thread; import com.qax.ngsoc.indicator.monitor.bo.*; import com.qax.ngsoc.indicator.monitor.config.WarnConfig; import com.qax.ngsoc.indicator.monitor.config.WarnSateEnum; import com.qax.ngsoc.indicator.monitor.print.Layout; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.GetMapping; import javax.annotation.Resource; import java.io.File; import java.lang.management.*; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 监控 Controller * * @author caolei02 */ @Slf4j @EnableScheduling public abstract class MonitorBaseController { @Resource protected GlobalConfigs globalConfigs; /** * 巡检 */ @Scheduled(cron = "0 0/10 * * * ?") public void patrol() { // 发现预警 warn(); } /** * 预警 * * @return {@link WarnSateEnum} */ @GetMapping("/warn") public WarnSateEnum warn() { WarnConfig warnConfig = globalConfigs.getMonitorWarn(); // 不开启,则返回空 if (Objects.isNull(warnConfig) || BooleanUtil.isFalse(warnConfig.getEnabled())) return null; WarnSateEnum result = WarnSateEnum.GREEN; // 判断CPU Cpu cpu = cpu(); if (NumberUtil.sub(cpu.getSystemCpuUsage().doubleValue() * 100, warnConfig.getCpuStage2().doubleValue()) >= 0 || NumberUtil.sub(cpu.getProcessCpuUsage().doubleValue() * 100, warnConfig.getCpuStage2().doubleValue()) >= 0) { result = result.max(WarnSateEnum.RED); log.warn("Monitoring capability detection cpu warning!!!" + StrUtil.LF + cpu); } else if (NumberUtil.sub(cpu.getSystemCpuUsage().doubleValue() * 100, warnConfig.getCpuStage1().doubleValue()) >= 0 || NumberUtil.sub(cpu.getProcessCpuUsage().doubleValue() * 100, warnConfig.getCpuStage1().doubleValue()) >= 0) { result = result.max(WarnSateEnum.YELLOW); log.warn("Monitoring capability detection cpu warning!!!" + StrUtil.LF + cpu); } // 判断内存 Memory memory = memory(); if (NumberUtil.sub(memory.getPhysicalMemoryUsage().doubleValue() * 100, warnConfig.getMemoryStage2().doubleValue()) >= 0 || NumberUtil.sub(memory.getMemoryUsage().doubleValue() * 100, warnConfig.getMemoryStage2().doubleValue()) >= 0) { result = result.max(WarnSateEnum.RED); log.warn("Monitoring capability detection memory warning!!!" + StrUtil.LF + memory); } else if (NumberUtil.sub(memory.getPhysicalMemoryUsage().doubleValue() * 100, warnConfig.getMemoryStage1().doubleValue()) >= 0 || NumberUtil.sub(memory.getMemoryUsage().doubleValue() * 100, warnConfig.getMemoryStage1().doubleValue()) >= 0) { result = result.max(WarnSateEnum.YELLOW); log.warn("Monitoring capability detection memory warning!!!" + StrUtil.LF + memory); } // 判断磁盘 Disk disk = disk(); double diskTotalUsage = Opt.ofNullable(disk.getDevices().stream().map(Disk.Drive::getDriveUsage) .mapToDouble(BigDecimal::doubleValue).average()).map(OptionalDouble::getAsDouble) .orElse(BigInteger.ZERO.doubleValue()); if (NumberUtil.sub(diskTotalUsage * 100, warnConfig.getDiskStage2().doubleValue()) >= 0) { result = result.max(WarnSateEnum.RED); log.warn("Monitoring capability detection disk warning!!!" + StrUtil.LF + disk); } else if (NumberUtil.sub(diskTotalUsage * 100, warnConfig.getDiskStage1().doubleValue()) >= 0) { result = result.max(WarnSateEnum.YELLOW); log.warn("Monitoring capability detection disk warning!!!" + StrUtil.LF + disk); } return result; } /** * 打印 * * @return {@link String} */ @GetMapping({"", "/print"}) public String print(String cmd) { boolean isAll = StrUtil.equalsIgnoreCase("all", cmd); Map> items = new LinkedHashMap<>(); items.put("Work State", unused -> work()); items.put("Kafka", unused -> kafka()); items.put("Server Info", unused -> server()); items.put("JVM", unused -> jvm()); items.put("CPU", unused -> cpu()); items.put("Memory", unused -> memory()); items.put("Disk", unused -> disk()); items.put("Heap/NonHeap", unused -> heap()); items.put("Thread", unused -> thread()); items.put("ThreadPool", unused -> threadPool()); items.put("GC", unused -> gc()); Stream streams = items.entrySet().stream() .filter(entry -> isAll ? Boolean.TRUE : StrUtil.containsAnyIgnoreCase(entry.getKey(), Opt.ofBlankAble(cmd).orElse("CPU&Memory&ThreadPool").split(Objects.toString(CharPool.AMP)))) .map(entry -> Layout.of(entry.getKey(), entry.getValue().apply(null))); String tip = !isAll ? "Tip: Set parameters [cmd=all] will return more information." + StrUtil.LF + StrUtil.LF : StrUtil.EMPTY; return tip + streams.map(Layout::toString).collect(Collectors.joining(StrUtil.LF)); } /** * 工作状态 * * @return {@link Work} */ protected abstract Work work(); /** * KAFKA信息 * * @return {@link Kafka} */ protected abstract Kafka kafka(); /** * 服务器信息 * * @return {@link Server} */ @SneakyThrows protected Server server() { InetAddress inetAddress = InetAddress.getLocalHost(); OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); return Server.builder() .hostName(inetAddress.getHostName()) .osName(operatingSystemMXBean.getName()) .osVersion(operatingSystemMXBean.getVersion()) .arch(operatingSystemMXBean.getArch()) .localIp(inetAddress.getHostAddress()) .build(); } /** * Java虚拟机信息 * * @return {@link Jvm} */ protected Jvm jvm() { RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); Map systemProperties = runtimeMXBean.getSystemProperties(); return Jvm.builder() .vmName(runtimeMXBean.getVmName()) .jdkVersion(systemProperties.get("java.runtime.version")) .javaHome(systemProperties.get("java.home")) .pid(systemProperties.get("PID")) .startTime(LocalDateTimeUtil.of(runtimeMXBean.getStartTime())) .runtime(runtimeMXBean.getUptime()) .startParameters(runtimeMXBean.getInputArguments()) .build(); } /** * CPU * * @return {@link Cpu} */ protected Cpu cpu() { OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); JSONObject operatingSystemJson = JSONObject.parseObject(JSONObject.toJSONString(operatingSystemMXBean)); return Cpu.builder() .availableProcssors(operatingSystemMXBean.getAvailableProcessors()) .systemCpuUsage(operatingSystemJson.getBigDecimal("systemCpuLoad")) .processCpuUsage(operatingSystemJson.getBigDecimal("processCpuLoad")) .build(); } /** * 内存 * * @return {@link Memory} */ protected Memory memory() { OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); JSONObject operatingSystemJson = JSONObject.parseObject(JSONObject.toJSONString(operatingSystemMXBean)); long totalPhysicalMemory = operatingSystemJson.getLongValue("totalPhysicalMemorySize"); long freePhysicalMemory = operatingSystemJson.getLongValue("freePhysicalMemorySize"); long usedPhysicalMemory = totalPhysicalMemory - freePhysicalMemory; Runtime runtime = Runtime.getRuntime(); return Memory.builder() .totalPhysicalMemory(totalPhysicalMemory) .freePhysicalMemory(freePhysicalMemory) .usedPhysicalMemory(usedPhysicalMemory) .physicalMemoryUsage(NumberUtil.toBigDecimal(NumberUtil.div(usedPhysicalMemory, totalPhysicalMemory))) .totalMemory(runtime.totalMemory()) .freeMemory(runtime.freeMemory()) .usedMemory(runtime.totalMemory() - runtime.freeMemory()).maxMemory(runtime.maxMemory()) .maxUseMemory(runtime.maxMemory() - runtime.totalMemory() + runtime.freeMemory()) .memoryUsage(NumberUtil.toBigDecimal(NumberUtil.div(runtime.totalMemory() - runtime.freeMemory(), runtime.totalMemory()))) .build(); } /** * 磁盘信息 * * @return {@link Disk} */ protected Disk disk() { List drives = Stream.of(File.listRoots()).map(file -> Disk.Drive.builder() .name(file.toString()) .totalSpace(file.getTotalSpace()) .freeSpace(file.getFreeSpace()) .usedSpace(file.getTotalSpace() - file.getFreeSpace()) .driveUsage(NumberUtil.toBigDecimal(NumberUtil.div(file.getTotalSpace() - file.getFreeSpace(), file.getTotalSpace()))) .build() ).collect(Collectors.toList()); return new Disk(drives); } /** * 堆/非堆 * * @return {@link StringBuilder} */ protected Heap heap() { MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); return Heap.builder() .heapInit(heapMemoryUsage.getInit()) .heapMaxMemory(heapMemoryUsage.getMax()) .heapUsedMemory(heapMemoryUsage.getUsed()) .heapFreeMemory(heapMemoryUsage.getMax() - heapMemoryUsage.getUsed()) .nonHeapInit(nonHeapMemoryUsage.getInit()) .nonHeapMaxMemory(nonHeapMemoryUsage.getMax()) .nonHeapUsedMemory(nonHeapMemoryUsage.getUsed()) .nonHeapFreeMemory(nonHeapMemoryUsage.getMax() - nonHeapMemoryUsage.getUsed()) .build(); } /** * 线程信息 * * @return {@link Thread} */ protected Thread thread() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); return Thread.builder() .threadCount(threadMXBean.getThreadCount()) .peakThreadCount(threadMXBean.getPeakThreadCount()) .daemonThreadCount(threadMXBean.getDaemonThreadCount()) .nonDaemonThreadCount(threadMXBean.getThreadCount() - threadMXBean.getDaemonThreadCount()) .totalStartedThreadCount(threadMXBean.getTotalStartedThreadCount()) .build(); } /** * 线程池 * * @return {@link ThreadPool} */ protected abstract ThreadPool threadPool(); /** * 垃圾回收信息 * * @return {@link Gc} */ private Gc gc() { List collectors = ManagementFactory.getGarbageCollectorMXBeans().stream().map(garbageCollectorMXBean -> Gc.GcCollector.builder() .name(garbageCollectorMXBean.getName()) .count(garbageCollectorMXBean.getCollectionCount()) .time(garbageCollectorMXBean.getCollectionTime()) .build()).sorted(Comparator.comparing(Gc.GcCollector::getCount).reversed()) .collect(Collectors.toList()); return Gc.builder().collectors(collectors).build(); } }
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 启动类 * * @author CL */ @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
4. 测试
curl http://127.0.0.1:8080/monitor/print
接口响应:
Server Info: ------------------------------------ ┊ HostName ┊ c3sontes-pc ┊ ┊ OS ┊ Windows 10/10.0 ┊ ┊ Arch ┊ amd64 ┊ ┊ LocalIp ┊ 127.0.0.1 ┊ ------------------------------------ JVM: -------------------------------------------------------------- ┊ VM Name ┊ Java HotSpot(TM) 64-Bit Server VM ┊ ┊ JDK Version ┊ 1.8.0_102-b14 ┊ ┊ Java Home ┊ C:\Java\jdk1.8.0_102\jre ┊ ┊ PID ┊ 29128 ┊ ┊ StartTime ┊ 2023-03-09 21:14:42 ┊ ┊ RunTime ┊ 1m8s ┊ ┊ Start Parameters ┊ -Xms1g ┊ ┊ ┊ -Xmx2g ┊ ┊ ┊ -Xss512k ┊ ┊ ┊ -XX:MetaspaceSize=128m ┊ ┊ ┊ -XX:MaxMetaspaceSize=256m ┊ ┊ ┊ -Dfile.encoding=UTF-8 ┊ -------------------------------------------------------------- CPU: --------------------------------------- ┊ Available Processors ┊ 8 ┊ ┊ System CPU Usage ┊ 54.10% ┊ ┊ Process CPU Usage ┊ 12.63% ┊ --------------------------------------- Memory: ------------------------------------------ ┊ Total Physical Memory ┊ 15.76GB ┊ ┊ Free Physical Memory ┊ 2.22GB ┊ ┊ Used Physical Memory ┊ 13.53GB ┊ ┊ Physical Memory Usage ┊ 85.89% ┊ ┊ Total Memory ┊ 258.5MB ┊ ┊ Free Memory ┊ 241.64MB ┊ ┊ Used Memory ┊ 16.86MB ┊ ┊ Max Memory ┊ 3.50GB ┊ ┊ Max Use Memory ┊ 3.49GB ┊ ┊ Memory Usage ┊ 6.52% ┊ ------------------------------------------ Disk: -------------------------------- ┊C:\ ┊ -------------------------------- ┊ Total Space ┊ 150.00GB ┊ ┊ Free Space ┊ 53.59GB ┊ ┊ Used Space ┊ 96.41GB ┊ ┊ Drive Space ┊ 64.27% ┊ -------------------------------- -------------------------------- ┊D:\ ┊ -------------------------------- ┊ Total Space ┊ 326.52GB ┊ ┊ Free Space ┊ 263.06GB ┊ ┊ Used Space ┊ 63.46GB ┊ ┊ Drive Space ┊ 19.43% ┊ -------------------------------- Heap/NonHeap: --------------------------------------- ┊ Heap Init Size ┊ 254MB ┊ ┊ Heap Max Size ┊ 3.50GB ┊ ┊ Heap Used Size ┊ 16.86MB ┊ ┊ Heap Free Size ┊ 3.49GB ┊ ┊ NonHeap Init Size ┊ 2.44MB ┊ ┊ NonHeap Max Size ┊ -1 ┊ ┊ NonHeap Used Size ┊ 43.12MB ┊ ┊ NonHeap Free Size ┊ -45210705 ┊ --------------------------------------- Thread: --------------------------------------- ┊ Active Thread Count ┊ 22 ┊ ┊ Active Peak Thread Count ┊ 25 ┊ ┊ Daemon Thread Count ┊ 18 ┊ ┊ NonDaemon Thread Count ┊ 4 ┊ ┊ Total Thread Count ┊ 38 ┊ --------------------------------------- GC: ----------------------- ┊PS Scavenge ┊ ----------------------- ┊ Count ┊ 5 ┊ ┊ Time ┊ 240ms ┊ ----------------------- ----------------------- ┊PS MarkSweep ┊ ----------------------- ┊ Count ┊ 2 ┊ ┊ Time ┊ 266ms ┊ -----------------------
curl http://127.0.0.1:8080/monitor/print?cmd=memory
接口响应:
Tip: Set parameters [cmd=all] will return more information. Memory: ------------------------------------------ ┊ Total Physical Memory ┊ 15.76GB ┊ ┊ Free Physical Memory ┊ 2.85GB ┊ ┊ Used Physical Memory ┊ 12.91GB ┊ ┊ Physical Memory Usage ┊ 81.93% ┊ ┊ Total Memory ┊ 249MB ┊ ┊ Free Memory ┊ 226.00MB ┊ ┊ Used Memory ┊ 23.00MB ┊ ┊ Max Memory ┊ 3.50GB ┊ ┊ Max Use Memory ┊ 3.48GB ┊ ┊ Memory Usage ┊ 9.24% ┊ ------------------------------------------
curl http://127.0.0.1:8080/monitor/print
接口响应:
Tip: Set parameters [cmd=all] will return more information. CPU: --------------------------------------- ┊ Available Processors ┊ 8 ┊ ┊ System CPU Usage ┊ 67.12% ┊ ┊ Process CPU Usage ┊ 0.07% ┊ --------------------------------------- Memory: ------------------------------------------ ┊ Total Physical Memory ┊ 15.76GB ┊ ┊ Free Physical Memory ┊ 3.15GB ┊ ┊ Used Physical Memory ┊ 12.61GB ┊ ┊ Physical Memory Usage ┊ 80.00% ┊ ┊ Total Memory ┊ 249MB ┊ ┊ Free Memory ┊ 218.76MB ┊ ┊ Used Memory ┊ 30.24MB ┊ ┊ Max Memory ┊ 3.50GB ┊ ┊ Max Use Memory ┊ 3.47GB ┊ ┊ Memory Usage ┊ 12.14% ┊ ------------------------------------------ GC: ----------------------- ┊PS Scavenge ┊ ----------------------- ┊ Count ┊ 5 ┊ ┊ Time ┊ 240ms ┊ ----------------------- ----------------------- ┊PS MarkSweep ┊ ----------------------- ┊ Count ┊ 2 ┊ ┊ Time ┊ 266ms ┊ -----------------------
curl http://127.0.0.1:8080/monitor/warn
接口响应:
"YELLOW"
日志打印:
2023-03-09 21:10:50.110 WARN 97132 --- [nio-8080-exec-2] com.c3stones.monitor.MonitorController : Monitoring capability detection memory warning!!! ------------------------------------------ ┊ Total Physical Memory ┊ 15.76GB ┊ ┊ Free Physical Memory ┊ 2.23GB ┊ ┊ Used Physical Memory ┊ 13.53GB ┊ ┊ Physical Memory Usage ┊ 85.84% ┊ ┊ Total Memory ┊ 258.5MB ┊ ┊ Free Memory ┊ 228.27MB ┊ ┊ Used Memory ┊ 30.23MB ┊ ┊ Max Memory ┊ 3.50GB ┊ ┊ Max Use Memory ┊ 3.47GB ┊ ┊ Memory Usage ┊ 11.70% ┊ ------------------------------------------
5. 项目地址
spring-boot-monitor-demo