融合通信

ds-hefei
luyya 2026-03-16 15:42:46 +08:00
parent 43652655de
commit 5ab0418957
16 changed files with 1867 additions and 0 deletions

View File

@ -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;
}
/**
* WebFilter
*/
@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");
}
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -0,0 +1,14 @@
package org.dromara.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.dromara.system.domain.SysSqlFirewall;
/**
* SQLMapper
*
* @author CodeGeeX
*/
public interface SysSqlFirewallMapper extends BaseMapper<SysSqlFirewall> {
}

View File

@ -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();
}

View File

@ -0,0 +1,18 @@
package org.dromara.system.service;
import org.dromara.system.domain.vo.SystemMonitorVo;
/**
*
*
* @author CodeGeeX
*/
public interface ISystemMonitorService {
/**
*
*
* @return
*/
SystemMonitorVo getAllSystemInfo();
}

View File

@ -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));
}
}

View File

@ -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();
}
}
}