diff --git a/file_monitor.go b/file_monitor.go index 8f016b8..85fc9a7 100644 --- a/file_monitor.go +++ b/file_monitor.go @@ -8,6 +8,7 @@ import ( "strings" "watch-tool/models" "watch-tool/parser" + "watch-tool/patterns" "github.com/hpcloud/tail" ) @@ -22,22 +23,28 @@ func NewFileMonitor(config ToolConfig, hostname string) *FileMonitor { var logParser parser.Parser if config.Format.Pattern != "" { - pattern, err := regexp.Compile(config.Format.Pattern) + compiledRegex, err := regexp.Compile(config.Format.Pattern) if err != nil { - slog.Error("invalid regex pattern", "tool", config.Name, "error", err) - logParser = &parser.DefaultParser{} + slog.Error("Invalid regex pattern in tool config", "tool", config.Name, "error", err) + logParser = parser.NewGenericParser(config.Name, hostname) } else { - logParser = &parser.RegexLogParser{ - Pattern: pattern, - Fields: config.Format.Fields, - Toolname: config.Name, + gp := parser.NewGenericParser(config.Name, hostname) + + customExtractor := patterns.CompiledExtractor{ + Name: "config_custom_pattern", + Pattern: compiledRegex, + Fields: config.Format.Fields, } + + gp.Extractors = append(gp.Extractors, customExtractor) + logParser = gp } } else { var err error logParser, err = parser.New(config.Name, "custom", hostname) if err != nil { - slog.Error("cannot get tool specific parser", "error", err) + slog.Error("Cannot get tool specific parser from factory", "error", err) + logParser = parser.NewGenericParser(config.Name, hostname) } } @@ -74,7 +81,7 @@ func (fm *FileMonitor) Start(ctx context.Context, out chan<- models.LogMessage) } if line.Err != nil { - slog.Error("error reading log file", "tool", fm.config.Name, "error", line.Err) + slog.Error("Error reading log file", "tool", fm.config.Name, "error", line.Err) continue } @@ -84,7 +91,11 @@ func (fm *FileMonitor) Start(ctx context.Context, out chan<- models.LogMessage) entry, err := fm.parser.Parse(line.Text) if err != nil { - slog.Error("error parsing log line", "error", err) + slog.Error("Error parsing log line", "tool", fm.config.Name, "error", err) + } else { + if entry.Tool == "" { + entry.Tool = fm.config.Name + } } select { diff --git a/parser/am_parser.go b/parser/am_parser.go deleted file mode 100644 index 0b8c813..0000000 --- a/parser/am_parser.go +++ /dev/null @@ -1,49 +0,0 @@ -package parser - -import ( - "log/slog" - "regexp" - "strings" - "time" - "watch-tool/helpers" - "watch-tool/models" -) - -var ( - amServicePattern = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)\s+(\w+)\s+(\d+)\s+---\s+\[\s*([^\]]*)\]\s+([\w\.]+)\s*:\s*(.*)$`) -) - -type AMParser struct{} - -func (a *AMParser) Parse(line string) (models.LogMessage, error) { - newEntry := models.LogMessage{ - Service: "access-manager", - } - syslogFields, logContent := helpers.ExtractSyslogHeader(line) - newEntry.Host = syslogFields.Hostname - - matches := amServicePattern.FindStringSubmatch(strings.TrimSpace(logContent)) - if len(matches) != 7 { - newEntry.Timestamp = time.Now() - newEntry.LogMessage = line - return newEntry, nil - } - - timestampStr := strings.Join(strings.Split(matches[1], " "), "T") - timestamp, err := helpers.ParseRFC3339WithOptionalZ(timestampStr) - if err != nil { - slog.Error("unable to parse time", "error", err) - return newEntry, err - } - baseInfo := models.AMBaseInfo{ - ProcessID: matches[3], - ThreadID: strings.TrimSpace(matches[4]), - LoggerName: matches[5], - } - newEntry.Timestamp = timestamp - newEntry.LogLevel = matches[2] - newEntry.LogMessage = matches[6] - newEntry.ServiceInformation = baseInfo - - return newEntry, nil -} diff --git a/parser/default_parser.go b/parser/default_parser.go deleted file mode 100644 index fcf8a80..0000000 --- a/parser/default_parser.go +++ /dev/null @@ -1,29 +0,0 @@ -package parser - -import ( - "strings" - "time" - "watch-tool/models" -) - -type DefaultParser struct { - Service string - Tool string - Hostname string -} - -func (d *DefaultParser) Parse(line string) (models.LogMessage, error) { - msg := models.LogMessage{ - LogLevel: "unknown", - LogMessage: strings.TrimSpace(line), - Raw: line, - Timestamp: time.Now(), - } - if d.Service != "" { - msg.Service = d.Service - } - if d.Tool != "" { - msg.Tool = d.Tool - } - return msg, nil -} diff --git a/parser/generic_parser.go b/parser/generic_parser.go index 58c94c9..af62eba 100644 --- a/parser/generic_parser.go +++ b/parser/generic_parser.go @@ -1,117 +1,3 @@ -// package parser - -// import ( -// "fmt" -// "strconv" -// "strings" -// "time" -// "watch-tool/models" -// "watch-tool/patterns" -// ) - -// type GenericParser struct { -// ServiceName string -// Hostname string -// Extractors []patterns.CompiledExtractor -// CommonExt []patterns.CompiledExtractor -// } - -// func NewGenericParser(serviceName, hostname string) *GenericParser { -// repo := patterns.GetInstance() -// return &GenericParser{ -// ServiceName: serviceName, -// Hostname: hostname, -// Extractors: repo.GetExtractors(serviceName), -// CommonExt: repo.GetExtractors("common"), -// } -// } - -// func (p *GenericParser) Parse(line string) (models.LogMessage, error) { -// entry := models.LogMessage{ -// Service: p.ServiceName, -// Host: p.Hostname, -// Timestamp: time.Now(), -// Raw: line, -// Fields: make(map[string]any), -// } - -// // 1. Common Extractors laufen lassen (z.B. Syslog Header entfernen/parsen) -// // Wir nutzen eine temporäre Variable für den Rest-String, falls Header entfernt werden soll -// currentLine := line - -// // Hinweis: Hier könnte man Syslog-Logik generisch einbauen. -// // Fürs Erste wenden wir Pattern einfach auf die Zeile an. - -// // 2. Service Extractors anwenden -// // Wir probieren ALLE Extractors, um maximale Informationen zu gewinnen. -// // Das simuliert die Logik deiner alten Parser (erst Header, dann Details). -// allExtractors := append(p.CommonExt, p.Extractors...) - -// for _, ext := range allExtractors { -// matches := ext.Pattern.FindStringSubmatch(currentLine) -// if matches == nil { -// continue -// } - -// subexpNames := ext.Pattern.SubexpNames() -// for i, matchValue := range matches { -// if i == 0 || matchValue == "" { -// continue -// } - -// groupName := subexpNames[i] -// if groupName == "" { -// continue -// } - -// targetType := ext.Fields[groupName] -// parsedValue, err := convertType(matchValue, targetType) -// if err == nil { -// switch groupName { -// case "timestamp": -// if t, ok := parsedValue.(time.Time); ok { -// entry.Timestamp = t -// } -// case "log_level": -// entry.LogLevel = fmt.Sprintf("%v", parsedValue) -// case "message": -// entry.LogMessage = fmt.Sprintf("%v", parsedValue) -// default: -// entry.Fields[groupName] = parsedValue -// } -// } -// } -// } - -// if entry.LogMessage == "" { -// entry.LogMessage = strings.TrimSpace(line) -// } - -// return entry, nil -// } - -// func convertType(value, typeDef string) (any, error) { -// if strings.HasPrefix(typeDef, "int") { -// return strconv.Atoi(value) -// } -// if strings.HasPrefix(typeDef, "float") { -// return strconv.ParseFloat(value, 64) -// } -// if after, ok := strings.CutPrefix(typeDef, "time:"); ok { -// layout := after -// // Workaround für Syslog (Jahr fehlt oft), hier vereinfacht: -// if layout == "Jan 02 15:04:05" { -// t, err := time.Parse(layout, value) -// if err == nil { -// return t.AddDate(time.Now().Year(), 0, 0), nil -// } -// return t, err -// } -// return time.Parse(layout, value) -// } -// // Default: String -// return value, nil -// } package parser import ( @@ -287,8 +173,6 @@ func (p *GenericParser) mapField(entry *models.LogMessage, key string, value any entry.PID = pid } } - // Mapping auf ServiceInformation Felder (Optional, falls nötig) - // case "transfer_id": ... default: entry.Fields[key] = value diff --git a/parser/nginx_parser.go b/parser/nginx_parser.go deleted file mode 100644 index 0bec84b..0000000 --- a/parser/nginx_parser.go +++ /dev/null @@ -1,57 +0,0 @@ -package parser - -import ( - "log/slog" - "regexp" - "strconv" - "strings" - "watch-tool/models" -) - -var ( - nginxAccessPattern = regexp.MustCompile(`^(\S+)\s+\S+\s+(\S+)\s+\[([^\]]+)\]\s+"([^"]+)"\s+(\d+)\s+(\d+|-)\s*(?:"([^"]*)"\s+"([^"]*)")?`) -) - -type NginxParser struct{} - -func (n *NginxParser) Parse(line string) (models.LogMessage, error) { - newEntry := models.LogMessage{ - Service: "nginx", - } - - matches := nginxAccessPattern.FindStringSubmatch(strings.TrimSpace(line)) - if len(matches) < 7 { - return newEntry, nil - } - statusCode, err := strconv.ParseInt(matches[5], 10, 64) - if err != nil { - slog.Error("cant parse statuscode", "error", err) - } - bytesSend, err := strconv.ParseInt(matches[6], 10, 64) - if err != nil { - slog.Error("cant parse bytessend", "error", err) - } - baseInfo := models.NGinXBaseInfo{ - ClientIP: matches[1], - RemoteUser: matches[2], - Request: matches[4], - StatusCode: int(statusCode), - BytesSend: int(bytesSend), - } - - if len(matches) > 7 && matches[7] != "" { - baseInfo.Referer = matches[7] - } - if len(matches) > 8 && matches[8] != "" { - baseInfo.UserAgent = matches[8] - } - - if requestParts := strings.Fields(matches[4]); len(requestParts) >= 3 { - baseInfo.HTTPMethod = requestParts[0] - baseInfo.RequestURI = requestParts[1] - baseInfo.HTTPVersion = requestParts[2] - } - newEntry.ServiceInformation = baseInfo - - return newEntry, nil -} diff --git a/parser/regex_parser.go b/parser/regex_parser.go deleted file mode 100644 index b4c6a82..0000000 --- a/parser/regex_parser.go +++ /dev/null @@ -1,58 +0,0 @@ -package parser - -import ( - "regexp" - "strings" - "watch-tool/models" -) - -type RegexLogParser struct { - Pattern *regexp.Regexp - Fields map[string]string - Toolname string - Hostname string -} - -func (p *RegexLogParser) Parse(line string) (models.LogMessage, error) { - entry := models.LogMessage{Type: "log_entry"} - entry.Tool = p.Toolname - entry.Raw = line - entry.Host = p.Hostname - - fields := p.parseWithPattern(line) - if fields != nil { - entry.Fields = fields - } else { - entry.LogMessage = strings.TrimSpace(line) - } - - return entry, nil -} - -func (p *RegexLogParser) parseWithPattern(text string) map[string]any { - matches := p.Pattern.FindStringSubmatch(text) - if matches == nil { - return nil - } - - fields := make(map[string]any) - subexpNames := p.Pattern.SubexpNames() - - for i, match := range matches { - if i == 0 { - continue - } - - if i < len(subexpNames) && subexpNames[i] != "" { - fieldName := subexpNames[i] - - if mappedName, exists := p.Fields[fieldName]; exists { - fieldName = mappedName - } - - fields[fieldName] = match - } - } - - return fields -} diff --git a/parser/tcc_parser.go b/parser/tcc_parser.go deleted file mode 100644 index 1cca213..0000000 --- a/parser/tcc_parser.go +++ /dev/null @@ -1,49 +0,0 @@ -package parser - -import ( - "log/slog" - "regexp" - "strings" - "time" - "watch-tool/helpers" - "watch-tool/models" -) - -var ( - tccServicePattern = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)\s+(\w+)\s+(\d+)\s+---\s+\[\s*([^\]]*)\]\s+([\w\.]+)\s*:\s*(.*)$`) -) - -type TCCParser struct{} - -func (t *TCCParser) Parse(line string) (models.LogMessage, error) { - newEntry := models.LogMessage{ - Service: "tixel-control-center", - } - syslogFields, logContent := helpers.ExtractSyslogHeader(line) - newEntry.Host = syslogFields.Hostname - - matches := tccServicePattern.FindStringSubmatch(strings.TrimSpace(logContent)) - if len(matches) != 7 { - newEntry.Timestamp = time.Now() - newEntry.LogMessage = line - return newEntry, nil - } - - timestampStr := strings.Join(strings.Split(matches[1], " "), "T") - timestamp, err := helpers.ParseRFC3339WithOptionalZ(timestampStr) - if err != nil { - slog.Error("unable to parse time", "error", err) - return newEntry, err - } - baseInfo := models.TCCBaseInfo{ - ProcessID: matches[3], - ThreadID: strings.TrimSpace(matches[4]), - LoggerName: matches[5], - } - newEntry.Timestamp = timestamp - newEntry.LogLevel = matches[2] - newEntry.LogMessage = matches[6] - newEntry.ServiceInformation = baseInfo - - return newEntry, nil -} diff --git a/parser/tjm_parser.go b/parser/tjm_parser.go deleted file mode 100644 index cbf6498..0000000 --- a/parser/tjm_parser.go +++ /dev/null @@ -1,91 +0,0 @@ -package parser - -import ( - "log/slog" - "regexp" - "strings" - "watch-tool/helpers" - "watch-tool/models" -) - -var ( - tjmServicePattern = regexp.MustCompile(`^(?