feat(syslog-consumer-rule): 增加多节点原始日志采集并修复天眼/微步JSON规则匹配

一、增加现场原始日志采集功能

1. 新增按设备采集原始日志的API:
   - 指定设备ID;
   - 采集时长最长10分钟;
   - 单次最多返回50条样本。

2. 样本选择策略:
   - 优先保留时间窗口内最早到达的N条日志;
   - 后续出现JSON解析异常、非标、规则执行异常、InfluxDB/达梦写入异常或Retry/DLT时,
     使用异常日志逐条替换最早的正常样本;
   - 异常样本同样遵循先到先得。

3. 支持三个syslog-consumer-rule实例联合采集:
   - 采集请求只需发送到任意一个实例;
   - 三个实例通过公共Redis同步采集任务并汇总候选样本;
   - 不需要配置其他实例IP;
   - Redis只保存有界候选集,不保存时间窗口内的全部日志。

4. 生命周期和资源控制:
   - 采集到期后自动停止接收新日志;
   - 默认进入30秒DRAINING阶段,等待异步处理异常完成样本替换;
   - 最终结果默认保留30分钟,之后自动删除;
   - 单实例、单会话最多保留“前N条+前N条异常”;
   - Redis故障不阻断Kafka主消费链路。

5. 返回规则解析和非标表截断之前的完整原始报文,不受
   HB_XDR_TRUNCATED字段截断影响。

6. 原始日志采集和多实例汇聚默认启用:
   SYSLOG_RAW_CAPTURE_ENABLED=true
   SYSLOG_RAW_CAPTURE_CLUSTER_ENABLED=true

二、修复天眼/微步JSON规则匹配问题

1. JSON规则配置了cropper时,先判断原始报文是否包含对应head_key;
   不匹配的规则不再将当前报文误判为非法JSON。

2. 同一设备绑定多条规则时,单条不适用或解析失败的JSON规则不再立即终止整个规则链,
   程序会继续匹配后续适用规则。

3. 只有全部适用JSON规则均解析失败、且没有其他规则成功处理时,才写入
   “JSON解析异常”非标记录。

4. 对cropper截取后仍包含Syslog或设备前缀的内容,在JSON专用路径中重新收敛到
   第一个“{”至最后一个“}”,确保交给Jackson的是完整外层JSON。

5. 修复以下现场场景:
   - 天眼规则129截取结果仍带有alarm前缀;
   - 微步设备同时绑定固定偏移规则和Threatbook/TDP JSON规则;
   - 不适用的固定偏移规则抢先产生JSON解析异常;
   - Syslog头加JSON正文不能继续匹配后续适用规则。

三、增加现场采集工具及运维说明

新增upgrade-doc/v5:
   - README.md:升级、配置、生命周期和故障排查说明;
   - capture-device-raw-logs.sh:一键创建采集会话、等待完成并自动落盘。

脚本输出:
   - result.json:完整采集结果;
   - samples.jsonl:每行一条样本及处理状态;
   - raw/*.log:每条未截断原始报文;
   - summary.txt:采集数量、异常数量和替换数量;
   - latest.json:中断或超时时的最后有效快照。

四、兼容性与升级范围

- 兼容JDK 8;
- 无需新增或修改达梦数据库表;
- 无需创建新的Kafka Topic;
- 需要滚动升级全部syslog-consumer-rule实例;
- 三个实例必须连接相同的Redis;
- 本次提交不包含syslog-serve的高内存、WAL或JVM参数调整。
This commit is contained in:
王绍宏
2026-09-14 23:11:45 +08:00
parent 7e6b2fc4dc
commit 1c07acc01d
17 changed files with 3131 additions and 43 deletions
@@ -23,9 +23,23 @@ syslog-serve/docker-compose/docker-compose-serve-prod.yaml
- SYSLOG_MESSAGE_MAX_BYTES=6291456
- SYSLOG_LARGE_MESSAGE_THRESHOLD_BYTES=131072
# WAL 和 Kafka 投递最多各占用 256 MiB 在途内存。
- SYSLOG_WAL_MAX_PENDING_BYTES=268435456
- SYSLOG_DISPATCH_MAX_INFLIGHT_BYTES=268435456
# 200KiB/条容量基线;WAL 高水位约在 14,000 条、2.7~2.8GiB 对齐触发。
- SYSLOG_WAL_QUEUE_CAPACITY=20000
- SYSLOG_WAL_MAX_PENDING_BYTES=4294967296
- SYSLOG_WAL_HIGH_WATERMARK_PERCENT=70
- SYSLOG_WAL_LOW_WATERMARK_PERCENT=40
- SYSLOG_WAL_FLUSH_RECORDS=200
- SYSLOG_WAL_FLUSH_INTERVAL_MS=100
- SYSLOG_WAL_SEGMENT_MAX_BYTES=4294967296
# Kafka 投递限制正文最多 1GiB 在途,并增加加密后的 Producer 缓冲。
- SYSLOG_DISPATCH_WORKER_THREADS=8
- SYSLOG_DISPATCH_MAX_INFLIGHT=5000
- SYSLOG_DISPATCH_MAX_INFLIGHT_BYTES=1073741824
- SPRING_KAFKA_PRODUCER_PROPERTIES_BUFFER_MEMORY=536870912
# Dockerfile 不展开 JAVA_OPTS,使用 JVM 自动读取的 JAVA_TOOL_OPTIONS。
- JAVA_TOOL_OPTIONS=-Xms8g -Xmx24g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:MaxDirectMemorySize=8g -XX:+ExitOnOutOfMemoryError
# 大日志 Kafka Topic 及 Producer 请求上限。
- KAFKA_PRODUCER_LARGE_TOPIC=agent-01-syslog-topic.large
@@ -37,7 +51,9 @@ syslog-serve/docker-compose/docker-compose-serve-prod.yaml
- `SYSLOG_MAX_FRAME_LENGTH`:TCP 单帧最大长度;
- `SYSLOG_MESSAGE_MAX_BYTES`:所有来源的单条日志硬上限,超过后拒绝;
- `SYSLOG_LARGE_MESSAGE_THRESHOLD_BYTES`:达到该值后走大日志 Topic;
- `SYSLOG_WAL_MAX_PENDING_BYTES`、`SYSLOG_DISPATCH_MAX_INFLIGHT_BYTES`:限制 JVM 在途数据量。
- `SYSLOG_WAL_QUEUE_CAPACITY`、`SYSLOG_WAL_MAX_PENDING_BYTES`:共同限制 WAL 接入条数和原文字节量;
- `SYSLOG_DISPATCH_MAX_INFLIGHT_BYTES`:限制 Kafka Dispatcher 在途正文总量;
- `JAVA_TOOL_OPTIONS`:设置实际 JVM 堆和直接内存上限,不能替换成当前 Dockerfile 不会展开的 `JAVA_OPTS`。
`SYSLOG_MAX_FRAME_LENGTH` 必须大于或等于 `SYSLOG_MESSAGE_MAX_BYTES`。如果设备发送 GBK,应将
`SYSLOG_CHARSET` 改为 `GBK`。
@@ -0,0 +1,45 @@
# 查询 Kafka 当前积压
在 Kafka 的 `bin` 目录执行。查询操作只读,不会修改消息或消费组 offset。
## 查询主日志消费积压
```bash
./kafka-consumer-groups.sh \
--bootstrap-server 10.141.121.35:9092 \
--group agent-01-syslog-group-dm \
--describe
```
如果现场主消费组仍使用旧名称,将组名改为 `agent-01-syslog-group`。
## 查询所有消费组的总积压
```bash
./kafka-consumer-groups.sh \
--bootstrap-server 10.141.121.35:9092 \
--all-groups \
--describe \
| awk '$6 ~ /^[0-9]+$/ {lag[$1]+=$6} END {for (g in lag) print lag[g], g}' \
| sort -nr
```
输出第一列是总积压条数,第二列是消费组名称,积压最多的排在最前面。
需要查看某个消费组各分区的明细时执行:
```bash
./kafka-consumer-groups.sh \
--bootstrap-server 10.141.121.35:9092 \
--group 消费组名称 \
--describe
```
重点查看:
- `CURRENT-OFFSET`:已经消费并提交的位置;
- `LOG-END-OFFSET`:Kafka 当前最新位置;
- `LAG`:该分区积压条数。
间隔一分钟查询两次:`LAG` 下降表示正在追赶;`LAG` 上升表示消费速度低于数据进入速度;
`CURRENT-OFFSET` 不变且 `LAG` 大于零,表示消费没有进展。
@@ -0,0 +1,61 @@
# 查询主动丢弃的非告警数据
积压采样只丢弃部分普通日志和非标消息,告警全部保留。审计数据保存在达梦表
`syslog_backlog_retention_hourly`。
## 查看当前运行累计值
```bash
docker exec syslog-consumer-rule-dm sh -c \
'curl -fsS http://127.0.0.1:8289/xdrrule/api/syslog/backlog-sampling/status'
```
主要字段:
- `normal_rows_dropped_total`:本次应用启动后主动丢弃的普通日志行数;
- `non_normal_rows_dropped_total`:本次应用启动后主动丢弃的非标消息行数;
- `raw_dropped_total`:本次应用启动后没有写入 InfluxDB 的原文条数。
## 查询指定一天的丢弃总数
以下查询统计上海时间 `2026-09-13` 一整天:
```sql
SELECT
NVL(SUM(normal_rows_dropped), 0) AS normal_dropped,
NVL(SUM(non_normal_rows_dropped), 0) AS non_normal_dropped,
NVL(SUM(normal_rows_dropped), 0)
+ NVL(SUM(non_normal_rows_dropped), 0) AS non_alarm_rows_dropped,
NVL(SUM(raw_dropped), 0) AS raw_dropped,
NVL(SUM(alarm_rows_total), 0) AS alarm_total,
NVL(SUM(alarm_rows_saved), 0) AS alarm_saved
FROM syslog_backlog_retention_hourly
WHERE source_hour >= TIMESTAMP '2026-09-13 00:00:00'
AND source_hour < TIMESTAMP '2026-09-14 00:00:00';
```
## 按小时统计
```sql
SELECT
source_hour,
SUM(normal_rows_dropped) AS normal_dropped,
SUM(non_normal_rows_dropped) AS non_normal_dropped,
SUM(normal_rows_dropped) + SUM(non_normal_rows_dropped)
AS non_alarm_rows_dropped,
SUM(raw_dropped) AS raw_dropped,
SUM(alarm_rows_total) AS alarm_total,
SUM(alarm_rows_saved) AS alarm_saved,
MAX(max_lag_ms) AS max_lag_ms
FROM syslog_backlog_retention_hourly
WHERE source_hour >= TIMESTAMP '2026-09-13 00:00:00'
AND source_hour < TIMESTAMP '2026-09-14 00:00:00'
GROUP BY source_hour
ORDER BY source_hour;
```
`non_alarm_rows_dropped` 是普通和非标两类达梦业务行的合计。`raw_dropped` 是原始消息口径,
可能与这两类记录重叠,因此不要再把 `raw_dropped` 加到 `non_alarm_rows_dropped` 中。
正常情况下 `alarm_total` 应等于 `alarm_saved`。查询结果为零或没有小时记录,表示该时间段没有触发
积压采样,或者采样审计尚未成功写入。