融合通信
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