package parser import ( "fmt" "regexp" "strconv" "time" ) // ErrorEntry holds a parsed nginx error log entry. type ErrorEntry struct { Time time.Time Level string // debug/info/notice/warn/error/crit/alert/emerg PID int TID int ConnID int // 0 if not present Message string } // errorLogRe matches nginx error log format. // Groups: time, level, pid, tid, connID (optional), message var errorLogRe = regexp.MustCompile( `^(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (\d+)#(\d+): (?:\*(\d+) )?(.+)$`, ) const errorTimeLayout = "2006/01/02 15:04:05" // ParseErrorLine parses a single nginx error log line. // Returns an error for malformed lines. func ParseErrorLine(line string) (ErrorEntry, error) { m := errorLogRe.FindStringSubmatch(line) if m == nil { return ErrorEntry{}, fmt.Errorf("error: no match: %q", line) } t, err := time.ParseInLocation(errorTimeLayout, m[1], time.UTC) if err != nil { return ErrorEntry{}, fmt.Errorf("error: parse time %q: %w", m[1], err) } pid, err := strconv.Atoi(m[3]) if err != nil { return ErrorEntry{}, fmt.Errorf("error: parse pid %q: %w", m[3], err) } tid, err := strconv.Atoi(m[4]) if err != nil { return ErrorEntry{}, fmt.Errorf("error: parse tid %q: %w", m[4], err) } var connID int if m[5] != "" { connID, err = strconv.Atoi(m[5]) if err != nil { return ErrorEntry{}, fmt.Errorf("error: parse connid %q: %w", m[5], err) } } return ErrorEntry{ Time: t, Level: m[2], PID: pid, TID: tid, ConnID: connID, Message: m[6], }, nil }