融合通信
parent
43652655de
commit
5ab0418957
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
package org.dromara.system.config;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.support.jakarta.StatViewServlet;
|
||||
import com.alibaba.druid.support.jakarta.WebStatFilter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Druid配置类
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Configuration
|
||||
public class DruidConfig {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
/**
|
||||
* 配置Druid监控页面
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
|
||||
ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
|
||||
|
||||
// 配置监控页面访问控制
|
||||
Map<String, String> initParams = new HashMap<>();
|
||||
initParams.put("loginUsername", "admin");
|
||||
initParams.put("loginPassword", "admin");
|
||||
initParams.put("allow", ""); // 默认允许所有IP访问
|
||||
// initParams.put("deny", ""); // 拒绝访问的IP
|
||||
|
||||
registrationBean.setInitParameters(initParams);
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置Web应用监控Filter
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public FilterRegistrationBean<WebStatFilter> druidWebStatFilter() {
|
||||
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
|
||||
|
||||
// 配置拦截规则
|
||||
Map<String, String> initParams = new HashMap<>();
|
||||
initParams.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
|
||||
initParams.put("profileEnable", "true");
|
||||
initParams.put("sessionStatEnable", "true");
|
||||
|
||||
filterRegistrationBean.setInitParameters(initParams);
|
||||
filterRegistrationBean.addUrlPatterns("/*");
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置数据源监控统计
|
||||
*/
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.filter.stat.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public void druidStatFilter() throws SQLException {
|
||||
if (dataSource instanceof DruidDataSource) {
|
||||
DruidDataSource druidDataSource = (DruidDataSource) dataSource;
|
||||
// 开启监控统计功能
|
||||
druidDataSource.setFilters("stat,wall,slf4j");
|
||||
// 慢SQL记录
|
||||
druidDataSource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
package org.dromara.system.controller.monitor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.system.service.IDruidMonitorService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Druid监控控制器
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/monitor/druid")
|
||||
public class DruidMonitorController {
|
||||
|
||||
private final IDruidMonitorService druidMonitorService;
|
||||
|
||||
/**
|
||||
* 获取数据源监控信息
|
||||
*/
|
||||
@GetMapping("/datasource")
|
||||
public R<List<Object>> getDataSourceInfo() {
|
||||
return R.ok(druidMonitorService.getDataSourceInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SQL监控信息
|
||||
*/
|
||||
@GetMapping("/sql")
|
||||
public R<List<Object>> getSqlInfo() {
|
||||
return R.ok(druidMonitorService.getSqlInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SQL防火墙信息
|
||||
*/
|
||||
@GetMapping("/firewall")
|
||||
public R<List<Object>> getSqlFirewallInfo() {
|
||||
return R.ok(druidMonitorService.getSqlFirewallInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取URI监控信息
|
||||
*/
|
||||
@GetMapping("/uri")
|
||||
public R<List<Object>> getUriInfo() {
|
||||
return R.ok(druidMonitorService.getUriInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置SQL监控统计信息
|
||||
*/
|
||||
@PostMapping("/sql/reset")
|
||||
public R<Void> resetSqlStat() {
|
||||
druidMonitorService.resetSqlStat();
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃连接信息
|
||||
*/
|
||||
@GetMapping("/activeConnections")
|
||||
public R<List<Object>> getActiveConnections() {
|
||||
return R.ok(druidMonitorService.getActiveConnections());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
package org.dromara.system.controller.monitor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.system.domain.vo.SystemMonitorVo;
|
||||
import org.dromara.system.service.ISystemMonitorService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 系统监控
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/monitor")
|
||||
public class SystemMonitorController {
|
||||
|
||||
private final ISystemMonitorService systemMonitorService;
|
||||
|
||||
/**
|
||||
* 获取系统监控信息
|
||||
*/
|
||||
@GetMapping("/sysInfo")
|
||||
public R<SystemMonitorVo> getInfo() {
|
||||
return R.ok(systemMonitorService.getAllSystemInfo());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
|
||||
package org.dromara.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* SQL防火墙记录实体
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_sql_firewall")
|
||||
public class SysSqlFirewall implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* SQL语句
|
||||
*/
|
||||
private String sql;
|
||||
|
||||
/**
|
||||
* SQL类型
|
||||
*/
|
||||
private String sqlType;
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 是否被拦截
|
||||
*/
|
||||
private Boolean blocked;
|
||||
|
||||
/**
|
||||
* 拦截原因
|
||||
*/
|
||||
private String blockReason;
|
||||
|
||||
/**
|
||||
* 来源IP
|
||||
*/
|
||||
private String sourceIp;
|
||||
|
||||
/**
|
||||
* 执行用户
|
||||
*/
|
||||
private String executeUser;
|
||||
|
||||
/**
|
||||
* 执行时间
|
||||
*/
|
||||
private LocalDateTime executeTime;
|
||||
|
||||
/**
|
||||
* 风险等级
|
||||
*/
|
||||
private String riskLevel;
|
||||
|
||||
/**
|
||||
* 是否包含敏感字段
|
||||
*/
|
||||
private Boolean hasSensitiveFields;
|
||||
|
||||
/**
|
||||
* 影响行数
|
||||
*/
|
||||
private Integer affectRows;
|
||||
|
||||
/**
|
||||
* 数据源名称
|
||||
*/
|
||||
private String dataSourceName;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
package org.dromara.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* SQL监控历史记录实体
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_sql_monitor")
|
||||
public class SysSqlMonitor implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* SQL语句
|
||||
*/
|
||||
private String sql;
|
||||
|
||||
/**
|
||||
* 执行次数
|
||||
*/
|
||||
private Long executeCount;
|
||||
|
||||
/**
|
||||
* 执行时间(毫秒)
|
||||
*/
|
||||
private Long executeTimeMillis;
|
||||
|
||||
/**
|
||||
* 最大执行时间(毫秒)
|
||||
*/
|
||||
private Long maxTimespan;
|
||||
|
||||
/**
|
||||
* 最小执行时间(毫秒)
|
||||
*/
|
||||
private Long minTimespan;
|
||||
|
||||
/**
|
||||
* 平均执行时间(毫秒)
|
||||
*/
|
||||
private Long avgTimespan;
|
||||
|
||||
/**
|
||||
* 最后执行时间
|
||||
*/
|
||||
private LocalDateTime lastExecuteTime;
|
||||
|
||||
/**
|
||||
* 出错次数
|
||||
*/
|
||||
private Long errorCount;
|
||||
|
||||
/**
|
||||
* 更新行数
|
||||
*/
|
||||
private Long updateCount;
|
||||
|
||||
/**
|
||||
* 读取行数
|
||||
*/
|
||||
private Long fetchRowCount;
|
||||
|
||||
/**
|
||||
* 运行中数量
|
||||
*/
|
||||
private Integer runningCount;
|
||||
|
||||
/**
|
||||
* 并发数量
|
||||
*/
|
||||
private Integer concurrentMax;
|
||||
|
||||
/**
|
||||
* 数据源名称
|
||||
*/
|
||||
private String dataSourceName;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
|
||||
package org.dromara.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* URI监控记录实体
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_uri_monitor")
|
||||
public class SysUriMonitor implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 请求URI
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 请求方法
|
||||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 请求次数
|
||||
*/
|
||||
private Long requestCount;
|
||||
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private Long successCount;
|
||||
|
||||
/**
|
||||
* 失败次数
|
||||
*/
|
||||
private Long failureCount;
|
||||
|
||||
/**
|
||||
* 总响应时间(毫秒)
|
||||
*/
|
||||
private Long totalResponseTime;
|
||||
|
||||
/**
|
||||
* 最大响应时间(毫秒)
|
||||
*/
|
||||
private Long maxResponseTime;
|
||||
|
||||
/**
|
||||
* 最小响应时间(毫秒)
|
||||
*/
|
||||
private Long minResponseTime;
|
||||
|
||||
/**
|
||||
* 平均响应时间(毫秒)
|
||||
*/
|
||||
private Long avgResponseTime;
|
||||
|
||||
/**
|
||||
* 最后访问时间
|
||||
*/
|
||||
private LocalDateTime lastAccessTime;
|
||||
|
||||
/**
|
||||
* 并发数
|
||||
*/
|
||||
private Integer concurrentCount;
|
||||
|
||||
/**
|
||||
* 最大并发数
|
||||
*/
|
||||
private Integer maxConcurrentCount;
|
||||
|
||||
/**
|
||||
* QPS(每秒查询率)
|
||||
*/
|
||||
private BigDecimal qps;
|
||||
|
||||
/**
|
||||
* 错误率
|
||||
*/
|
||||
private BigDecimal errorRate;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
|
||||
package org.dromara.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 数据源监控信息
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
public class DataSourceMonitorVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 数据源名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 数据库类型
|
||||
*/
|
||||
private String dbType;
|
||||
|
||||
/**
|
||||
* 连接URL
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 驱动类名
|
||||
*/
|
||||
private String driverClassName;
|
||||
|
||||
/**
|
||||
* 初始化连接数
|
||||
*/
|
||||
private int initialSize;
|
||||
|
||||
/**
|
||||
* 最小空闲连接数
|
||||
*/
|
||||
private int minIdle;
|
||||
|
||||
/**
|
||||
* 最大活跃连接数
|
||||
*/
|
||||
private int maxActive;
|
||||
|
||||
/**
|
||||
* 当前活跃连接数
|
||||
*/
|
||||
private int activeCount;
|
||||
|
||||
/**
|
||||
* 空闲连接数
|
||||
*/
|
||||
private int idleCount;
|
||||
|
||||
/**
|
||||
* 总连接数
|
||||
*/
|
||||
private int totalCount;
|
||||
|
||||
/**
|
||||
* 最大等待时间(毫秒)
|
||||
*/
|
||||
private long maxWait;
|
||||
|
||||
/**
|
||||
* 连接池使用率
|
||||
*/
|
||||
private double usageRate;
|
||||
|
||||
/**
|
||||
* 是否有效
|
||||
*/
|
||||
private boolean valid;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String createTime;
|
||||
|
||||
/**
|
||||
* 最后一次使用时间
|
||||
*/
|
||||
private String lastUseTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
package org.dromara.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* SQL防火墙监控信息
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
public class SqlFirewallVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* SQL语句
|
||||
*/
|
||||
private String sql;
|
||||
|
||||
/**
|
||||
* SQL类型(SELECT/INSERT/UPDATE/DELETE等)
|
||||
*/
|
||||
private String sqlType;
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 是否被拦截
|
||||
*/
|
||||
private boolean blocked;
|
||||
|
||||
/**
|
||||
* 拦截原因
|
||||
*/
|
||||
private String blockReason;
|
||||
|
||||
/**
|
||||
* 来源IP
|
||||
*/
|
||||
private String sourceIp;
|
||||
|
||||
/**
|
||||
* 执行用户
|
||||
*/
|
||||
private String executeUser;
|
||||
|
||||
/**
|
||||
* 执行时间
|
||||
*/
|
||||
private String executeTime;
|
||||
|
||||
/**
|
||||
* 风险等级(低/中/高)
|
||||
*/
|
||||
private String riskLevel;
|
||||
|
||||
/**
|
||||
* 是否包含敏感字段
|
||||
*/
|
||||
private boolean hasSensitiveFields;
|
||||
|
||||
/**
|
||||
* 影响行数
|
||||
*/
|
||||
private long affectRows;
|
||||
|
||||
/**
|
||||
* 数据源名称
|
||||
*/
|
||||
private String dataSourceName;
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
|
||||
package org.dromara.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* SQL监控信息
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
public class SqlMonitorVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* SQL ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* SQL语句
|
||||
*/
|
||||
private String sql;
|
||||
|
||||
/**
|
||||
* 执行次数
|
||||
*/
|
||||
private long executeCount;
|
||||
|
||||
/**
|
||||
* 执行时间(毫秒)
|
||||
*/
|
||||
private long executeTimeMillis;
|
||||
|
||||
/**
|
||||
* 最大执行时间(毫秒)
|
||||
*/
|
||||
private long maxTimespan;
|
||||
|
||||
/**
|
||||
* 最小执行时间(毫秒)
|
||||
*/
|
||||
private long minTimespan;
|
||||
|
||||
/**
|
||||
* 平均执行时间(毫秒)
|
||||
*/
|
||||
private long avgTimespan;
|
||||
|
||||
/**
|
||||
* 最后执行时间
|
||||
*/
|
||||
private String lastExecuteTime;
|
||||
|
||||
/**
|
||||
* 出错次数
|
||||
*/
|
||||
private long errorCount;
|
||||
|
||||
/**
|
||||
* 更新行数
|
||||
*/
|
||||
private long updateCount;
|
||||
|
||||
/**
|
||||
* 读取行数
|
||||
*/
|
||||
private long fetchRowCount;
|
||||
|
||||
/**
|
||||
* 运行中数量
|
||||
*/
|
||||
private Long runningCount;
|
||||
|
||||
/**
|
||||
* 并发数量
|
||||
*/
|
||||
private Long concurrentMax;
|
||||
|
||||
/**
|
||||
* 数据源名称
|
||||
*/
|
||||
private String dataSourceName;
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
|
||||
package org.dromara.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 系统监控信息
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
public class SystemMonitorVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* CPU信息
|
||||
*/
|
||||
private CpuInfo cpu;
|
||||
|
||||
/**
|
||||
* 内存信息
|
||||
*/
|
||||
private MemoryInfo memory;
|
||||
|
||||
/**
|
||||
* JVM信息
|
||||
*/
|
||||
private JvmInfo jvm;
|
||||
|
||||
/**
|
||||
* 系统信息
|
||||
*/
|
||||
private SystemInfo sys;
|
||||
|
||||
/**
|
||||
* 磁盘信息
|
||||
*/
|
||||
private DiskInfo disk;
|
||||
|
||||
/**
|
||||
* CPU信息
|
||||
*/
|
||||
@Data
|
||||
public static class CpuInfo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 核心数
|
||||
*/
|
||||
private int cpuNum;
|
||||
|
||||
/**
|
||||
* CPU总的使用率
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* CPU系统使用率
|
||||
*/
|
||||
private double sys;
|
||||
|
||||
/**
|
||||
* CPU用户使用率
|
||||
*/
|
||||
private double used;
|
||||
|
||||
/**
|
||||
* CPU当前等待率
|
||||
*/
|
||||
private double wait;
|
||||
|
||||
/**
|
||||
* CPU当前空闲率
|
||||
*/
|
||||
private double free;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内存信息
|
||||
*/
|
||||
@Data
|
||||
public static class MemoryInfo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 总内存
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* 已用内存
|
||||
*/
|
||||
private double used;
|
||||
|
||||
/**
|
||||
* 剩余内存
|
||||
*/
|
||||
private double free;
|
||||
|
||||
/**
|
||||
* 使用率
|
||||
*/
|
||||
private double usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* JVM信息
|
||||
*/
|
||||
@Data
|
||||
public static class JvmInfo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 当前JVM占用的内存总数(M)
|
||||
*/
|
||||
private double total;
|
||||
|
||||
/**
|
||||
* JVM最大可用内存总数(M)
|
||||
*/
|
||||
private double max;
|
||||
|
||||
/**
|
||||
* JVM空闲内存(M)
|
||||
*/
|
||||
private double free;
|
||||
|
||||
/**
|
||||
* JDK版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* JDK路径
|
||||
*/
|
||||
private String home;
|
||||
|
||||
/**
|
||||
* 启动时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 运行时间
|
||||
*/
|
||||
private String runTime;
|
||||
|
||||
/**
|
||||
* 运行时间(秒)
|
||||
*/
|
||||
private long runTimeSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统信息
|
||||
*/
|
||||
@Data
|
||||
public static class SystemInfo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 服务器名称
|
||||
*/
|
||||
private String computerName;
|
||||
|
||||
/**
|
||||
* 服务器IP
|
||||
*/
|
||||
private String computerIp;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* 系统架构
|
||||
*/
|
||||
private String osArch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 磁盘信息
|
||||
*/
|
||||
@Data
|
||||
public static class DiskInfo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 盘符路径
|
||||
*/
|
||||
private String dirName;
|
||||
|
||||
/**
|
||||
* 盘符类型
|
||||
*/
|
||||
private String sysTypeName;
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
private String typeName;
|
||||
|
||||
/**
|
||||
* 总大小
|
||||
*/
|
||||
private String total;
|
||||
|
||||
/**
|
||||
* 剩余大小
|
||||
*/
|
||||
private String free;
|
||||
|
||||
/**
|
||||
* 已经使用量
|
||||
*/
|
||||
private String used;
|
||||
|
||||
/**
|
||||
* 资源的使用率
|
||||
*/
|
||||
private double usage;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
|
||||
package org.dromara.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* URI监控信息
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Data
|
||||
public class UriMonitorVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* URI ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 请求URI
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 请求方法(GET/POST/PUT/DELETE等)
|
||||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 请求次数
|
||||
*/
|
||||
private long requestCount;
|
||||
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private long successCount;
|
||||
|
||||
/**
|
||||
* 失败次数
|
||||
*/
|
||||
private long failureCount;
|
||||
|
||||
/**
|
||||
* 总响应时间(毫秒)
|
||||
*/
|
||||
private long totalResponseTime;
|
||||
|
||||
/**
|
||||
* 最大响应时间(毫秒)
|
||||
*/
|
||||
private long maxResponseTime;
|
||||
|
||||
/**
|
||||
* 最小响应时间(毫秒)
|
||||
*/
|
||||
private long minResponseTime;
|
||||
|
||||
/**
|
||||
* 平均响应时间(毫秒)
|
||||
*/
|
||||
private long avgResponseTime;
|
||||
|
||||
/**
|
||||
* 最后访问时间
|
||||
*/
|
||||
private String lastAccessTime;
|
||||
|
||||
/**
|
||||
* 并发数
|
||||
*/
|
||||
private int concurrentCount;
|
||||
|
||||
/**
|
||||
* 最大并发数
|
||||
*/
|
||||
private int maxConcurrentCount;
|
||||
|
||||
/**
|
||||
* QPS(每秒查询率)
|
||||
*/
|
||||
private double qps;
|
||||
|
||||
/**
|
||||
* 错误率
|
||||
*/
|
||||
private double errorRate;
|
||||
|
||||
/**
|
||||
* 来源IP
|
||||
*/
|
||||
private String sourceIp;
|
||||
|
||||
/**
|
||||
* 用户代理
|
||||
*/
|
||||
private String userAgent;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
package org.dromara.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.dromara.system.domain.SysSqlFirewall;
|
||||
|
||||
/**
|
||||
* SQL防火墙记录Mapper接口
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
public interface SysSqlFirewallMapper extends BaseMapper<SysSqlFirewall> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
package org.dromara.system.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Druid监控服务接口
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
public interface IDruidMonitorService {
|
||||
|
||||
/**
|
||||
* 获取数据源监控信息
|
||||
*
|
||||
* @return 数据源监控信息列表
|
||||
*/
|
||||
List<Object> getDataSourceInfo();
|
||||
|
||||
/**
|
||||
* 获取SQL监控信息
|
||||
*
|
||||
* @return SQL监控信息列表
|
||||
*/
|
||||
List<Object> getSqlInfo();
|
||||
|
||||
/**
|
||||
* 获取SQL防火墙信息
|
||||
*
|
||||
* @return SQL防火墙信息列表
|
||||
*/
|
||||
List<Object> getSqlFirewallInfo();
|
||||
|
||||
/**
|
||||
* 获取URI监控信息
|
||||
*
|
||||
* @return URI监控信息列表
|
||||
*/
|
||||
List<Object> getUriInfo();
|
||||
|
||||
/**
|
||||
* 重置SQL监控统计信息
|
||||
*/
|
||||
void resetSqlStat();
|
||||
|
||||
/**
|
||||
* 获取活跃连接信息
|
||||
*
|
||||
* @return 活跃连接信息列表
|
||||
*/
|
||||
List<Object> getActiveConnections();
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.dromara.system.service;
|
||||
|
||||
import org.dromara.system.domain.vo.SystemMonitorVo;
|
||||
|
||||
/**
|
||||
* 系统监控服务
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
public interface ISystemMonitorService {
|
||||
|
||||
/**
|
||||
* 获取系统监控信息
|
||||
*
|
||||
* @return 系统监控信息
|
||||
*/
|
||||
SystemMonitorVo getAllSystemInfo();
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
package org.dromara.system.service.impl;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.system.domain.vo.SqlMonitorVo;
|
||||
import org.dromara.system.service.IDruidMonitorService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Druid监控服务实现
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class DruidMonitorServiceImpl implements IDruidMonitorService {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
/**
|
||||
* 获取所有Druid数据源
|
||||
*/
|
||||
private Map<String, DruidDataSource> getDruidDataSources() {
|
||||
Map<String, DruidDataSource> druidDataSources = new HashMap<>();
|
||||
|
||||
if (dataSource instanceof DynamicRoutingDataSource) {
|
||||
DynamicRoutingDataSource dynamicDataSource = (DynamicRoutingDataSource) dataSource;
|
||||
Map<String, DataSource> dataSources = dynamicDataSource.getDataSources();
|
||||
|
||||
for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
|
||||
DataSource ds = entry.getValue();
|
||||
// 尝试获取底层的Druid数据源
|
||||
if (ds instanceof DruidDataSource) {
|
||||
druidDataSources.put(entry.getKey(), (DruidDataSource) ds);
|
||||
}
|
||||
// 如果数据源被代理,尝试解包
|
||||
else {
|
||||
try {
|
||||
// 对于p6spy等代理,可能需要解包
|
||||
if (ds instanceof com.p6spy.engine.spy.P6DataSource) {
|
||||
java.lang.reflect.Field realDataSourceField = ds.getClass().getDeclaredField("realDataSource");
|
||||
realDataSourceField.setAccessible(true);
|
||||
Object realDs = realDataSourceField.get(ds);
|
||||
if (realDs instanceof DruidDataSource) {
|
||||
druidDataSources.put(entry.getKey(), (DruidDataSource) realDs);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("无法获取数据源 {} 的底层Druid数据源: {}", entry.getKey(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (dataSource instanceof DruidDataSource) {
|
||||
// 如果是直接使用Druid数据源
|
||||
druidDataSources.put("default", (DruidDataSource) dataSource);
|
||||
}
|
||||
|
||||
return druidDataSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据源监控信息
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getDataSourceInfo() {
|
||||
List<Object> dataSourceList = new ArrayList<>();
|
||||
Map<String, DruidDataSource> druidDataSources = getDruidDataSources();
|
||||
|
||||
for (Map.Entry<String, DruidDataSource> entry : druidDataSources.entrySet()) {
|
||||
DruidDataSource druidDataSource = entry.getValue();
|
||||
Map<String, Object> dsInfo = new LinkedHashMap<>();
|
||||
dsInfo.put("name", entry.getKey() + "(" + druidDataSource.getName() + ")");
|
||||
dsInfo.put("url", druidDataSource.getUrl());
|
||||
dsInfo.put("username", druidDataSource.getUsername());
|
||||
dsInfo.put("dbType", druidDataSource.getDbType());
|
||||
dsInfo.put("driverClassName", druidDataSource.getDriverClassName());
|
||||
dsInfo.put("initialSize", druidDataSource.getInitialSize());
|
||||
dsInfo.put("minIdle", druidDataSource.getMinIdle());
|
||||
dsInfo.put("maxActive", druidDataSource.getMaxActive());
|
||||
dsInfo.put("activeCount", druidDataSource.getActiveCount());
|
||||
dsInfo.put("poolingCount", druidDataSource.getPoolingCount());
|
||||
dsInfo.put("maxWait", druidDataSource.getMaxWait());
|
||||
dsInfo.put("validationQuery", druidDataSource.getValidationQuery());
|
||||
dsInfo.put("testOnBorrow", druidDataSource.isTestOnBorrow());
|
||||
dsInfo.put("testOnReturn", druidDataSource.isTestOnReturn());
|
||||
dsInfo.put("testWhileIdle", druidDataSource.isTestWhileIdle());
|
||||
|
||||
// 计算连接池使用率
|
||||
int maxActive = druidDataSource.getMaxActive();
|
||||
int activeCount = druidDataSource.getActiveCount();
|
||||
double usageRate = maxActive > 0 ? (activeCount * 100.0 / maxActive) : 0;
|
||||
dsInfo.put("usageRate", String.format("%.2f", usageRate) + "%");
|
||||
|
||||
dsInfo.put("createTime", formatDate(druidDataSource.getCreatedTime().getTime()));
|
||||
dsInfo.put("lastUseTime", formatDate(System.currentTimeMillis()));
|
||||
|
||||
dataSourceList.add(dsInfo);
|
||||
}
|
||||
|
||||
if (dataSourceList.isEmpty()) {
|
||||
log.warn("未找到Druid数据源,当前数据源类型: {}", dataSource.getClass().getName());
|
||||
}
|
||||
|
||||
return dataSourceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SQL监控信息
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getSqlInfo() {
|
||||
List<Object> sqlList = new ArrayList<>();
|
||||
Map<String, DruidDataSource> druidDataSources = getDruidDataSources();
|
||||
|
||||
for (Map.Entry<String, DruidDataSource> entry : druidDataSources.entrySet()) {
|
||||
DruidDataSource druidDataSource = entry.getValue();
|
||||
Map<String, com.alibaba.druid.stat.JdbcSqlStat> sqlStatMap = druidDataSource.getSqlStatMap();
|
||||
|
||||
if (sqlStatMap != null && !sqlStatMap.isEmpty()) {
|
||||
for (Map.Entry<String, com.alibaba.druid.stat.JdbcSqlStat> statEntry : sqlStatMap.entrySet()) {
|
||||
SqlMonitorVo sqlMonitor = new SqlMonitorVo();
|
||||
com.alibaba.druid.stat.JdbcSqlStat sqlStat = statEntry.getValue();
|
||||
sqlMonitor.setId(sqlStat.getId());
|
||||
sqlMonitor.setSql(sqlStat.getSql());
|
||||
sqlMonitor.setExecuteCount(sqlStat.getExecuteCount());
|
||||
sqlMonitor.setExecuteTimeMillis(sqlStat.getExecuteMillisTotal());
|
||||
sqlMonitor.setMaxTimespan(sqlStat.getExecuteMillisMax());
|
||||
sqlMonitor.setMinTimespan(0L);
|
||||
|
||||
long executeCount = sqlMonitor.getExecuteCount();
|
||||
long executeTimeMillis = sqlMonitor.getExecuteTimeMillis();
|
||||
sqlMonitor.setAvgTimespan(executeCount > 0 ? executeTimeMillis / executeCount : 0);
|
||||
|
||||
sqlMonitor.setLastExecuteTime(formatDate(System.currentTimeMillis()));
|
||||
sqlMonitor.setErrorCount(sqlStat.getErrorCount());
|
||||
sqlMonitor.setUpdateCount(sqlStat.getUpdateCount());
|
||||
sqlMonitor.setFetchRowCount(sqlStat.getFetchRowCount());
|
||||
sqlMonitor.setRunningCount(sqlStat.getRunningCount());
|
||||
sqlMonitor.setConcurrentMax(sqlStat.getConcurrentMax());
|
||||
sqlMonitor.setDataSourceName(entry.getKey());
|
||||
|
||||
sqlList.add(sqlMonitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqlList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SQL防火墙信息
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getSqlFirewallInfo() {
|
||||
List<Object> firewallList = new ArrayList<>();
|
||||
Map<String, DruidDataSource> druidDataSources = getDruidDataSources();
|
||||
|
||||
for (Map.Entry<String, DruidDataSource> entry : druidDataSources.entrySet()) {
|
||||
DruidDataSource druidDataSource = entry.getValue();
|
||||
Map<String, Object> wallStatMap = druidDataSource.getWallStatMap();
|
||||
if (wallStatMap != null && !wallStatMap.isEmpty()) {
|
||||
Map<String, Object> firewallInfo = new LinkedHashMap<>();
|
||||
firewallInfo.put("dataSource", entry.getKey());
|
||||
firewallInfo.put("checkCount", wallStatMap.getOrDefault("checkCount", 0));
|
||||
firewallInfo.put("violationCount", wallStatMap.getOrDefault("violationCount", 0));
|
||||
firewallInfo.put("blackListHitCount", wallStatMap.getOrDefault("blackListHitCount", 0));
|
||||
firewallInfo.put("whiteListHitCount", wallStatMap.getOrDefault("whiteListHitCount", 0));
|
||||
firewallInfo.put("syntaxErrorCount", wallStatMap.getOrDefault("syntaxErrorCount", 0));
|
||||
firewallInfo.put("tableCount", wallStatMap.getOrDefault("tableCount", 0));
|
||||
firewallInfo.put("selectCount", wallStatMap.getOrDefault("selectCount", 0));
|
||||
firewallInfo.put("updateCount", wallStatMap.getOrDefault("updateCount", 0));
|
||||
firewallInfo.put("deleteCount", wallStatMap.getOrDefault("deleteCount", 0));
|
||||
firewallInfo.put("insertCount", wallStatMap.getOrDefault("insertCount", 0));
|
||||
|
||||
firewallList.add(firewallInfo);
|
||||
}
|
||||
}
|
||||
return firewallList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取URI监控信息
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getUriInfo() {
|
||||
// URI监控功能在新版Druid中已移除
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置SQL监控统计信息
|
||||
*/
|
||||
@Override
|
||||
public void resetSqlStat() {
|
||||
Map<String, DruidDataSource> druidDataSources = getDruidDataSources();
|
||||
for (Map.Entry<String, DruidDataSource> entry : druidDataSources.entrySet()) {
|
||||
entry.getValue().resetStat();
|
||||
log.info("数据源 {} 的SQL监控统计信息已重置", entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃连接信息
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getActiveConnections() {
|
||||
List<Object> activeConnections = new ArrayList<>();
|
||||
Map<String, DruidDataSource> druidDataSources = getDruidDataSources();
|
||||
|
||||
for (Map.Entry<String, DruidDataSource> entry : druidDataSources.entrySet()) {
|
||||
DruidDataSource druidDataSource = entry.getValue();
|
||||
Set<com.alibaba.druid.pool.DruidPooledConnection> activeSet = druidDataSource.getActiveConnections();
|
||||
|
||||
for (com.alibaba.druid.pool.DruidPooledConnection activeConn : activeSet) {
|
||||
Map<String, Object> connInfo = new LinkedHashMap<>();
|
||||
connInfo.put("dataSource", entry.getKey());
|
||||
connInfo.put("connectTime", formatDate(System.currentTimeMillis()));
|
||||
connInfo.put("connectHoldTime", 0L);
|
||||
connInfo.put("lastSql", "-");
|
||||
connInfo.put("lastExecuteTime", "-");
|
||||
|
||||
activeConnections.add(connInfo);
|
||||
}
|
||||
}
|
||||
return activeConnections;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
*/
|
||||
private String formatDate(long timestamp) {
|
||||
if (timestamp <= 0) {
|
||||
return "-";
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.format(new Date(timestamp));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,436 @@
|
|||
|
||||
package org.dromara.system.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.system.utils.IPUtils;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.software.os.FileSystem;
|
||||
import oshi.software.os.OSFileStore;
|
||||
import oshi.util.Util;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.system.domain.vo.SystemMonitorVo;
|
||||
import org.dromara.system.service.ISystemMonitorService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统监控服务实现
|
||||
*
|
||||
* @author CodeGeeX
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SystemMonitorServiceImpl implements ISystemMonitorService {
|
||||
|
||||
private static final int OSHI_WAIT_SECONDS = 1000;
|
||||
|
||||
/**
|
||||
* 获取系统监控信息
|
||||
*
|
||||
* @return 系统监控信息
|
||||
*/
|
||||
@Override
|
||||
public SystemMonitorVo getAllSystemInfo() {
|
||||
SystemMonitorVo monitorVo = new SystemMonitorVo();
|
||||
|
||||
// 获取系统信息
|
||||
monitorVo.setSys(getSystemInfo());
|
||||
|
||||
// 获取CPU信息
|
||||
monitorVo.setCpu(getCpuInfo());
|
||||
|
||||
// 获取内存信息
|
||||
monitorVo.setMemory(getMemoryInfo());
|
||||
|
||||
// 获取JVM信息
|
||||
monitorVo.setJvm(getJvmInfo());
|
||||
|
||||
// 获取磁盘信息
|
||||
monitorVo.setDisk(getDiskInfo());
|
||||
|
||||
return monitorVo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统信息
|
||||
*/
|
||||
private SystemMonitorVo.SystemInfo getSystemInfo() {
|
||||
SystemMonitorVo.SystemInfo sysInfo = new SystemMonitorVo.SystemInfo();
|
||||
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getLocalHost();
|
||||
sysInfo.setComputerName(inetAddress.getHostName());
|
||||
sysInfo.setComputerIp(inetAddress.getHostAddress());
|
||||
} catch (UnknownHostException e) {
|
||||
sysInfo.setComputerName("未知");
|
||||
sysInfo.setComputerIp("未知");
|
||||
}
|
||||
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
sysInfo.setOsName(systemInfo.getOperatingSystem().getFamily() + " " + systemInfo.getOperatingSystem().getVersionInfo());
|
||||
sysInfo.setOsArch(systemInfo.getHardware().getProcessor().getPhysicalProcessorCount() + "核 " + systemInfo.getHardware().getProcessor().getLogicalProcessorCount() + "线程");
|
||||
|
||||
return sysInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取CPU信息
|
||||
*/
|
||||
private SystemMonitorVo.CpuInfo getCpuInfo() {
|
||||
SystemMonitorVo.CpuInfo cpuInfo = new SystemMonitorVo.CpuInfo();
|
||||
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
CentralProcessor processor = systemInfo.getHardware().getProcessor();
|
||||
|
||||
// CPU核心数
|
||||
cpuInfo.setCpuNum(processor.getLogicalProcessorCount());
|
||||
|
||||
// CPU信息
|
||||
long[] prevTicks = processor.getSystemCpuLoadTicks();
|
||||
// 等待1秒
|
||||
Util.sleep(OSHI_WAIT_SECONDS);
|
||||
long[] ticks = processor.getSystemCpuLoadTicks();
|
||||
|
||||
long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
|
||||
long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
|
||||
long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
|
||||
long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
|
||||
long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
|
||||
long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
|
||||
long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
|
||||
long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
|
||||
long total = user + nice + cSys + idle + iowait + irq + softirq + steal;
|
||||
|
||||
cpuInfo.setTotal(Arith.div(total - idle, total, 4) * 100);
|
||||
cpuInfo.setSys(Arith.div(cSys + irq + softirq, total, 4) * 100);
|
||||
cpuInfo.setUsed(Arith.div(user + nice, total, 4) * 100);
|
||||
cpuInfo.setWait(Arith.div(iowait, total, 4) * 100);
|
||||
cpuInfo.setFree(Arith.div(idle, total, 4) * 100);
|
||||
|
||||
return cpuInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内存信息
|
||||
*/
|
||||
private SystemMonitorVo.MemoryInfo getMemoryInfo() {
|
||||
SystemMonitorVo.MemoryInfo memoryInfo = new SystemMonitorVo.MemoryInfo();
|
||||
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
GlobalMemory memory = systemInfo.getHardware().getMemory();
|
||||
|
||||
// 总内存
|
||||
long total = memory.getTotal();
|
||||
// 已用内存
|
||||
long used = total - memory.getAvailable();
|
||||
// 剩余内存
|
||||
long free = memory.getAvailable();
|
||||
|
||||
memoryInfo.setTotal(Arith.div(total, (1024 * 1024 * 1024), 2));
|
||||
memoryInfo.setUsed(Arith.div(used, (1024 * 1024 * 1024), 2));
|
||||
memoryInfo.setFree(Arith.div(free, (1024 * 1024 * 1024), 2));
|
||||
memoryInfo.setUsage(Arith.mul(Arith.div(used, total, 4), 100));
|
||||
|
||||
return memoryInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JVM信息
|
||||
*/
|
||||
private SystemMonitorVo.JvmInfo getJvmInfo() {
|
||||
SystemMonitorVo.JvmInfo jvmInfo = new SystemMonitorVo.JvmInfo();
|
||||
|
||||
// JVM运行时间
|
||||
long startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||
Date date = new Date(startTime);
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
jvmInfo.setStartTime(sdf.format(date));
|
||||
|
||||
// 运行时间
|
||||
long runTime = System.currentTimeMillis() - startTime;
|
||||
jvmInfo.setRunTimeSeconds(runTime / 1000);
|
||||
jvmInfo.setRunTime(formatTime(runTime));
|
||||
|
||||
// JVM内存
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
// JVM总内存
|
||||
jvmInfo.setTotal(Arith.div(runtime.totalMemory(), (1024 * 1024), 2));
|
||||
// JVM最大可用内存
|
||||
jvmInfo.setMax(Arith.div(runtime.maxMemory(), (1024 * 1024), 2));
|
||||
// JVM空闲内存
|
||||
jvmInfo.setFree(Arith.div(runtime.freeMemory(), (1024 * 1024), 2));
|
||||
|
||||
// JDK版本
|
||||
jvmInfo.setVersion(System.getProperty("java.version"));
|
||||
|
||||
// JDK路径
|
||||
jvmInfo.setHome(System.getProperty("java.home"));
|
||||
|
||||
return jvmInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取磁盘信息
|
||||
*/
|
||||
private SystemMonitorVo.DiskInfo getDiskInfo() {
|
||||
SystemMonitorVo.DiskInfo diskInfo = new SystemMonitorVo.DiskInfo();
|
||||
|
||||
try {
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
FileSystem fileSystem = systemInfo.getOperatingSystem().getFileSystem();
|
||||
List<OSFileStore> fileStores = fileSystem.getFileStores();
|
||||
|
||||
// 判断操作系统类型
|
||||
boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows");
|
||||
|
||||
// 用于Linux系统的后备选项
|
||||
OSFileStore fallbackFileStore = null;
|
||||
|
||||
for (OSFileStore fileStore : fileStores) {
|
||||
String mount = fileStore.getMount();
|
||||
String name = fileStore.getName();
|
||||
|
||||
if (StringUtils.isEmpty(mount)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Windows系统处理
|
||||
if (isWindows) {
|
||||
// 修正:正确的Windows盘符匹配模式
|
||||
if (mount.matches("^[A-Za-z]:\\\\$") || mount.matches("^[A-Za-z]:/$") || mount.equals("C:\\") || mount.equals("C:/")) {
|
||||
setDiskInfo(diskInfo, fileStore, mount);
|
||||
|
||||
// 添加日志便于调试
|
||||
System.out.println("Found Windows disk: " + mount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Linux/Unix系统处理
|
||||
else {
|
||||
// 跳过系统特殊目录
|
||||
if (mount.startsWith("/dev") || mount.startsWith("/sys") ||
|
||||
mount.startsWith("/proc") || mount.startsWith("/run") ||
|
||||
mount.startsWith("/boot") || mount.equals("/tmp")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 优先获取根目录(/)
|
||||
if (mount.equals("/")) {
|
||||
setDiskInfo(diskInfo, fileStore, mount);
|
||||
System.out.println("Found root directory: " + mount);
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果还没找到根目录,保存第一个有效的挂载点作为后备
|
||||
if (fallbackFileStore == null && isValidMountPoint(mount)) {
|
||||
fallbackFileStore = fileStore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linux系统:如果没找到根目录但找到了其他有效挂载点
|
||||
if (!isWindows && StringUtils.isEmpty(diskInfo.getDirName()) && fallbackFileStore != null) {
|
||||
setDiskInfo(diskInfo, fallbackFileStore, fallbackFileStore.getMount());
|
||||
System.out.println("Using fallback mount point: " + fallbackFileStore.getMount());
|
||||
}
|
||||
|
||||
// 如果仍然没有获取到磁盘信息,尝试获取第一个有效磁盘
|
||||
if (StringUtils.isEmpty(diskInfo.getDirName()) && !fileStores.isEmpty()) {
|
||||
for (OSFileStore fileStore : fileStores) {
|
||||
String mount = fileStore.getMount();
|
||||
if (!StringUtils.isEmpty(mount)) {
|
||||
setDiskInfo(diskInfo, fileStore, mount);
|
||||
System.out.println("Using first available disk: " + mount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 处理异常情况,设置默认值
|
||||
diskInfo.setDirName("Unknown");
|
||||
diskInfo.setTotal("0G");
|
||||
diskInfo.setFree("0G");
|
||||
diskInfo.setUsed("0G");
|
||||
diskInfo.setUsage(0);
|
||||
}
|
||||
|
||||
return diskInfo;
|
||||
}
|
||||
|
||||
// 提取公共方法设置磁盘信息
|
||||
private void setDiskInfo(SystemMonitorVo.DiskInfo diskInfo, OSFileStore fileStore, String mount) {
|
||||
try {
|
||||
diskInfo.setDirName(mount);
|
||||
diskInfo.setSysTypeName(fileStore.getType());
|
||||
diskInfo.setTypeName(fileStore.getVolume());
|
||||
|
||||
long total = fileStore.getTotalSpace();
|
||||
long free = fileStore.getUsableSpace();
|
||||
long used = total - free;
|
||||
|
||||
// 防止除零错误
|
||||
if (total > 0) {
|
||||
diskInfo.setTotal(Arith.div(total, (1024 * 1024 * 1024), 2) + "G");
|
||||
diskInfo.setFree(Arith.div(free, (1024 * 1024 * 1024), 2) + "G");
|
||||
diskInfo.setUsed(Arith.div(used, (1024 * 1024 * 1024), 2) + "G");
|
||||
diskInfo.setUsage(Arith.mul(Arith.div(used, total, 4), 100));
|
||||
} else {
|
||||
diskInfo.setTotal("0G");
|
||||
diskInfo.setFree("0G");
|
||||
diskInfo.setUsed("0G");
|
||||
diskInfo.setUsage(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否是有效的挂载点
|
||||
private boolean isValidMountPoint(String mount) {
|
||||
return !StringUtils.isEmpty(mount) &&
|
||||
!mount.startsWith("/dev") &&
|
||||
!mount.startsWith("/sys") &&
|
||||
!mount.startsWith("/proc");
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
*
|
||||
* @param time 毫秒
|
||||
* @return 格式化后的时间字符串
|
||||
*/
|
||||
private String formatTime(long time) {
|
||||
long days = time / (1000 * 60 * 60 * 24);
|
||||
long hours = (time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60);
|
||||
long minutes = (time % (1000 * 60 * 60)) / (1000 * 60);
|
||||
long seconds = (time % (1000 * 60)) / 1000;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (days > 0) {
|
||||
sb.append(days).append("天");
|
||||
}
|
||||
if (hours > 0) {
|
||||
sb.append(hours).append("小时");
|
||||
}
|
||||
if (minutes > 0) {
|
||||
sb.append(minutes).append("分钟");
|
||||
}
|
||||
if (seconds > 0) {
|
||||
sb.append(seconds).append("秒");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 精确计算
|
||||
*/
|
||||
private static class Arith {
|
||||
/**
|
||||
* 默认除法运算精度
|
||||
*/
|
||||
private static final int DEF_DIV_SCALE = 10;
|
||||
|
||||
private Arith() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算
|
||||
*
|
||||
* @param v1 被加数
|
||||
* @param v2 加数
|
||||
* @return 两个参数的和
|
||||
*/
|
||||
public static double add(double v1, double v2) {
|
||||
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
|
||||
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
|
||||
return b1.add(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算
|
||||
*
|
||||
* @param v1 被减数
|
||||
* @param v2 减数
|
||||
* @return 两个参数的差
|
||||
*/
|
||||
public static double sub(double v1, double v2) {
|
||||
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
|
||||
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
|
||||
return b1.subtract(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算
|
||||
*
|
||||
* @param v1 被乘数
|
||||
* @param v2 乘数
|
||||
* @return 两个参数的积
|
||||
*/
|
||||
public static double mul(double v1, double v2) {
|
||||
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
|
||||
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
|
||||
return b1.multiply(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
|
||||
* 小数点以后10位,以后的数字四舍五入
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2) {
|
||||
return div(v1, v2, DEF_DIV_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
|
||||
* 定精度,以后的数字四舍五入
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @param scale 表示表示需要精确到小数点以后几位。
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
|
||||
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
|
||||
return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的小数位四舍五入处理
|
||||
*
|
||||
* @param v 需要四舍五入的数字
|
||||
* @param scale 小数点后保留几位
|
||||
* @return 四舍五入后的结果
|
||||
*/
|
||||
public static double round(double v, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
|
||||
java.math.BigDecimal one = new java.math.BigDecimal("1");
|
||||
return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue