feat(syslog): 优化WAL配置并在入站阶段丢弃无效设备日志
- 默认在进入WAL前丢弃未注册、重复或未分配到当前探针的Syslog日志 - 使用后台设备内存快照,避免Netty网络线程访问Redis或达梦 - 增加Terminal审计开关,允许恢复原有逐条审计文件模式 - 历史WAL中的无效设备记录直接推进checkpoint - 增加无效设备丢弃、快照状态及刷新失败监控指标 - 生产WAL队列调整为20000条 - WAL待写预算和分段大小调整为2GiB - 同步生产配置、Docker Compose及升级文档 - 增加无效设备前置过滤和Terminal开关测试 chore(syslog): 统一生产环境 WAL 挂载目录 - 将生产环境 WAL 默认目录调整为 /app/logs/syslog-wal - Docker Compose 显式设置 SYSLOG_WAL_DIRECTORY - WAL 与应用日志共用 /app/logs 宿主机挂载 - 移除旧的 /data/syslog-archive 独立挂载 - 宿主机 WAL 路径统一为 /home/syslog/logs/syslog-wal
This commit is contained in:
@@ -23,19 +23,21 @@ syslog-serve/docker-compose/docker-compose-serve-prod.yaml
|
||||
- SYSLOG_MESSAGE_MAX_BYTES=6291456
|
||||
- SYSLOG_LARGE_MESSAGE_THRESHOLD_BYTES=131072
|
||||
|
||||
# 200KiB/条容量基线;WAL 高水位约在 14,000 条、2.7~2.8GiB 对齐触发。
|
||||
# 200KiB/条容量基线;2GiB 字节预算在 70% 时约 7,300 条、1.4GiB 触发背压。
|
||||
- SYSLOG_WAL_QUEUE_CAPACITY=20000
|
||||
- SYSLOG_WAL_MAX_PENDING_BYTES=4294967296
|
||||
- SYSLOG_WAL_MAX_PENDING_BYTES=2147483648
|
||||
- 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
|
||||
- SYSLOG_WAL_SEGMENT_MAX_BYTES=2147483648
|
||||
|
||||
# Kafka 投递限制正文最多 1GiB 在途,并增加加密后的 Producer 缓冲。
|
||||
- SYSLOG_DISPATCH_WORKER_THREADS=8
|
||||
- SYSLOG_DISPATCH_MAX_INFLIGHT=5000
|
||||
- SYSLOG_DISPATCH_MAX_INFLIGHT_BYTES=1073741824
|
||||
# 性能优先:无效设备正文不进 WAL,仅按设备缓存周期打印来源 IP。
|
||||
- SYSLOG_DISPATCH_TERMINAL_RECORD_ENABLED=false
|
||||
- SPRING_KAFKA_PRODUCER_PROPERTIES_BUFFER_MEMORY=536870912
|
||||
|
||||
# Dockerfile 不展开 JAVA_OPTS,使用 JVM 自动读取的 JAVA_TOOL_OPTIONS。
|
||||
@@ -53,10 +55,12 @@ syslog-serve/docker-compose/docker-compose-serve-prod.yaml
|
||||
- `SYSLOG_LARGE_MESSAGE_THRESHOLD_BYTES`:达到该值后走大日志 Topic;
|
||||
- `SYSLOG_WAL_QUEUE_CAPACITY`、`SYSLOG_WAL_MAX_PENDING_BYTES`:共同限制 WAL 接入条数和原文字节量;
|
||||
- `SYSLOG_DISPATCH_MAX_INFLIGHT_BYTES`:限制 Kafka Dispatcher 在途正文总量;
|
||||
- `SYSLOG_DISPATCH_TERMINAL_RECORD_ENABLED`:`false` 在 WAL 前丢弃 Syslog 无效设备正文并按缓存周期打印 IP;`true` 写 WAL 并逐条保存 Terminal 审计文件;
|
||||
- `JAVA_TOOL_OPTIONS`:设置实际 JVM 堆和直接内存上限,不能替换成当前 Dockerfile 不会展开的 `JAVA_OPTS`。
|
||||
|
||||
`SYSLOG_MAX_FRAME_LENGTH` 必须大于或等于 `SYSLOG_MESSAGE_MAX_BYTES`。如果设备发送 GBK,应将
|
||||
`SYSLOG_CHARSET` 改为 `GBK`。
|
||||
`SYSLOG_CHARSET` 改为 `GBK`。无效设备判断只读取后台定时刷新的内存快照,不会在 Netty 网络线程访问 Redis 或达梦;
|
||||
首次快照暂未就绪或刷新失败时,消息保守进入 WAL。
|
||||
|
||||
## 2. syslog-consumer-rule
|
||||
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
# 天眼/微步 JSON 解析异常排查入口
|
||||
|
||||
## 目标
|
||||
|
||||
定位一条日志为什么进入 `syslog_non_normal_message` 并标记为“JSON解析异常”。
|
||||
|
||||
只分析当前设备绑定的规则:
|
||||
|
||||
- 天眼设备重点看规则 `129 / skyeye_alarm_log`;
|
||||
- 微步设备看该设备实际绑定的规则,现场已见 `150 / weibu_TDP_alarmlog_JSON`、
|
||||
`158 / weibujsomv2`、`159 / weibujson`;
|
||||
- `Threatbook[pid]: {...}` 属于微步/TDP 报文,不要拿规则129分析;
|
||||
- 设备与规则的绑定以 `dm_normalize_rule_device` 为准,不要模糊查询全表后把所有规则都拿来匹配。
|
||||
|
||||
## 第一步:取得两个输入
|
||||
|
||||
### 1. 解析前的完整原文
|
||||
|
||||
不要使用达梦非标表里带有以下标记的内容判断 JSON 是否合法:
|
||||
|
||||
```text
|
||||
[HB_XDR_TRUNCATED original_utf8_bytes=...]
|
||||
```
|
||||
|
||||
该内容已经在规则计算完成后按 2KB 限制截断,不是 Jackson 实际收到的报文。
|
||||
|
||||
使用 v5 脚本按设备采集解析前原文:
|
||||
|
||||
```bash
|
||||
chmod 700 upgrade-doc/v5/capture-device-raw-logs.sh
|
||||
|
||||
upgrade-doc/v5/capture-device-raw-logs.sh \
|
||||
<设备ID> 120 20 \
|
||||
http://127.0.0.1:8289/xdrrule \
|
||||
/data/haobang/raw-log-captures
|
||||
```
|
||||
|
||||
调试需要生成目录中的 `result.json` 和 `raw/*.log`。如果接口不可用,再从原始 InfluxDB 或
|
||||
`syslog-serve` 接收日志按 `device_id + receive_time/event_id` 导出完整 `message`。
|
||||
|
||||
### 2. 当前设备绑定的完整规则
|
||||
|
||||
`rule_content` 最长约一万多字符,在达梦 `disql` 中设置 20000 字符并 spool 到文件:
|
||||
|
||||
```sql
|
||||
SET LONG 20000
|
||||
SET LONGCHUNKSIZE 20000
|
||||
SET LINESIZE 32767
|
||||
SET PAGESIZE 0
|
||||
SET TRIMSPOOL ON
|
||||
SPOOL /tmp/device-normalize-rules.txt
|
||||
|
||||
SELECT b.device_id,
|
||||
r.id AS rule_id,
|
||||
r.name,
|
||||
r.display_name,
|
||||
r.data_type,
|
||||
DBMS_LOB.GETLENGTH(r.rule_content) AS rule_content_length,
|
||||
DBMS_LOB.SUBSTR(r.rule_content, 20000, 1) AS rule_content
|
||||
FROM dm_normalize_rule_device b
|
||||
JOIN dm_normalize_rule r ON r.id = b.normalize_rule_id
|
||||
WHERE b.device_id = <设备ID>
|
||||
AND r.del_flag = '0'
|
||||
ORDER BY r.id;
|
||||
|
||||
SPOOL OFF
|
||||
```
|
||||
|
||||
若长度超过 20000,再取下一段:
|
||||
|
||||
```sql
|
||||
DBMS_LOB.SUBSTR(rule_content, 20000, 20001)
|
||||
```
|
||||
|
||||
## 第二步:在代码中拿到 Jackson 的真实输入
|
||||
|
||||
当前主链路:
|
||||
|
||||
```text
|
||||
SysLogProcessor.prepareNormalization()
|
||||
-> LogNormalProcessor.prepare()
|
||||
-> LogNormalProcessor.collectCandidates()
|
||||
-> CompiledRule.isApplicableTo()
|
||||
-> CompiledRule.parse()
|
||||
-> logNormalData.ParserMessageJsonType()
|
||||
-> JsonParser.extractJsonFromLogMessage()(必要时)
|
||||
-> NestedJsonParserUtil.safeParseJson()
|
||||
-> Jackson
|
||||
```
|
||||
|
||||
建议断点:
|
||||
|
||||
| 位置 | 观察内容 |
|
||||
| --- | --- |
|
||||
| `LogNormalProcessor.java:516` | 当前设备加载了哪些规则,每条规则是否进入候选解析 |
|
||||
| `CompiledRule.java:72` | 当前 `ruleId`、原始 `message` 和 cropper 配置 |
|
||||
| `CompiledRule.java:94` | cropper 结果是否需要再次截取 `{...}` |
|
||||
| `CompiledRule.java:99` | 变量 `cropped`,即 Compiled 链路交给 Jackson 的完整字符串 |
|
||||
| `CompiledRule.java:124` | `head_key` 是否真的能标识这类报文 |
|
||||
| `NestedJsonParserUtil.java:41` | Jackson 的首次解析结果及准确行列错误 |
|
||||
| `LogNormalProcessor.java:1014` | 关闭 Compiled 优化时的 legacy 入口,观察 `strJson` |
|
||||
|
||||
相关源码:
|
||||
|
||||
```text
|
||||
syslog-consumer-rule/src/main/java/com/Modules/NormalData/LogNormalProcessor.java
|
||||
syslog-consumer-rule/src/main/java/com/Modules/NormalData/compiled/CompiledRule.java
|
||||
syslog-consumer-rule/src/main/java/com/Modules/NormalData/logNormalData.java
|
||||
syslog-consumer-rule/src/main/java/com/common/util/JsonParser.java
|
||||
syslog-consumer-rule/src/main/java/com/common/util/NestedJsonParserUtil.java
|
||||
syslog-consumer-rule/src/main/java/com/common/service/RawLaneFieldLimiter.java
|
||||
```
|
||||
|
||||
对每条失败样本保存以下信息即可:
|
||||
|
||||
```text
|
||||
deviceId
|
||||
ruleId / ruleName
|
||||
完整原文 message
|
||||
cropper_params
|
||||
截取结果 cropped(或 legacy 的 strJson)
|
||||
Jackson 原始错误及行列号
|
||||
```
|
||||
|
||||
不要为了调试在生产 `INFO` 日志中持续打印完整原文;使用采集文件、断点或只针对一条脱敏样本的测试。
|
||||
|
||||
## 第三步:根据真实输入分类
|
||||
|
||||
| 现象 | 结论 | 处理位置 |
|
||||
| --- | --- | --- |
|
||||
| `cropped` 仍含 Syslog 头或天眼分隔前缀 | 截取未完成 | `cropper_params` 或 JSON 对象边界截取 |
|
||||
| `cropped` 缺少开头 `{`、结尾 `}` 或被截在中间 | cropper 范围错误 | 优先修当前规则 cropper |
|
||||
| 报文不含规则的格式特征,却进入该规则 | 候选规则归属错误 | `isApplicableTo()`/规则特征配置 |
|
||||
| `cropped` 是完整外层 JSON,但 `req_body` 等字段内有未转义双引号、反斜杠或真实换行 | 设备报文/嵌套字段转义错误 | 优先让设备修;必要时做设备/规则专用预处理 |
|
||||
| 完整原文在进入规则前已经不完整 | 上游提前截断 | 查 `syslog-serve`、Kafka 和原始存储链路 |
|
||||
| 只有达梦非标字段被截断 | 与解析失败无关 | 不修改解析代码 |
|
||||
|
||||
`JsonParser.extractJsonFromLogMessage()` 的默认行为是取第一个 `{` 到最后一个 `}`。配置了 cropper 时,
|
||||
先执行 `ParserMessageJsonType()`;如果结果不是完整 `{...}`,当前 Compiled JSON 路径才会再次执行对象边界截取。
|
||||
|
||||
## 天眼与微步的现有复现入口
|
||||
|
||||
测试文件:
|
||||
|
||||
```text
|
||||
syslog-consumer-rule/src/test/java/com/Modules/NormalData/LogNormalProcessorRuleSelectionTest.java
|
||||
```
|
||||
|
||||
直接参考两个用例:
|
||||
|
||||
- `threatbookUsesTdpRuleAfterFixedOffsetHoneypotRulesAreRejected`:微步 Threatbook 报文应选择规则150,
|
||||
不能被158/159的固定偏移规则提前判为非标;
|
||||
- `skyeyeCropperPrefixIsNarrowedToOuterJsonBeforeJackson`:天眼规则129截取后,Jackson 输入必须是完整外层 JSON。
|
||||
|
||||
把现场完整原文脱敏后增加为 fixture,并使用现场规则的真实 `cropper_params`。至少断言:
|
||||
|
||||
```text
|
||||
选中的 normalize_rule_id
|
||||
一个关键字段的映射值
|
||||
未生成“JSON解析异常”非标记录
|
||||
```
|
||||
|
||||
使用 JDK 8 执行:
|
||||
|
||||
```bash
|
||||
mvn -Dtest=LogNormalProcessorRuleSelectionTest test
|
||||
```
|
||||
|
||||
## 必看的历史修改
|
||||
|
||||
```bash
|
||||
git show a70707c72969c7979aef9c0221fd55360c365227
|
||||
git show a70707c^:syslog-consumer-rule/src/main/java/com/common/util/NestedJsonParserUtil.java
|
||||
git show c6e3505
|
||||
```
|
||||
|
||||
- `a70707c...` 删除了首次失败后的全局坏 JSON 猜测修复和第二次 Jackson 解析;
|
||||
- 旧版会依次尝试修复未转义引号、单引号、反斜杠、缺失引号、数值、布尔和 null;
|
||||
- `c6e3505` 修改了候选规则失败处理及 JSON 对象边界截取,避免前一条不适用规则失败后立即转非标。
|
||||
|
||||
判断问题是否由删除旧修复引起,必须对同一份完整原文记录四项结果:
|
||||
|
||||
```text
|
||||
当前首次解析结果
|
||||
旧版首次解析结果
|
||||
旧版修复后的字符串(并指出实际生效步骤)
|
||||
旧版第二次解析结果
|
||||
```
|
||||
|
||||
没有这四项证据,不要恢复旧版全局修复。全局恢复会给每条坏 JSON 增加多次扫描、正则替换、字符串分配和
|
||||
第二次 Jackson 解析。若确实需要兼容设备缺陷,优先限定到指定设备及指定规则,最后才考虑通用解析器。
|
||||
@@ -1,5 +1,8 @@
|
||||
# v5 现场设备原始日志采样
|
||||
|
||||
天眼/微步 JSON 异常的代码入口和现场开发排查顺序见
|
||||
[`02-json-format-error-debug-guide.md`](./02-json-format-error-debug-guide.md)。
|
||||
|
||||
## 功能说明
|
||||
|
||||
v5 为 `syslog-consumer-rule` 增加按设备短时采集原始日志的诊断接口,并提供自动落盘脚本。
|
||||
|
||||
Reference in New Issue
Block a user