Files
ai-security-xdr/haobang-security-dm/upgrade-doc/v5/02-json-format-error-debug-guide.md
T
王绍宏 faa9ffe2d9 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
2026-09-15 22:10:46 +08:00

192 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 天眼/微步 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 解析。若确实需要兼容设备缺陷,优先限定到指定设备及指定规则,最后才考虑通用解析器。