package ingest import ( "fmt" "regexp" "strconv" "strings" ) var rePri = regexp.MustCompile(`^<(\d{1,3})>`) type ParsedSyslog struct { Priority int Hostname string Tag string Message string RawLine string } func parseSyslogPayload(payload []byte) ParsedSyslog { line := strings.TrimSpace(string(payload)) p := ParsedSyslog{RawLine: line, Message: line} if line == "" { return p } rest := line if m := rePri.FindStringSubmatch(line); len(m) == 2 { if pri, err := strconv.Atoi(m[1]); err == nil { p.Priority = pri } rest = line[len(m[0]):] } rest = strings.TrimSpace(rest) fields := strings.SplitN(rest, " ", 6) if len(fields) >= 2 && len(fields[0]) == 1 && fields[0][0] >= '1' && fields[0][0] <= '9' { if len(fields) >= 4 { p.Hostname = fields[2] if len(fields) >= 6 { p.Message = fields[5] } else if len(fields) == 5 { p.Message = fields[4] } } return p } tokens := strings.SplitN(rest, " ", 3) if len(tokens) >= 2 { if len(tokens) >= 3 && isMonthAbbr(tokens[0]) { p.Hostname = tokens[2] if idx := strings.Index(rest, ": "); idx > 0 { p.Message = strings.TrimSpace(rest[idx+2:]) } } else { p.Hostname = tokens[1] if len(tokens) >= 3 { tagMsg := tokens[2] if idx := strings.Index(tagMsg, ": "); idx > 0 { p.Tag = tagMsg[:idx] p.Message = strings.TrimSpace(tagMsg[idx+2:]) } else { p.Message = tagMsg } } } } return p } func isMonthAbbr(s string) bool { if len(s) < 3 { return false } mons := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} for _, m := range mons { if strings.HasPrefix(s, m) { return true } } return false } func syslogPriorityToSeverity(pri int) string { sev := pri % 8 switch sev { case 0, 1, 2: return "critical" case 3: return "major" case 4: return "warning" default: return "info" } } func formatSyslogSummary(p ParsedSyslog) string { host := p.Hostname if host == "" { host = "unknown-host" } return fmt.Sprintf("%s: %s", host, truncate(p.Message, 512)) } func truncate(s string, n int) string { if len(s) <= n { return s } return s[:n] + "..." }