fix(syslog-consumer-rule): 修复现场日志解析并补充升级工具

- 支持设备头字段值包含空格,避免 WAF 等名称被误拆为无效键值对
- 将空字符串 cropper offset 按未配置处理,兼容历史标准化规则
- 增加设备头解析和空 offset 回归测试
- 增加统计物化 Topic 创建脚本
- 增加容器内 JAR 热替换脚本及操作说明
This commit is contained in:
王绍宏
2026-09-12 11:40:57 +08:00
parent c1472b9a95
commit 3b6c9e4714
10 changed files with 321 additions and 66 deletions
@@ -71,46 +71,7 @@ public class KeyValueParser {
* @throws IllegalArgumentException 如果格式不正确
*/
public static Map<String, String> parseKeyValuePairs(String text) {
if (text == null || text.trim().isEmpty()) {
throw new IllegalArgumentException("Input text cannot be null or empty");
}
// 查找第一个 [ 和对应的 ]
int start = text.indexOf('[');
int end = text.indexOf(']', start + 1);
if (start == -1 || end == -1) {
throw new IllegalArgumentException("No valid [] pair found in text");
}
// 提取 [] 内的内容
String content = text.substring(start + 1, end).trim();
if (content.isEmpty()) {
return new HashMap<>(); // 返回空Map
}
// 分割键值对
String[] pairs = content.split("\\s+");
Map<String, String> result = new HashMap<>();
for (String pair : pairs) {
// 分割键和值
int equalSign = pair.indexOf('=');
if (equalSign == -1) {
throw new IllegalArgumentException("Invalid key-value pair format: " + pair);
}
String key = pair.substring(0, equalSign).trim();
String value = pair.substring(equalSign + 1).trim();
if (key.isEmpty()) {
throw new IllegalArgumentException("Key cannot be empty in pair: " + pair);
}
result.put(key, value);
}
return result;
return SyslogParser.parseKeyValuePairs(text);
}
public static void main(String[] args) {
String input = "[name=非工作时间访问 riskLevel=中 riskMain=账号 appName=蜜罐系统-企信外网 riskDesc=账号在2025-04-27,通过47.94.211.49客户端IP在非常用访问时间2025-04-27 15:38:54,进行了业务访问]";
@@ -132,4 +93,4 @@ public class KeyValueParser {
System.out.println("风险等级: " + result1.get("riskLevel"));
System.out.println("风险描述: " + result1.get("riskDesc"));
}
}
}
@@ -42,6 +42,13 @@ public class SyslogParser {
"(?<content>.+)$"
);
/**
* syslog-serve 的设备头使用空格分隔 key=value,但历史格式没有对 value 中的空格做转义。
* 因此不能直接按空白切分,只能把“空白 + 合法 key=”识别为下一个字段的开始。
*/
private static final Pattern DEVICE_HEADER_KEY_PATTERN =
Pattern.compile("(?<!\\S)([A-Za-z_][A-Za-z0-9_.-]*)=");
// RFC 3164 时间戳格式
private static final DateTimeFormatter RFC3164_FORMATTER =
DateTimeFormatter.ofPattern("MMM dd HH:mm:ss");
@@ -328,28 +335,31 @@ public class SyslogParser {
return new HashMap<>(); // 返回空Map
}
// 分割键值对
String[] pairs = content.split("\\s+");
Map<String, String> result = new HashMap<>();
Map<String, String> result = new LinkedHashMap<>();
Matcher matcher = DEVICE_HEADER_KEY_PATTERN.matcher(content);
String previousKey = null;
int previousValueStart = -1;
for (String pair : pairs) {
// 分割键和值
int equalSign = pair.indexOf('=');
if (equalSign == -1) {
throw new IllegalArgumentException("Invalid key-value pair format: " + pair);
while (matcher.find()) {
if (previousKey == null) {
String invalidPrefix = content.substring(0, matcher.start()).trim();
if (!invalidPrefix.isEmpty()) {
throw new IllegalArgumentException("Invalid key-value pair format: " + invalidPrefix);
}
} else {
String value = content.substring(previousValueStart, matcher.start()).trim();
result.put(previousKey, value);
}
String key = pair.substring(0, equalSign).trim();
String value = pair.substring(equalSign + 1).trim();
if (key.isEmpty()) {
throw new IllegalArgumentException("Key cannot be empty in pair: " + pair);
}
result.put(key, value);
previousKey = matcher.group(1);
previousValueStart = matcher.end();
}
if (previousKey == null) {
throw new IllegalArgumentException("Invalid key-value pair format: " + content);
}
result.put(previousKey, content.substring(previousValueStart).trim());
return result;
}
}