Files
ai-security-xdr/haobang-security-dm/syslog-consumer-rule/src/main/java/com/common/util/SyslogParser.java
T

356 lines
12 KiB
Java
Raw Normal View History

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;
}
}