Files
ai-security-xdr/haobang-security-dm/syslog-consumer-rule/src/main/java/com/common/util/SyslogParser.java
T
nanChen a360895292 1、新增功能探针联动处置、心跳在线检测
2、syslog-consumer模块拆分 syslog-consumer-rule模块实现日志数据消费、解析、泛化入库。
2026-05-28 14:30:06 +08:00

356 lines
12 KiB
Java
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.
package com.common.util;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.common.entity.SyslogMessage;
import com.common.entity.RFC3164Message;
import com.common.entity.RFC5424Message;
import com.influx.SyslogToInfluxApp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.Locale;
public class SyslogParser {
private static final Logger logger = LoggerFactory.getLogger(SyslogToInfluxApp.class);
// RFC 5424 正则表达式
private static final Pattern RFC5424_PATTERN = Pattern.compile(
"^<(?<pri>\\d{1,3})>(?<version>\\d+) " +
"(?<timestamp>\\S+) " +
"(?<host>\\S+) " +
"(?<app>\\S+) " +
"(?<pid>\\S+) " +
"(?<msgid>\\S+) " +
"(?<sd>(?:-|\\[.*\\]))" +
"(?: (?<message>.+))?$"
);
// RFC 3164 正则表达式
private static final Pattern RFC3164_PATTERN = Pattern.compile(
"^<(?<pri>\\d{1,3})>" +
"(?<timestamp>[A-Z][a-z]{2}\\s+\\d{1,2}\\s+\\d{2}:\\d{2}:\\d{2}) " +
"(?<host>\\S+) " +
"(?<content>.+)$"
);
// RFC 3164 时间戳格式
private static final DateTimeFormatter RFC3164_FORMATTER =
DateTimeFormatter.ofPattern("MMM dd HH:mm:ss");
/**
* 解析 Syslog 消息,自动检测格式
*/
public static SyslogMessage parse(String logLine) {
if (logLine == null || logLine.trim().isEmpty()) {
throw new IllegalArgumentException("Log line cannot be null or empty");
}
// 尝试解析为 RFC 5424 格式
try {
RFC5424Message msg5424 = parseRFC5424(logLine);
if (msg5424 != null) {
return msg5424;
}
} catch (Exception e) {
// 继续尝试 RFC 3164
}
// 尝试解析为 RFC 3164 格式
try {
RFC3164Message msg3164 = parseRFC3164(logLine);
if (msg3164 != null) {
return msg3164;
}
} catch (Exception e) {
// 解析失败
}
// 解析非标数据格式,提取字符【{】自后的内容
try {
String content = extractJsonStringNew(logLine);
RFC5424Message msgClass =new RFC5424Message();
if (content != null) {
msgClass.setMessage(content.toString());
return msgClass;
}
} catch (Exception e) {
// 解析失败
}
throw new IllegalArgumentException("Unable to parse syslog message: " + logLine);
}
/**
* 解析 RFC 5424 格式
*/
public static RFC5424Message parseRFC5424(String logLine) {
Matcher matcher = RFC5424_PATTERN.matcher(logLine);
if (!matcher.find()) {
return null;
}
RFC5424Message message = new RFC5424Message();
// 解析优先级
int pri = Integer.parseInt(matcher.group("pri"));
message.setPriority(pri);
// 解析版本
message.setVersion(Integer.parseInt(matcher.group("version")));
// 解析时间戳 (RFC 3339 格式)
String timestampStr = matcher.group("timestamp");
try {
message.setTimestamp(OffsetDateTime.parse(timestampStr));
} catch (DateTimeParseException e) {
throw new IllegalArgumentException("Invalid RFC5424 timestamp: " + timestampStr, e);
}
// 解析其他字段
message.setHostname(matcher.group("host"));
message.setAppName(matcher.group("app"));
message.setProcId(matcher.group("pid"));
message.setMsgId(matcher.group("msgid"));
// 解析结构化数据
String sd = matcher.group("sd");
if (!"-".equals(sd)) {
message.setStructuredData(parseStructuredData(sd));
}
// 解析消息内容
String msg = matcher.group("message");
if (msg != null) {
message.setMessage(msg.trim());
}
return message;
}
/**
* 解析 RFC 3164 格式
*/
public static RFC3164Message parseRFC3164(String logLine) {
Matcher matcher = RFC3164_PATTERN.matcher(logLine);
if (!matcher.find()) {
return null;
}
RFC3164Message message = new RFC3164Message();
// 解析优先级
int pri = Integer.parseInt(matcher.group("pri"));
message.setPriority(pri);
// 解析时间戳 (需要特殊处理,因为没有年份)
String timestampStr = matcher.group("timestamp");
message.setTimestamp(parseRFC3164Timestamp(timestampStr));
// 解析主机名
message.setHostname(matcher.group("host"));
// 解析内容和标签
String content = matcher.group("content");
parseRFC3164Content(content, message);
return message;
}
/**
* 解析 RFC 3164 时间戳
*/
private static OffsetDateTime parseRFC3164Timestamp(String timestampStr) {
try {
// 添加当前年份,因为 RFC3164 时间戳不包含年份
String currentYear = String.valueOf(java.time.Year.now().getValue());
String fullTimestamp = currentYear + " " + timestampStr;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MMM dd HH:mm:ss", Locale.US);
LocalDateTime localDateTime = LocalDateTime.parse(fullTimestamp, formatter);
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MMM dd HH:mm:ss", Locale.US);
// 假设为系统默认时区
return localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(localDateTime));
} catch (DateTimeParseException e) {
throw new IllegalArgumentException("Invalid RFC3164 timestamp: " + timestampStr, e);
}
}
/**
* 解析 RFC 3164 内容部分
*/
private static void parseRFC3164Content(String content, RFC3164Message message) {
// 尝试匹配标签格式: app[pid]:
Pattern tagPattern = Pattern.compile("^(?<tag>\\S+?)(?:\\[(?<pid>\\d+)\\])?:\\s*(?<message>.*)$");
Matcher tagMatcher = tagPattern.matcher(content);
if (tagMatcher.find()) {
message.setTag(tagMatcher.group("tag"));
String pid = tagMatcher.group("pid");
if (pid != null) {
message.setProcId(pid);
}
message.setMessage(tagMatcher.group("message").trim());
} else {
// 如果没有标签格式,整个内容作为消息
message.setTag("");
message.setMessage(content.trim());
}
// 对于 RFC3164,appName 通常与 tag 相同
message.setAppName(message.getTag());
}
/**
* 解析结构化数据
*/
private static Map<String, Map<String, String>> parseStructuredData(String sd) {
Map<String, Map<String, String>> structuredData = new HashMap<>();
// 简单的结构化数据解析,实际应用可能需要更复杂的解析
Pattern sdPattern = Pattern.compile("\\[(?<id>[^\\]]+)\\]");
Pattern paramPattern = Pattern.compile("(?<key>[^=\\s]+)=\"(?<value>[^\"]*)\"");
Matcher sdMatcher = sdPattern.matcher(sd);
while (sdMatcher.find()) {
String sdElement = sdMatcher.group("id");
Map<String, String> params = new HashMap<>();
Matcher paramMatcher = paramPattern.matcher(sdElement);
while (paramMatcher.find()) {
params.put(paramMatcher.group("key"), paramMatcher.group("value"));
}
// 使用第一个参数作为键,或者生成一个键
String key = params.containsKey("sdId") ? params.get("sdId") :
"element" + structuredData.size();
structuredData.put(key, params);
}
return structuredData;
}
/**
* 提取 JSON 字符串(不解析)
*/
public static Optional<String> extractJsonString(String syslogMessage) {
try {
if (syslogMessage == null) return Optional.empty();
int jsonStart = syslogMessage.indexOf('{');
if (jsonStart == -1) return Optional.empty();
return Optional.of(syslogMessage.substring(jsonStart));
} catch (Exception e) {
return Optional.empty();
}
}
/**
* 提取 JSON 字符串(不解析)
*/
public static String extractJsonStringNew (String syslogMessage) {
try {
if (syslogMessage == null) return "";
int jsonStart = syslogMessage.indexOf('{');
if (jsonStart == -1) return "";
return syslogMessage.substring(jsonStart);
} catch (Exception e) {
return "";
}
}
/**
* 截取字符串中第一个 ']' 之后的所有内容
*
* @param text 原始字符串
* @return 第一个 ']' 之后的内容,如果没有 ']' 则返回空字符串
*/
public static String substringAfterFirstCloseBracket(String text) {
if (text == null) {
return "";
}
int closeBracketIndex = text.indexOf(']');
if (closeBracketIndex == -1) {
return ""; // 没有找到 ']',返回空字符串
}
// 返回 ']' 之后的内容(+1 是为了跳过 ']' 本身)
return text.substring(closeBracketIndex + 1);
}
/**
* 截取字符串中第一个指定字符之前的所有内容
*
* @param text 原始字符串
* @param delimiterChar 分隔字符
* @return 分隔字符之前的内容
*/
public static String substringBeforeFirstChar(String text, char delimiterChar) {
if (text == null) {
return "";
}
int delimiterIndex = text.indexOf(delimiterChar);
if (delimiterIndex == -1) {
return text;
}
return text.substring(0, delimiterIndex+1);
}
/**
* 从文本中提取第一个 [] 包裹的键值对字符串并转换为 Map
*
* @param text 包含键值对的原始文本
* @return 包含键值对的 Map
* @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;
}
}