1、新增功能探针联动处置、心跳在线检测

2、syslog-consumer模块拆分 syslog-consumer-rule模块实现日志数据消费、解析、泛化入库。
This commit is contained in:
2026-05-28 14:30:06 +08:00
parent 19c563b3f3
commit a360895292
1479 changed files with 116572 additions and 4549 deletions
@@ -0,0 +1,116 @@
# ============================================
# Syslog Consumer 部署配置
# ============================================
# 使用方法: docker compose -f docker-compose-consumer.yaml up -d
# ============================================
services:
# ============================================
# Syslog Consumer - 数据消费服务 (平台端)
# ============================================
syslog-consumer:
build:
context: ./syslog-consumer
dockerfile: Dockerfile
image: syslog-consumer:1.2.0
container_name: syslog-consumer
restart: unless-stopped
environment:
# 环境配置
- SPRING_PROFILES_ACTIVE=dev
- TZ=Asia/Shanghai
# 数据库配置
- spring.datasource.url=jdbc:postgresql://117.72.68.72:54329/ecosys
- spring.datasource.username=postgres
- spring.datasource.password=TnLanWaidYSwTSG5
- spring.datasource.driver-class-name=org.postgresql.Driver
# HikariCP 连接池配置
- spring.datasource.hikari.maximum-pool-size=50
- spring.datasource.hikari.minimum-idle=5
- spring.datasource.hikari.connection-timeout=30000
- spring.datasource.hikari.idle-timeout=600000
- spring.datasource.hikari.max-lifetime=900000
- spring.datasource.hikari.pool-name=HikariPool-SyslogConsumer
- spring.datasource.hikari.auto-commit=false
- spring.datasource.hikari.schema=public
# Redis配置
- spring.redis.host=192.168.222.131
- spring.redis.port=6379
- spring.redis.password=
- spring.redis.database=0
- spring.redis.timeout=2000
- spring.cache.redis.time-to-live=600000
# Kafka配置
- spring.kafka.consumer.bootstrap-servers=192.168.222.130:9092
- spring.kafka.consumer.group-id=test-group-app
- spring.kafka.consumer.auto-offset-reset=latest
- spring.kafka.consumer.enable-auto-commit=false
- spring.kafka.consumer.topic=test-topic
- spring.kafka.consumer.max-poll-records=1000
- spring.kafka.listener.ack-mode=manual
- spring.kafka.listener.concurrency=2
- spring.kafka.listener.type=batch
# InfluxDB配置
- influxdb.url=http://192.168.222.131:8086
- influxdb.token=3Tvu-IZWtaY03UDkbUDlufD0kxn85keo9LhYQcv2Cxk0LJmXqqHkNVrO664DbaJAYwoGI7UIg904KqZC7Q_ZFA==
- influxdb.org=yelang
- influxdb.bucket=yelangbucket
- influxdb.batch.size=1000
- influxdb.flush.interval=1000
- influxdb.connection.timeout=30s
- influxdb.connection.read-timeout=30s
- influxdb.connection.write-timeout=60s
# Elasticsearch配置
- spring.elasticsearch.uris=http://192.168.1.174:9200
- spring.elasticsearch.username=CONTAINER_NAME
- spring.elasticsearch.password=t2NZCiajmdazxBrF
- spring.elasticsearch.connection-timeout=10s
- spring.elasticsearch.socket-timeout=30s
# API配置
- interlocking.api-key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
- interlocking.api.base-url=http://192.168.222.131:8089/xdrservice/interlocking
# 探针心跳配置
- probe.heartbeat.enabled=true
- probe.heartbeat.offline-threshold-minutes=10
- probe.status.check.cron=0 */10 * * * ?
- probe.heartbeat.tenant-id=000000
- probe.heartbeat.history.keep-days=10
- probe.heartbeat.history.cleanup-enabled=true
- probe.history.cleanup.cron=0 0 1 * * ?
# 告警健康检查配置
- alarm.health-check.alarm-hours=4
- alarm.health-check.alarm-visit-hours=2
- alarm.health-check.enabled=true
# 关联分析规则配置
- analysis.realtime.enabled=true
- analysis.realtime.check-interval-seconds=10
# 分区表检查配置
- partition.check.tomorrow.enabled=true
- partition.check.future.days=7
- partition.auto.create=true
# 定时任务配置
- spring.task.scheduling.pool.size=10
# 日志配置
- logging.level.com.common.schedule=INFO
- logging.level.com.common.service=INFO
# ETL配置
- etl.batch.page-size=1000
- etl.batch.insert-batch-size=500
- etl.schedule.cron=0 0 2 * * ?
# JVM配置
- JAVA_OPTS=-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
ports:
- "8089:8089"
volumes:
- /home/syslog/logs:/app/logs
networks:
- xdr-network
# ============================================
# 网络配置
# ============================================
networks:
xdr-network:
driver: bridge
@@ -0,0 +1,143 @@
# ============================================
# Syslog Consumer 部署配置
# ============================================
# 使用方法: docker-compose -f docker-compose-consumer.yaml up -d
# ============================================
version: '3.8'
services:
# ============================================
# Syslog Consumer - 数据消费服务 (平台端)
# ============================================
syslog-consumer:
build:
context: ./syslog-consumer
dockerfile: Dockerfile
container_name: xdr-syslog-consumer
restart: unless-stopped
environment:
# 环境配置
- SPRING_PROFILES_ACTIVE=prod
- TZ=Asia/Shanghai
# 数据库配置
- spring.datasource.url=jdbc:postgresql://117.72.68.72:54329/ecosys
- spring.datasource.username=postgres
- spring.datasource.password=TnLanWaidYSwTSG5
- spring.datasource.driver-class-name=org.postgresql.Driver
# HikariCP 连接池配置
- spring.datasource.hikari.maximum-pool-size=50
- spring.datasource.hikari.minimum-idle=5
- spring.datasource.hikari.connection-timeout=30000
- spring.datasource.hikari.idle-timeout=600000
- spring.datasource.hikari.max-lifetime=900000
- spring.datasource.hikari.pool-name=HikariPool-SyslogConsumer
- spring.datasource.hikari.auto-commit=false
- spring.datasource.hikari.schema=public
# Redis配置
- spring.redis.host=127.0.0.1
- spring.redis.port=6379
- spring.redis.password=
- spring.redis.database=0
- spring.redis.timeout=2000
- spring.cache.redis.time-to-live=600000
# Kafka配置
- spring.kafka.consumer.bootstrap-servers=192.168.222.130:9092
- spring.kafka.consumer.group-id=test-group-app
- spring.kafka.consumer.auto-offset-reset=latest
- spring.kafka.consumer.enable-auto-commit=false
- spring.kafka.consumer.topic=test-topic
- spring.kafka.consumer.max-poll-records=1000
- spring.kafka.listener.ack-mode=manual
- spring.kafka.listener.concurrency=2
- spring.kafka.listener.type=batch
# InfluxDB配置
- influxdb.url=http://192.168.222.131:8086
- influxdb.token=3Tvu-IZWtaY03UDkbUDlufD0kxn85keo9LhYQcv2Cxk0LJmXqqHkNVrO664DbaJAYwoGI7UIg904KqZC7Q_ZFA==
- influxdb.org=yelang
- influxdb.bucket=yelangbucket
- influxdb.batch.size=1000
- influxdb.flush.interval=1000
- influxdb.connection.timeout=30s
- influxdb.connection.read-timeout=30s
- influxdb.connection.write-timeout=60s
# Elasticsearch配置
- spring.elasticsearch.uris=http://192.168.1.174:9200
- spring.elasticsearch.username=CONTAINER_NAME
- spring.elasticsearch.password=t2NZCiajmdazxBrF
- spring.elasticsearch.connection-timeout=10s
- spring.elasticsearch.socket-timeout=30s
# API配置
- interlocking.api-key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
- interlocking.api.base-url=http://localhost:8089/xdrservice/interlocking
# 探针心跳配置
- probe.heartbeat.enabled=true
- probe.heartbeat.offline-threshold-minutes=10
- probe.status.check.cron=0 */10 * * * ?
- probe.heartbeat.tenant-id=000000
- probe.heartbeat.history.keep-days=10
- probe.heartbeat.history.cleanup-enabled=true
- probe.history.cleanup.cron=0 0 1 * * ?
# 告警健康检查配置
- alarm.health-check.alarm-hours=4
- alarm.health-check.alarm-visit-hours=2
- alarm.health-check.enabled=true
# 关联分析规则配置
- analysis.realtime.enabled=true
- analysis.realtime.check-interval-seconds=10
# 分区表检查配置
- partition.check.tomorrow.enabled=true
- partition.check.future.days=7
- partition.auto.create=true
# 定时任务配置
- spring.task.scheduling.pool.size=10
# 日志配置
- logging.level.com.common.schedule=INFO
- logging.level.com.common.service=INFO
# ETL配置
- etl.batch.page-size=1000
- etl.batch.insert-batch-size=500
- etl.schedule.cron=0 0 2 * * ?
# JVM配置
- JAVA_OPTS=-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
ports:
- "8089:8089"
volumes:
- syslog-consumer-logs:/app/logs
networks:
- xdr-network
# ============================================
# PostgreSQL 数据库 (可选,如已存在可注释掉)
# ============================================
postgres:
image: postgres:14-alpine
container_name: xdr-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ecosys
POSTGRES_USER: postgres
POSTGRES_PASSWORD: TnLanWaidYSwTSG5
TZ: Asia/Shanghai
ports:
- "54329:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- xdr-network
# ============================================
# 网络配置
# ============================================
networks:
xdr-network:
driver: bridge
# ============================================
# 卷配置
# ============================================
volumes:
syslog-consumer-logs:
driver: local
postgres_data:
driver: local
@@ -0,0 +1,116 @@
# ============================================
# Syslog Consumer 部署配置
# ============================================
# 使用方法: docker compose -f docker-compose-consumer.yaml up -d
# ============================================
services:
# ============================================
# Syslog Consumer - 数据消费服务 (平台端)
# ============================================
syslog-consumer:
build:
context: ./syslog-consumer
dockerfile: Dockerfile
image: syslog-consumer:1.0.1
container_name: syslog-consumer
restart: unless-stopped
environment:
# 环境配置
- SPRING_PROFILES_ACTIVE=dev
- TZ=Asia/Shanghai
# 数据库配置
- spring.datasource.url=jdbc:postgresql://117.72.68.72:54329/ecosys
- spring.datasource.username=postgres
- spring.datasource.password=TnLanWaidYSwTSG5
- spring.datasource.driver-class-name=org.postgresql.Driver
# HikariCP 连接池配置
- spring.datasource.hikari.maximum-pool-size=50
- spring.datasource.hikari.minimum-idle=5
- spring.datasource.hikari.connection-timeout=30000
- spring.datasource.hikari.idle-timeout=600000
- spring.datasource.hikari.max-lifetime=900000
- spring.datasource.hikari.pool-name=HikariPool-SyslogConsumer
- spring.datasource.hikari.auto-commit=false
- spring.datasource.hikari.schema=public
# Redis配置
- spring.redis.host=192.168.222.131
- spring.redis.port=6379
- spring.redis.password=
- spring.redis.database=0
- spring.redis.timeout=2000
- spring.cache.redis.time-to-live=600000
# Kafka配置
- spring.kafka.consumer.bootstrap-servers=192.168.222.130:9092
- spring.kafka.consumer.group-id=test-group-app
- spring.kafka.consumer.auto-offset-reset=latest
- spring.kafka.consumer.enable-auto-commit=false
- spring.kafka.consumer.topic=test-topic
- spring.kafka.consumer.max-poll-records=1000
- spring.kafka.listener.ack-mode=manual
- spring.kafka.listener.concurrency=2
- spring.kafka.listener.type=batch
# InfluxDB配置
- influxdb.url=http://192.168.222.131:8086
- influxdb.token=3Tvu-IZWtaY03UDkbUDlufD0kxn85keo9LhYQcv2Cxk0LJmXqqHkNVrO664DbaJAYwoGI7UIg904KqZC7Q_ZFA==
- influxdb.org=yelang
- influxdb.bucket=yelangbucket
- influxdb.batch.size=1000
- influxdb.flush.interval=1000
- influxdb.connection.timeout=30s
- influxdb.connection.read-timeout=30s
- influxdb.connection.write-timeout=60s
# Elasticsearch配置
- spring.elasticsearch.uris=http://192.168.1.174:9200
- spring.elasticsearch.username=CONTAINER_NAME
- spring.elasticsearch.password=t2NZCiajmdazxBrF
- spring.elasticsearch.connection-timeout=10s
- spring.elasticsearch.socket-timeout=30s
# API配置
- interlocking.api-key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
- interlocking.api.base-url=http://192.168.222.131:8089/xdrservice/interlocking
# 探针心跳配置
- probe.heartbeat.enabled=true
- probe.heartbeat.offline-threshold-minutes=10
- probe.status.check.cron=0 */10 * * * ?
- probe.heartbeat.tenant-id=000000
- probe.heartbeat.history.keep-days=10
- probe.heartbeat.history.cleanup-enabled=true
- probe.history.cleanup.cron=0 0 1 * * ?
# 告警健康检查配置
- alarm.health-check.alarm-hours=4
- alarm.health-check.alarm-visit-hours=2
- alarm.health-check.enabled=true
# 关联分析规则配置
- analysis.realtime.enabled=true
- analysis.realtime.check-interval-seconds=10
# 分区表检查配置
- partition.check.tomorrow.enabled=true
- partition.check.future.days=7
- partition.auto.create=true
# 定时任务配置
- spring.task.scheduling.pool.size=10
# 日志配置
- logging.level.com.common.schedule=INFO
- logging.level.com.common.service=INFO
# ETL配置
- etl.batch.page-size=1000
- etl.batch.insert-batch-size=500
- etl.schedule.cron=0 0 2 * * ?
# JVM配置
- JAVA_OPTS=-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
ports:
- "8089:8089"
volumes:
- /home/syslog/logs:/app/logs
networks:
- xdr-network
# ============================================
# 网络配置
# ============================================
networks:
xdr-network:
driver: bridge
@@ -13,7 +13,6 @@ docker build -t syslog-consumer:v1.X.X .
--3.停止容器 并删除
docker stop ct-syslog-consumer && docker rm ct-syslog-consumer
--4.运行docker 文件
docker run --restart unless-stopped -e TZ=Asia/Shanghai -d --name ct-syslog-consumer -p 8089:8089 -v /home/syslog/logs:/app/logs --privileged=true syslog-consumer:v1.X.X
众诚CMD
@@ -11,6 +11,7 @@
<name>syslog-consumer</name>
<artifactId>syslog-consumer</artifactId>
<version>1.0.0</version>
<properties>
<java.version>1.8</java.version>
<influxdb-client.version>6.9.0</influxdb-client.version>
@@ -5,6 +5,7 @@ import com.common.util.SyslogParser;
import com.config.AppConfig;
import com.influxdb.client.domain.WritePrecision;
import com.influxdb.client.write.Point;
import com.influxdb.client.WriteApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.beans.factory.annotation.Autowired;
@@ -277,7 +278,9 @@ public class SysLogProcessor {
.addField("receive_time", mapdev.get("receive_time")) // 添加字段
.addField("uuid", sysLogUUID)
.time(System.currentTimeMillis(), WritePrecision.MS) ;// 毫秒级时间戳
influxClient.writePointBlocking(point);
//influxClient.writePointBlocking(point);
///写入单点(异步,立即返回)
influxClient.writePoint(point);
System.out.println("influxdb wirte syslog ,value:"+ record.key());
// 日志信息插入pg XdrHoneypot 表
@@ -0,0 +1,82 @@
package com.common.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 探针心跳状态实体类
* 对应表: device_collect_heartbeat
*/
@Data
public class DeviceCollectHeartbeat {
/** 自增主键 */
private Long id;
/** 探针唯一标识(由探针上报) */
private String collectId;
/** 探针名称(可选) */
private String collectName;
/** 探针IP(支持IPv6 */
private String deviceIp;
/** 探针版本 */
private String appVersion;
/** 最后一次心跳时间(毫秒精度) */
private LocalDateTime lastHeartbeat;
/** 累计心跳次数(可监控丢包率) */
private Long heartbeatCount;
/** 在线状态:online / offline */
private String status;
/** 连续心跳失败次数(平台侧计算) */
private Integer failCount;
/** 最后更新时间 */
private LocalDateTime updateTime;
public DeviceCollectHeartbeat() {
this.heartbeatCount = 1L;
this.status = "online";
this.failCount = 0;
this.updateTime = LocalDateTime.now();
}
/**
* 更新心跳
*/
public void updateHeartbeat() {
this.lastHeartbeat = LocalDateTime.now();
this.heartbeatCount = (this.heartbeatCount == null ? 1L : this.heartbeatCount + 1);
this.status = "online";
this.failCount = 0;
this.updateTime = LocalDateTime.now();
}
/**
* 标记为离线
*/
public void markOffline() {
this.status = "offline";
this.failCount = (this.failCount == null ? 1 : this.failCount + 1);
this.updateTime = LocalDateTime.now();
}
/**
* 恢复在线
*/
public void markOnline() {
this.status = "online";
this.failCount = 0;
this.updateTime = LocalDateTime.now();
}
}
@@ -0,0 +1,48 @@
package com.common.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 探针心跳历史记录实体类
* 对应表: device_collect_heartbeat_history
*/
@Data
public class DeviceCollectHeartbeatHistory {
/** 自增主键 */
private Long id;
/** 探针唯一标识 */
private String collectId;
/** 心跳上报时间 */
private LocalDateTime heartbeatTime;
/** 探针IP */
private String deviceIp;
/** 探针版本 */
private String appVersion;
/** 记录创建时间 */
private LocalDateTime createdAt;
public DeviceCollectHeartbeatHistory() {
this.createdAt = LocalDateTime.now();
}
/**
* 从心跳状态创建历史记录
*/
public static DeviceCollectHeartbeatHistory fromHeartbeat(DeviceCollectHeartbeat heartbeat) {
DeviceCollectHeartbeatHistory history = new DeviceCollectHeartbeatHistory();
history.setCollectId(heartbeat.getCollectId());
history.setHeartbeatTime(heartbeat.getLastHeartbeat());
history.setDeviceIp(heartbeat.getDeviceIp());
history.setAppVersion(heartbeat.getAppVersion());
history.setCreatedAt(LocalDateTime.now());
return history;
}
}
@@ -0,0 +1,59 @@
package com.common.mapper;
import com.common.entity.DeviceCollectHeartbeatHistory;
import org.apache.ibatis.annotations.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* 探针心跳历史 Mapper 接口
*/
@Mapper
public interface DeviceCollectHeartbeatHistoryMapper {
/**
* 插入心跳历史记录
*/
@Insert("INSERT INTO device_collect_heartbeat_history (" +
"collect_id, heartbeat_time, device_ip, app_version, created_at " +
") VALUES (" +
"#{collectId}, #{heartbeatTime}, #{deviceIp}, #{appVersion}, #{createdAt} " +
")")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(DeviceCollectHeartbeatHistory history);
/**
* 批量插入心跳历史记录
*/
@Insert("<script>" +
"INSERT INTO device_collect_heartbeat_history " +
"(collect_id, heartbeat_time, device_ip, app_version, created_at ) " +
"VALUES " +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.collectId}, #{item.heartbeatTime}, #{item.deviceIp}, #{item.appVersion}, " +
"#{item.createdAt} )" +
"</foreach>" +
"</script>")
int batchInsert(@Param("list") List<DeviceCollectHeartbeatHistory> histories);
/**
* 根据探针ID查询历史记录
*/
@Select("SELECT * FROM device_collect_heartbeat_history " +
"WHERE collect_id = #{collectId} ORDER BY heartbeat_time DESC LIMIT #{limit}")
List<DeviceCollectHeartbeatHistory> selectByCollectId(@Param("collectId") String collectId,
@Param("limit") Integer limit);
/**
* 删除指定日期之前的记录
*/
@Delete("DELETE FROM device_collect_heartbeat_history WHERE created_at < #{beforeTime}")
int deleteBeforeTime(@Param("beforeTime") LocalDateTime beforeTime);
/**
* 统计指定日期之前的记录数
*/
@Select("SELECT COUNT(*) FROM device_collect_heartbeat_history WHERE created_at < #{beforeTime}")
long countBeforeTime(@Param("beforeTime") LocalDateTime beforeTime);
}
@@ -0,0 +1,82 @@
package com.common.mapper;
import com.common.entity.DeviceCollectHeartbeat;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* 探针心跳状态 Mapper 接口
*/
@Mapper
public interface DeviceCollectHeartbeatMapper {
/**
* 根据探针ID查询
*/
@Select("SELECT * FROM device_collect_heartbeat WHERE collect_id = #{collectId}")
DeviceCollectHeartbeat selectByCollectId(@Param("collectId") String collectId);
/**
* 查询所有探针
*/
@Select("SELECT * FROM device_collect_heartbeat ORDER BY update_time DESC")
List<DeviceCollectHeartbeat> selectAll();
/**
* 查询所有在线探针
*/
@Select("SELECT * FROM device_collect_heartbeat WHERE status = 'online' ORDER BY update_time DESC")
List<DeviceCollectHeartbeat> selectAllOnline();
/**
* 查询所有离线探针
*/
@Select("SELECT * FROM device_collect_heartbeat WHERE status = 'offline' ORDER BY update_time DESC")
List<DeviceCollectHeartbeat> selectAllOffline();
/**
* 插入或更新(根据collect_id
*/
@Insert("INSERT INTO device_collect_heartbeat (" +
"collect_id, collect_name, device_ip, app_version, last_heartbeat, " +
"heartbeat_count, status, fail_count, update_time " +
") VALUES (" +
"#{collectId}, #{collectName}, #{deviceIp}, #{appVersion}, #{lastHeartbeat}, " +
"#{heartbeatCount}, #{status}, #{failCount}, #{updateTime} " +
") ON CONFLICT (collect_id) DO UPDATE SET " +
"collect_name = EXCLUDED.collect_name, " +
"device_ip = EXCLUDED.device_ip, " +
"app_version = EXCLUDED.app_version, " +
"last_heartbeat = EXCLUDED.last_heartbeat, " +
"heartbeat_count = EXCLUDED.heartbeat_count, " +
"status = EXCLUDED.status, " +
"fail_count = EXCLUDED.fail_count, " +
"update_time = EXCLUDED.update_time")
int upsert(DeviceCollectHeartbeat heartbeat);
/**
* 更新探针状态
*/
@Update("UPDATE device_collect_heartbeat SET " +
"status = #{status}, fail_count = #{failCount}, update_time = #{updateTime} " +
"WHERE collect_id = #{collectId}")
int updateStatus(@Param("collectId") String collectId,
@Param("status") String status,
@Param("failCount") Integer failCount,
@Param("updateTime") java.time.LocalDateTime updateTime);
/**
* 查询超过指定时间未心跳的探针(用于检测离线)
*/
@Select("SELECT * FROM device_collect_heartbeat " +
"WHERE status = 'online' AND last_heartbeat < #{thresholdTime} " +
"ORDER BY last_heartbeat ASC")
List<DeviceCollectHeartbeat> selectOfflineCandidates(@Param("thresholdTime") java.time.LocalDateTime thresholdTime);
/**
* 删除指定日期之前的记录
*/
@Delete("DELETE FROM device_collect_heartbeat WHERE update_time < #{beforeTime}")
int deleteBeforeTime(@Param("beforeTime") java.time.LocalDateTime beforeTime);
}
@@ -20,10 +20,10 @@ public class AlarmHealthCheckScheduler {
private AlarmHealthCheckService alarmHealthCheckService;
/**
* 每5分钟执行一次告警健康检查
* 每30分钟执行一次告警健康检查
* 巡检告警表 alarm 和告警日志表 alarm_visit
*/
@Scheduled(cron = "0 */5 * * * ?")
@Scheduled(cron = "0 */30 * * * ?")
public void scheduledHealthCheck() {
logger.info("========== 开始执行告警健康检查 ==========");
long startTime = System.currentTimeMillis();
@@ -0,0 +1,70 @@
package com.common.schedule;
import com.common.service.ProbeHeartbeatService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 探针心跳历史清理调度器
* 定期清理过期的探针心跳历史记录
*/
@Component
public class ProbeHeartbeatHistoryCleanupScheduler {
private static final Logger logger = LoggerFactory.getLogger(ProbeHeartbeatHistoryCleanupScheduler.class);
@Autowired
private ProbeHeartbeatService probeHeartbeatService;
/** 保留天数,默认10天 */
@Value("${probe.heartbeat.history.keep-days:10}")
private int keepDays;
/** 是否启用清理 */
@Value("${probe.heartbeat.history.cleanup-enabled:true}")
private boolean cleanupEnabled;
/**
* 每天凌晨1点执行清理
*/
@Scheduled(cron = "${probe.history.cleanup.cron:0 0 1 * * ?}")
public void cleanupHistory() {
if (!cleanupEnabled) {
logger.debug("心跳历史清理已禁用");
return;
}
logger.info("========== 开始清理心跳历史记录 ==========");
long startTime = System.currentTimeMillis();
try {
long deleted = probeHeartbeatService.cleanupHistory(keepDays);
long elapsedTime = System.currentTimeMillis() - startTime;
if (deleted > 0) {
logger.info("心跳历史清理完成,删除 {} 条 {} 天前的记录, 耗时: {}ms",
deleted, keepDays, elapsedTime);
} else {
logger.info("心跳历史清理完成,无过期记录需要清理, 耗时: {}ms", elapsedTime);
}
} catch (Exception e) {
logger.error("心跳历史清理异常: {}", e.getMessage(), e);
}
logger.info("========== 心跳历史清理结束 ==========");
}
/**
* 手动触发清理
* @return 删除的记录数
*/
public long manualCleanup() {
logger.info("手动触发心跳历史清理");
return probeHeartbeatService.cleanupHistory(keepDays);
}
}
@@ -0,0 +1,87 @@
package com.common.schedule;
import com.common.entity.DeviceCollectHeartbeat;
import com.common.service.ProbeHeartbeatService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 探针状态检查调度器
* 定期检查探针是否离线,触发告警
*/
@Component
public class ProbeStatusCheckScheduler {
private static final Logger logger = LoggerFactory.getLogger(ProbeStatusCheckScheduler.class);
@Autowired
private ProbeHeartbeatService probeHeartbeatService;
@Value("${probe.heartbeat.enabled:true}")
private boolean heartbeatEnabled;
/**
* 检查探针状态
* 默认每10分钟检查一次(可配置)
*/
@Scheduled(cron = "${probe.status.check.cron:0 */10 * * * ?}")
public void checkProbeStatus() {
if (!heartbeatEnabled) {
logger.debug("探针状态检查已禁用");
return;
}
logger.info("========== 开始探针状态检查 ==========");
long startTime = System.currentTimeMillis();
try {
// 检查并标记离线探针
List<DeviceCollectHeartbeat> newlyOffline = probeHeartbeatService.checkAndMarkOfflineProbes();
long elapsedTime = System.currentTimeMillis() - startTime;
if (!newlyOffline.isEmpty()) {
logger.warn("探针状态检查完成,发现 {} 个新离线探针, 离线阈值: {}分钟, 耗时: {}ms",
newlyOffline.size(),
probeHeartbeatService.getOfflineThresholdMinutes(),
elapsedTime);
for (DeviceCollectHeartbeat probe : newlyOffline) {
logger.warn("离线探针: collectId={}, ip={}, name={}",
probe.getCollectId(), probe.getDeviceIp(), probe.getCollectName());
}
} else {
logger.info("探针状态检查完成,所有探针在线, 耗时: {}ms", elapsedTime);
}
// 输出当前探针统计
List<DeviceCollectHeartbeat> allProbes = probeHeartbeatService.getAllProbes();
long onlineCount = allProbes.stream().filter(p -> "online".equals(p.getStatus())).count();
long offlineCount = allProbes.size() - onlineCount;
logger.info("探针统计: 总数={}, 在线={}, 离线={}",
allProbes.size(), onlineCount, offlineCount);
} catch (Exception e) {
logger.error("探针状态检查异常: {}", e.getMessage(), e);
}
logger.info("========== 探针状态检查结束 ==========");
}
/**
* 手动触发探针状态检查
* @return 新增的离线探针数量
*/
public int manualCheck() {
logger.info("手动触发探针状态检查");
List<DeviceCollectHeartbeat> newlyOffline = probeHeartbeatService.checkAndMarkOfflineProbes();
return newlyOffline.size();
}
}
@@ -272,6 +272,9 @@ public class AccessLogAlertService {
logObject.put("_source.vlan_id", "");
logObject.put("_type", "skyeye-file");
//补充syslog_normal_data.device_id 字段
logObject.put("_source.device_id", logData.getDeviceId() != null ? logData.getDeviceId() : "");
requestArray.add(logObject);
}
@@ -416,12 +419,33 @@ public class AccessLogAlertService {
log.debug("算法:{},ID:{} ,AlarmNme:{} 没有返回 origin_log节点.",AlgorithmName, alarmVisit.getId(), alarmVisit.getAlarmName());
return false;
}
/** 旧版有BUG
alarmVisit.setAttackPort( new Integer[]{alarmResult.getInteger("_source.sport")} );
alarmVisit.setVictimPort( new Integer[]{alarmResult.getInteger("_source.dport")} );
alarmVisit.setAttackMethod(alarmResult.getString("_source.method") );
String deviceIp= alarmResult.getString("_source.device_ip");
//alarmVisit.setDeviceId( new Integer[]{ getDeviceID(deviceIp)} );
alarmVisit.setHttpStatus( alarmResult.getString("_source.status"));
**/
// _source.sport/dport 在 JSON 中为字符串类型,需要用 getString() 读取后 parseInt
String sportStr = originLogObject.getString("_source.sport");
if (sportStr != null && !sportStr.isEmpty()) {
alarmVisit.setAttackPort(new Integer[]{Integer.parseInt(sportStr)});
}
String dportStr = originLogObject.getString("_source.dport");
if (dportStr != null && !dportStr.isEmpty()) {
alarmVisit.setVictimPort(new Integer[]{Integer.parseInt(dportStr)});
}
alarmVisit.setAttackMethod(originLogObject.getString("_source.method"));
String deviceIp = originLogObject.getString("_source.device_ip");
//alarmVisit.setDeviceId( new Integer[]{ getDeviceID(deviceIp)} );
//补充alarm_visist.device_id
String deviceidStr = originLogObject.getString("_source.device_id");
if (deviceidStr != null && !deviceidStr.isEmpty()) {
alarmVisit.setDeviceId(new Integer[]{Integer.parseInt(deviceidStr)});
}
return true;
} catch (Exception e) {
log.error("算法:{} 补充原始记录日志字段异常。error:{} ",AlgorithmName,e.getMessage(), e );
@@ -119,7 +119,6 @@ public class AlarmHealthCheckService {
String content = String.format(
"【数据巡检告警】%n" +
"表名: %s%n" +
"告警时间: %s%n" +
"告警描述: 最近%d小时内无新数据%n" +
"建议: 请检查数据采集服务是否正常运行%n" +
"状态: 需要人工介入检查",
@@ -0,0 +1,286 @@
package com.common.service;
import com.common.entity.DeviceCollectHeartbeat;
import com.common.entity.DeviceCollectHeartbeatHistory;
import com.common.entity.WecomNotification;
import com.common.mapper.DeviceCollectHeartbeatMapper;
import com.common.mapper.DeviceCollectHeartbeatHistoryMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 探针心跳服务
* 管理探针心跳状态、心跳历史、以及离线告警
*/
@Service
public class ProbeHeartbeatService {
private static final Logger logger = LoggerFactory.getLogger(ProbeHeartbeatService.class);
@Autowired
private DeviceCollectHeartbeatMapper heartbeatMapper;
@Autowired
private DeviceCollectHeartbeatHistoryMapper historyMapper;
@Autowired
private WecomNotificationService wecomNotificationService;
/** 离线阈值(分钟) */
@Value("${probe.heartbeat.offline-threshold-minutes:10}")
private int offlineThresholdMinutes;
/** 租户ID */
@Value("${probe.heartbeat.tenant-id:000000}")
private String tenantId;
/** 已发送离线告警的探针集合(用于告警收敛) */
private final Map<String, Boolean> alertedProbes = new ConcurrentHashMap<>();
/**
* 接收探针心跳
* @param heartbeatData 心跳数据
* @return 探针实体
*/
@Transactional
public DeviceCollectHeartbeat receiveHeartbeat(ProbeHeartbeatData heartbeatData) {
String collectId = heartbeatData.getCollectId();
LocalDateTime now = LocalDateTime.now();
// 查询现有探针状态
DeviceCollectHeartbeat existing = heartbeatMapper.selectByCollectId(collectId);
boolean wasOffline = existing != null && "offline".equals(existing.getStatus());
// 创建或更新探针状态
DeviceCollectHeartbeat heartbeat = new DeviceCollectHeartbeat();
heartbeat.setCollectId(collectId);
heartbeat.setCollectName(heartbeatData.getCollectName());
heartbeat.setDeviceIp(heartbeatData.getDeviceIp());
heartbeat.setAppVersion(heartbeatData.getAppVersion());
heartbeat.setLastHeartbeat(now);
heartbeat.setHeartbeatCount(existing == null ? 1L : existing.getHeartbeatCount() + 1);
heartbeat.setStatus("online");
heartbeat.setFailCount(0);
heartbeat.setUpdateTime(now);
// 如果有ID则保留
if (existing != null) {
heartbeat.setId(existing.getId());
}
// 插入/更新主表
heartbeatMapper.upsert(heartbeat);
// 插入历史记录
DeviceCollectHeartbeatHistory history = DeviceCollectHeartbeatHistory.fromHeartbeat(heartbeat);
historyMapper.insert(history);
// 如果从离线恢复,发送恢复通知
if (wasOffline) {
sendRecoveryNotification(heartbeat);
alertedProbes.remove(collectId); // 清除告警标记
logger.info("探针 {} 已恢复在线", collectId);
}
logger.debug("接收探针 {} 心跳,IP: {}, 版本: {}", collectId, heartbeat.getDeviceIp(), heartbeat.getAppVersion());
return heartbeat;
}
/**
* 获取所有探针状态
*/
public List<DeviceCollectHeartbeat> getAllProbes() {
return heartbeatMapper.selectAll();
}
/**
* 获取在线探针
*/
public List<DeviceCollectHeartbeat> getOnlineProbes() {
return heartbeatMapper.selectAllOnline();
}
/**
* 获取离线探针
*/
public List<DeviceCollectHeartbeat> getOfflineProbes() {
return heartbeatMapper.selectAllOffline();
}
/**
* 根据探针ID获取状态
*/
public DeviceCollectHeartbeat getProbeById(String collectId) {
return heartbeatMapper.selectByCollectId(collectId);
}
/**
* 检查探针状态并标记离线
* @return 新增的离线探针列表
*/
@Transactional
public List<DeviceCollectHeartbeat> checkAndMarkOfflineProbes() {
LocalDateTime thresholdTime = LocalDateTime.now().minusMinutes(offlineThresholdMinutes);
List<DeviceCollectHeartbeat> candidates = heartbeatMapper.selectOfflineCandidates(thresholdTime);
List<DeviceCollectHeartbeat> newlyOffline = new java.util.ArrayList<>();
for (DeviceCollectHeartbeat probe : candidates) {
String collectId = probe.getCollectId();
// 检查是否已经发送过告警(告警收敛)
if (alertedProbes.containsKey(collectId)) {
// 仅更新状态,不重复告警
heartbeatMapper.updateStatus(collectId, "offline",
(probe.getFailCount() == null ? 0 : probe.getFailCount()) + 1, LocalDateTime.now());
continue;
}
// 标记为离线
heartbeatMapper.updateStatus(collectId, "offline",
(probe.getFailCount() == null ? 0 : probe.getFailCount()) + 1, LocalDateTime.now());
// 发送离线告警
sendOfflineAlert(probe);
// 标记已告警
alertedProbes.put(collectId, true);
newlyOffline.add(probe);
logger.warn("探针 {} 已离线,最后心跳时间: {}", collectId, probe.getLastHeartbeat());
}
return newlyOffline;
}
/**
* 发送离线告警
*/
private void sendOfflineAlert(DeviceCollectHeartbeat probe) {
String content = String.format(
"【探针离线告警】%n" +
"探针ID: %s%n" +
"探针名称: %s%n" +
"探针IP: %s%n" +
"版本: %s%n" +
"离线时间: %s%n" +
"最后心跳: %s%n" +
"建议: 请检查探针服务是否正常运行",
probe.getCollectId(),
probe.getCollectName() != null ? probe.getCollectName() : "未知",
probe.getDeviceIp(),
probe.getAppVersion() != null ? probe.getAppVersion() : "未知",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
probe.getLastHeartbeat() != null ? probe.getLastHeartbeat().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : "未知"
);
try {
WecomNotification notification = wecomNotificationService.sendAlert(
"探针离线-" + probe.getCollectId(),
"probe_offline",
"4", // 紧急
content,
probe.getDeviceIp()
);
logger.info("发送探针离线告警成功, 通知ID: {}", notification.getWecomNotificationId());
} catch (Exception e) {
logger.error("发送探针离线告警失败: {}", e.getMessage(), e);
}
}
/**
* 发送恢复通知
*/
private void sendRecoveryNotification(DeviceCollectHeartbeat probe) {
String content = String.format(
"【探针恢复通知】%n" +
"探针ID: %s%n" +
"探针名称: %s%n" +
"探针IP: %s%n" +
"恢复时间: %s%n" +
"状态: 已恢复正常",
probe.getCollectId(),
probe.getCollectName() != null ? probe.getCollectName() : "未知",
probe.getDeviceIp(),
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
);
try {
WecomNotification notification = wecomNotificationService.sendAlert(
"探针恢复-" + probe.getCollectId(),
"probe_recovery",
"1", // 低
content,
probe.getDeviceIp()
);
logger.info("发送探针恢复通知成功, 通知ID: {}", notification.getWecomNotificationId());
} catch (Exception e) {
logger.error("发送探针恢复通知失败: {}", e.getMessage(), e);
}
}
/**
* 清理历史记录
* @param daysToKeep 保留天数
* @return 删除的记录数
*/
public long cleanupHistory(int daysToKeep) {
LocalDateTime beforeTime = LocalDateTime.now().minusDays(daysToKeep);
long count = historyMapper.countBeforeTime(beforeTime);
if (count > 0) {
int deleted = historyMapper.deleteBeforeTime(beforeTime);
logger.info("清理心跳历史记录: 删除 {} 条, 保留 {} 天前的数据", deleted, daysToKeep);
return deleted;
}
return 0;
}
/**
* 清除指定探针的告警标记(手动恢复)
*/
public void clearAlertFlag(String collectId) {
alertedProbes.remove(collectId);
logger.info("清除探针 {} 的告警标记", collectId);
}
/**
* 获取离线阈值配置
*/
public int getOfflineThresholdMinutes() {
return offlineThresholdMinutes;
}
/**
* 探针心跳数据DTO
*/
public static class ProbeHeartbeatData {
private String collectId;
private String collectName;
private String deviceIp;
private String appVersion;
private String loadStatus;
public String getCollectId() { return collectId; }
public void setCollectId(String collectId) { this.collectId = collectId; }
public String getCollectName() { return collectName; }
public void setCollectName(String collectName) { this.collectName = collectName; }
public String getDeviceIp() { return deviceIp; }
public void setDeviceIp(String deviceIp) { this.deviceIp = deviceIp; }
public String getAppVersion() { return appVersion; }
public void setAppVersion(String appVersion) { this.appVersion = appVersion; }
public String getLoadStatus() { return loadStatus; }
public void setLoadStatus(String loadStatus) { this.loadStatus = loadStatus; }
}
}
@@ -0,0 +1,243 @@
package com.haobang.controller;
import com.common.entity.DeviceCollectHeartbeat;
import com.common.service.ProbeHeartbeatService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 探针心跳接收接口
* 提供给探针端(syslog-serve)调用
*/
@RestController
@RequestMapping("/interlocking/probe")
public class ProbeHeartbeatController {
private static final Logger logger = LoggerFactory.getLogger(ProbeHeartbeatController.class);
@Autowired
private ProbeHeartbeatService probeHeartbeatService;
@Value("${interlocking.api-key:}")
private String expectedApiKey;
/**
* 接收探针心跳
* POST /interlocking/probe/heartbeat
*
* Headers: X-API-KEY: {api-key}
* Body: {
* "collectId": "probe-001",
* "collectName": "测试探针",
* "deviceIp": "192.168.1.100",
* "appVersion": "1.0.0",
* "loadStatus": "{}"
* }
*/
@PostMapping("/heartbeat")
public ResponseEntity<Map<String, Object>> receiveHeartbeat(
@RequestHeader(value = "X-API-KEY", required = false) String apiKey,
@RequestBody ProbeHeartbeatService.ProbeHeartbeatData heartbeatData) {
Map<String, Object> result = new HashMap<>();
// 验证API Key
if (expectedApiKey != null && !expectedApiKey.isEmpty()
&& !expectedApiKey.equals(apiKey)) {
result.put("code", 401);
result.put("message", "API Key验证失败");
logger.warn("探针心跳请求API Key验证失败");
return ResponseEntity.status(401).body(result);
}
// 验证必填字段
if (heartbeatData.getCollectId() == null || heartbeatData.getCollectId().isEmpty()) {
result.put("code", 400);
result.put("message", "collectId不能为空");
return ResponseEntity.badRequest().body(result);
}
try {
DeviceCollectHeartbeat heartbeat = probeHeartbeatService.receiveHeartbeat(heartbeatData);
Map<String, Object> data = new HashMap<>();
data.put("collectId", heartbeat.getCollectId());
data.put("status", heartbeat.getStatus());
data.put("lastHeartbeat", heartbeat.getLastHeartbeat().toString());
result.put("code", 200);
result.put("message", "success");
result.put("data", data);
return ResponseEntity.ok(result);
} catch (Exception e) {
logger.error("处理探针心跳异常: {}", e.getMessage(), e);
result.put("code", 500);
result.put("message", "处理心跳失败: " + e.getMessage());
return ResponseEntity.status(500).body(result);
}
}
/**
* 获取所有探针状态
* GET /interlocking/probe/list
*/
@GetMapping("/list")
public ResponseEntity<Map<String, Object>> getAllProbes(
@RequestHeader(value = "X-API-KEY", required = false) String apiKey) {
Map<String, Object> result = new HashMap<>();
if (expectedApiKey != null && !expectedApiKey.isEmpty()
&& !expectedApiKey.equals(apiKey)) {
result.put("code", 401);
result.put("message", "API Key验证失败");
return ResponseEntity.status(401).body(result);
}
try {
List<DeviceCollectHeartbeat> probes = probeHeartbeatService.getAllProbes();
result.put("code", 200);
result.put("message", "success");
result.put("data", probes);
result.put("total", probes.size());
return ResponseEntity.ok(result);
} catch (Exception e) {
logger.error("获取探针列表异常: {}", e.getMessage(), e);
result.put("code", 500);
result.put("message", "获取探针列表失败: " + e.getMessage());
return ResponseEntity.status(500).body(result);
}
}
/**
* 获取在线探针列表
* GET /interlocking/probe/online
*/
@GetMapping("/online")
public ResponseEntity<Map<String, Object>> getOnlineProbes(
@RequestHeader(value = "X-API-KEY", required = false) String apiKey) {
Map<String, Object> result = new HashMap<>();
if (expectedApiKey != null && !expectedApiKey.isEmpty()
&& !expectedApiKey.equals(apiKey)) {
result.put("code", 401);
result.put("message", "API Key验证失败");
return ResponseEntity.status(401).body(result);
}
try {
List<DeviceCollectHeartbeat> probes = probeHeartbeatService.getOnlineProbes();
result.put("code", 200);
result.put("message", "success");
result.put("data", probes);
result.put("total", probes.size());
return ResponseEntity.ok(result);
} catch (Exception e) {
logger.error("获取在线探针列表异常: {}", e.getMessage(), e);
result.put("code", 500);
result.put("message", "获取在线探针列表失败: " + e.getMessage());
return ResponseEntity.status(500).body(result);
}
}
/**
* 获取离线探针列表
* GET /interlocking/probe/offline
*/
@GetMapping("/offline")
public ResponseEntity<Map<String, Object>> getOfflineProbes(
@RequestHeader(value = "X-API-KEY", required = false) String apiKey) {
Map<String, Object> result = new HashMap<>();
if (expectedApiKey != null && !expectedApiKey.isEmpty()
&& !expectedApiKey.equals(apiKey)) {
result.put("code", 401);
result.put("message", "API Key验证失败");
return ResponseEntity.status(401).body(result);
}
try {
List<DeviceCollectHeartbeat> probes = probeHeartbeatService.getOfflineProbes();
result.put("code", 200);
result.put("message", "success");
result.put("data", probes);
result.put("total", probes.size());
return ResponseEntity.ok(result);
} catch (Exception e) {
logger.error("获取离线探针列表异常: {}", e.getMessage(), e);
result.put("code", 500);
result.put("message", "获取离线探针列表失败: " + e.getMessage());
return ResponseEntity.status(500).body(result);
}
}
/**
* 获取单个探针状态
* GET /interlocking/probe/{collectId}
*/
@GetMapping("/{collectId}")
public ResponseEntity<Map<String, Object>> getProbe(
@RequestHeader(value = "X-API-KEY", required = false) String apiKey,
@PathVariable String collectId) {
Map<String, Object> result = new HashMap<>();
if (expectedApiKey != null && !expectedApiKey.isEmpty()
&& !expectedApiKey.equals(apiKey)) {
result.put("code", 401);
result.put("message", "API Key验证失败");
return ResponseEntity.status(401).body(result);
}
try {
DeviceCollectHeartbeat probe = probeHeartbeatService.getProbeById(collectId);
if (probe == null) {
result.put("code", 404);
result.put("message", "探针不存在");
return ResponseEntity.status(404).body(result);
}
result.put("code", 200);
result.put("message", "success");
result.put("data", probe);
return ResponseEntity.ok(result);
} catch (Exception e) {
logger.error("获取探针状态异常: {}", e.getMessage(), e);
result.put("code", 500);
result.put("message", "获取探针状态失败: " + e.getMessage());
return ResponseEntity.status(500).body(result);
}
}
/**
* 健康检查
* GET /interlocking/probe/health
*/
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> health() {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "OK");
result.put("service", "probe-heartbeat-api");
result.put("timestamp", java.time.LocalDateTime.now().toString());
return ResponseEntity.ok(result);
}
}
@@ -67,6 +67,7 @@ public class InfluxDBClient implements AutoCloseable {
public void writePoint(Point point) {
try {
writeApi.writePoint(point);
// 优雅关闭 WriteApi,确保数据发送完成
} catch (Exception e) {
logger.error("Failed to write point to InfluxDB: {}", e.getMessage());
throw e;
@@ -156,7 +156,7 @@ analysis.realtime.check-interval-seconds: 10
# API-KEY认证(32位,建议使用随机生成的密钥)
interlocking.api-key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
# API接口基础URL(供syslog-serve调用)
interlocking.api.base-url=http://localhost:8089/xdrservice/interlocking
interlocking.api.base-url=http://192.168.222.131:8089/xdrservice/interlocking
# ============================================
# 告警健康检查配置
@@ -166,4 +166,23 @@ alarm.health-check.alarm-hours=2
# 告警日志表无数据阈值(小时)
alarm.health-check.alarm-visit-hours=4
# 是否启用定时巡检
alarm.health-check.enabled=true
alarm.health-check.enabled=true
# ============================================
# 探针心跳检测配置
# ============================================
# 是否启用心跳检测
probe.heartbeat.enabled=true
# 探针离线阈值(分钟),超过此时间未收到心跳则判定为离线
probe.heartbeat.offline-threshold-minutes=10
# 状态检查Cron表达式(默认每10分钟)
probe.status.check.cron=0 */10 * * * ?
# 探针租户ID
probe.heartbeat.tenant-id=000000
# 心跳历史保留天数
probe.heartbeat.history.keep-days=10
# 是否启用历史清理
probe.heartbeat.history.cleanup-enabled=true
# 历史清理Cron表达式(默认每天凌晨1点)
probe.history.cleanup.cron=0 0 1 * * ?
@@ -0,0 +1,188 @@
#Server Configuration
server.port=8089
server.servlet.context-path=/xdrservice
#server.address=0.0.0.0
server.tomcat.uri-encoding=UTF-8
server.error.include-message=always
server.error.include-binding-errors=always
#run.environment: dev|test|pro
server.run.environment=pro
# Syslog Server Configuration
syslog.tcp.port=514
syslog.udp.port=514
syslog.max.frame.length=65536
syslog.buffer.size=1000
syslog.sm4.generateKey=f79548ab6fa8a304fc0115e17230358a
# InfluxDB 2.7 Configuration
influxdb.url=http://10.150 81.211:8087
influxdb.token=LFjXZyRxTf1V84oN-wwjhSjS4qIK-ZMoHzQJB67ir3qHNSBVJbMcTkPuNmM0cNxvzFEDWLYNzrz1VJKMitY5hw==
influxdb.org=influxdb
influxdb.bucket=yelangbucket
influxdb.batch.size=1000
influxdb.flush.interval=1000
influxdb.retry.attempts=3
influxdb.retry.delay=1000
# InfluxDB 2.7 连接超时配置
influxdb.connection.timeout=30s
influxdb.connection.read-timeout=30s
influxdb.connection.write-timeout=60s
# Application Configuration
app.worker.threads=8
app.max.queue.size=10000
app.metrics.enabled=true
#database Configuration
spring.datasource.url=jdbc:postgresql://10.150 81.209:5432/ecosys
spring.datasource.username=postgres
spring.datasource.password=caZ2TcmXNSW8L2Ap
spring.datasource.driver-class-name=org.postgresql.Driver
# mybatis Configuration
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.common.entity
#mybatis handler 类
mybatis.configuration.default-statement-timeout=30
mybatis.configuration.default-fetch-size=1000
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.type-handlers-package=com.Modules.etl.handler
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.type-handlers-package=com.Modules.etl.handler
# kafka Configuration
spring.kafka.consumer.bootstrap-servers=10.150 81.211:9092
spring.kafka.consumer.group-id=agent-syslog-group
spring.kafka.consumer.auto-offset-reset=latest
spring.kafka.consumer.enable-auto-commit=false
spring.kafka.consumer.auto-commit-interval=1000
spring.kafka.consumer.topic=agent-syslog-topic
spring.kafka.consumer.max-poll-records=1000
spring.kafka.consumer.properties.max.poll.interval.ms=300000
spring.kafka.consumer.properties.session.timeout.ms=45000
spring.kafka.consumer.fetch-min-size= 1048576
spring.kafka.listener.ack-mode= manual
spring.kafka.listener.concurrency= 2
spring.kafka.listener.type=batch
# 定时任务配置
spring.task.scheduling.pool.size=10
# 日志配置
logging.level.com.common.schedule=INFO
logging.level.com.common.service=INFO
# 分区表检查配置
partition.check.tomorrow.enabled=true
partition.check.future.days=7
partition.auto.create=true
# 生产环境缓存配置
spring.redis.host=192.168.4.26
spring.redis.port=6379
# 密码(如果没有设置密码,可以省略)
spring.redis.password=123456
spring.redis.database=0
spring.redis.timeout=5000
#spring.redis.password=${REDIS_PASSWORD:default_prod_password}
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=5000
spring.redis.lettuce.pool.max-idle=10
spring.redis.lettuce.pool.min-idle=5
# 生产环境缓存时间较长
spring.cache.redis.time-to-live=3600000
# 应用处理器配置
app.processor.thread-pool.core-pool-size=10
app.processor.thread-pool.max-pool-size=20
app.processor.thread-pool.queue-capacity=2000
app.processor.thread-pool.keep-alive-seconds=60
app.processor.batch-size=100
app.processor.process-timeout-ms=30000
# 配置 Elasticsearch
# Elasticsearch连接地址
spring.elasticsearch.uris=http://192.168.1.174:9200
# 配置 Elasticsearch 用户名
spring.elasticsearch.username=CONTAINER_NAME
# 配置 Elasticsearch 密码
spring.elasticsearch.password=t2NZCiajmdazxBrF
# 连接超时时间
spring.elasticsearch.connection-timeout=10s
# Socket 超时时间
spring.elasticsearch.socket-timeout=30s
# ETL配置
etl.batch.page-size=1000
etl.batch.insert-batch-size=500
etl.schedule.cron=0 0 2 * * ?
# ============================================
# HikariCP Connection Pool Configuration
# ============================================
spring.datasource.hikari.maximum-pool-size=50
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=900000
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.validation-timeout=5000
spring.datasource.hikari.leak-detection-threshold=30000
spring.datasource.hikari.pool-name=HikariPool-SyslogConsumer
spring.datasource.hikari.auto-commit=false
spring.datasource.hikari.schema=public
# 关联分析规则配置
analysis.realtime.enabled= true
# 检查间隔(秒) - 默认10秒
analysis.realtime.check-interval-seconds: 10
# ============================================
# 探针联动API配置
# ============================================
# API-KEY认证(32位,建议使用随机生成的密钥)
interlocking.api-key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
# API接口基础URL(供syslog-serve调用)
interlocking.api.base-url=http://10.150 81.210:8089/xdrservice/interlocking
# ============================================
# 告警健康检查配置
# ============================================
# 告警表无数据阈值(小时)
alarm.health-check.alarm-hours=2
# 告警日志表无数据阈值(小时)
alarm.health-check.alarm-visit-hours=4
# 是否启用定时巡检
alarm.health-check.enabled=true
# ============================================
# 探针心跳检测配置
# ============================================
# 是否启用心跳检测
probe.heartbeat.enabled=true
# 探针离线阈值(分钟),超过此时间未收到心跳则判定为离线
probe.heartbeat.offline-threshold-minutes=10
# 状态检查Cron表达式(默认每10分钟)
probe.status.check.cron=0 */10 * * * ?
# 探针租户ID
probe.heartbeat.tenant-id=000000
# 心跳历史保留天数
probe.heartbeat.history.keep-days=10
# 是否启用历史清理
probe.heartbeat.history.cleanup-enabled=true
# 历史清理Cron表达式(默认每天凌晨1点)
probe.history.cleanup.cron=0 0 1 * * ?
@@ -157,7 +157,7 @@ analysis.realtime.check-interval-seconds: 10
# API-KEY认证(32位,建议使用随机生成的密钥)
interlocking.api-key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
# API接口基础URL(供syslog-serve调用)
interlocking.api.base-url=http://localhost:8089/xdrservice/interlocking
interlocking.api.base-url=http://192.168.4.26:8089/xdrservice/interlocking
# ============================================
# 告警健康检查配置
@@ -167,4 +167,22 @@ alarm.health-check.alarm-hours=2
# 告警日志表无数据阈值(小时)
alarm.health-check.alarm-visit-hours=4
# 是否启用定时巡检
alarm.health-check.enabled=true
alarm.health-check.enabled=true
# ============================================
# 探针心跳检测配置
# ============================================
# 是否启用心跳检测
probe.heartbeat.enabled=true
# 探针离线阈值(分钟),超过此时间未收到心跳则判定为离线
probe.heartbeat.offline-threshold-minutes=10
# 状态检查Cron表达式(默认每10分钟)
probe.status.check.cron=0 */10 * * * ?
# 探针租户ID
probe.heartbeat.tenant-id=000000
# 心跳历史保留天数
probe.heartbeat.history.keep-days=10
# 是否启用历史清理
probe.heartbeat.history.cleanup-enabled=true
# 历史清理Cron表达式(默认每天凌晨1点)
probe.history.cleanup.cron=0 0 1 * * ?
@@ -157,7 +157,7 @@ analysis.realtime.check-interval-seconds: 10
# API-KEY认证(32位,建议使用随机生成的密钥)
interlocking.api-key=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
# API接口基础URL(供syslog-serve调用)
interlocking.api.base-url=http://localhost:8089/xdrservice/interlocking
interlocking.api.base-url=http://192.168.4.26:8089/xdrservice/interlocking
# ============================================
# 告警健康检查配置
@@ -167,4 +167,22 @@ alarm.health-check.alarm-hours=2
# 告警日志表无数据阈值(小时)
alarm.health-check.alarm-visit-hours=4
# 是否启用定时巡检
alarm.health-check.enabled=true
alarm.health-check.enabled=true
# ============================================
# 探针心跳检测配置
# ============================================
# 是否启用心跳检测
probe.heartbeat.enabled=true
# 探针离线阈值(分钟),超过此时间未收到心跳则判定为离线
probe.heartbeat.offline-threshold-minutes=10
# 状态检查Cron表达式(默认每10分钟)
probe.status.check.cron=0 */10 * * * ?
# 探针租户ID
probe.heartbeat.tenant-id=000000
# 心跳历史保留天数
probe.heartbeat.history.keep-days=10
# 是否启用历史清理
probe.heartbeat.history.cleanup-enabled=true
# 历史清理Cron表达式(默认每天凌晨1点)
probe.history.cleanup.cron=0 0 1 * * ?