package app import ( "fmt" "log" "os" "regexp" "strings" "time" "gopkg.in/yaml.v3" ) type Conf struct { Patterns map[string]string `yaml:"patterns"` Streams map[string]*Stream `yaml:"streams"` } type Stream struct { name string `yaml:"-"` Cmd []string `yaml:"cmd"` Filters map[string]*Filter `yaml:"filters"` } type Filter struct { stream *Stream `yaml:"-"` name string `yaml:"-"` Regex []string `yaml:"regex"` compiledRegex []regexp.Regexp `yaml:"-"` patternName string `yaml:"-"` patternWithBraces string `yaml:"-"` Retry int `yaml:"retry"` RetryPeriod string `yaml:"retry-period"` retryDuration time.Duration `yaml:"-"` Actions map[string]*Action `yaml:"actions"` matches map[string][]time.Time `yaml:"-"` } type Action struct { filter *Filter `yaml:"-"` name string `yaml:"-"` Cmd []string `yaml:"cmd"` After string `yaml:"after"` afterDuration time.Duration `yaml:"-"` } func (c *Conf) setup() { for patternName, pattern := range c.Patterns { c.Patterns[patternName] = fmt.Sprintf("(?P<%s>%s)", patternName, pattern) } for streamName := range c.Streams { stream := c.Streams[streamName] stream.name = streamName for filterName := range stream.Filters { filter := stream.Filters[filterName] filter.stream = stream filter.name = filterName filter.matches = make(map[string][]time.Time) // Parse Duration retryDuration, err := time.ParseDuration(filter.RetryPeriod) if err != nil { log.Fatalln("Failed to parse time in configuration file:", err) } filter.retryDuration = retryDuration // Compute Regexes // Look for Patterns inside Regexes for _, regex := range filter.Regex { for patternName, pattern := range c.Patterns { if strings.Contains(regex, patternName) { switch filter.patternName { case "": filter.patternName = patternName filter.patternWithBraces = fmt.Sprintf("<%s>", patternName) case patternName: // no op default: log.Fatalf( "ERROR Can't mix different patterns (%s, %s) in same filter (%s.%s)\n", filter.patternName, patternName, streamName, filterName, ) } regex = strings.Replace(regex, fmt.Sprintf("<%s>", patternName), pattern, 1) } } filter.compiledRegex = append(filter.compiledRegex, *regexp.MustCompile(regex)) } for actionName := range filter.Actions { action := filter.Actions[actionName] action.filter = filter action.name = actionName // Parse Duration if action.After != "" { afterDuration, err := time.ParseDuration(action.After) if err != nil { log.Fatalln("Failed to parse time in configuration file:", err) } action.afterDuration = afterDuration } } } } } func parseConf(filename string) *Conf { data, err := os.ReadFile(filename) if err != nil { log.Fatalln("Failed to read configuration file:", err) } var conf Conf err = yaml.Unmarshal(data, &conf) if err != nil { log.Fatalln("Failed to parse configuration file:", err) } conf.setup() return &conf }