package inspurlegacy import ( "regexp" "strconv" "strings" "time" "git.mchus.pro/mchus/logpile/internal/models" ) // amiSyslogRe matches " host proc[pid]: message". var amiSyslogRe = regexp.MustCompile(`^<(\d+)>\s*(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\S*)\s+(\S+)\s+(\S+?):\s*(.*)$`) // ParseAMISyslog parses a flat AMI BMC severity log (err.log, warning.log, ...). func ParseAMISyslog(content []byte, source string) []models.Event { var events []models.Event lineNo := 0 for _, line := range strings.Split(string(content), "\n") { lineNo++ line = strings.TrimSpace(line) if line == "" { continue } m := amiSyslogRe.FindStringSubmatch(line) if m == nil { continue } pri, err := strconv.Atoi(m[1]) if err != nil { continue } ts, err := parseSyslogTime(m[2]) if err != nil { continue } proc := m[4] msg := strings.TrimSpace(m[5]) // AMI BMC pre-NTP lines carry a fabricated 1970 wall clock; without a // trustworthy boot epoch they cannot become dated events. if bogusYear(ts) { continue } severity := syslogSeverity(pri, source) if isBenignSyslogMessage(msg) { severity = models.SeverityInfo } events = append(events, models.Event{ ID: strings.TrimSuffix(source, ".log") + "_" + itoa(lineNo), Timestamp: ts, Source: "syslog/" + strings.TrimSuffix(source, ".log"), SensorType: "syslog", SensorName: proc, Severity: severity, Description: msg, RawData: line, }) } return events } func parseSyslogTime(s string) (time.Time, error) { if t, err := time.Parse(time.RFC3339, s); err == nil { return t, nil } return time.Parse("2006-01-02T15:04:05.999999-07:00", s) } // benignSyslogSubstrings are driver banners and inventory-provisioning notices // that AMI routes to alert.log/warning.log despite describing no fault. The PRI // value is not trustworthy for this observed set. var benignSyslogSubstrings = []string{ "helper module driver version", "copyright (c)", "changed from null to", "sn changed from", "color depth is", } func isBenignSyslogMessage(msg string) bool { low := strings.ToLower(msg) for _, s := range benignSyslogSubstrings { if strings.Contains(low, s) { return true } } return false } // syslogSeverity derives severity from the RFC 5424 PRI low three bits, falling // back to the source filename. func syslogSeverity(pri int, source string) models.Severity { switch pri & 7 { case 0, 1, 2: return models.SeverityCritical case 3, 4: return models.SeverityWarning case 5, 6, 7: return models.SeverityInfo } return severityFromSource(source) } func severityFromSource(source string) models.Severity { low := strings.ToLower(source) switch { case strings.Contains(low, "emerg"), strings.Contains(low, "alert"), strings.Contains(low, "crit"): return models.SeverityCritical case strings.Contains(low, "err"), strings.Contains(low, "warn"): return models.SeverityWarning default: return models.SeverityInfo } }