diff --git a/plugins/action.go b/plugins/action.go index 8de065d..31d0938 100644 --- a/plugins/action.go +++ b/plugins/action.go @@ -14,5 +14,6 @@ import ( // The Process method takes a log message, a slice of match groups, and a // configuration map, and returns an error if any occurs during processing. type Action interface { - Process(logmessage parsesyslog.LogMsg, matchgroup []string, confmap map[string]any) error + Config(confmap map[string]any) error + Process(logmessage parsesyslog.LogMsg, matchgroup []string) error } diff --git a/plugins/actions/file/file.go b/plugins/actions/file/file.go index 32f1c60..7f8fae8 100644 --- a/plugins/actions/file/file.go +++ b/plugins/actions/file/file.go @@ -15,11 +15,26 @@ import ( ) // File represents a file action that can be performed on a log message. -type File struct{} +type File struct { + Enabled bool + FilePath string + OutputTemplate string + Overwrite bool +} -// Process satisfies the plugins.Action interface for the File type -// It takes in the log message (lm), match groups (mg), and configuration map (cm). -func (f *File) Process(lm parsesyslog.LogMsg, mg []string, cm map[string]any) error { +// Config satisfies the plugins.Action interface for the File type +// It updates the configuration of the File action based on the provided +// configuration map. +// +// It expects the configuration map to have a key "file" which contains a submap +// with the following keys: +// - "output_filepath" (string): Specifies the file path where the output will be written. +// - "output_template" (string): Specifies the template to use for formatting the output. +// - "overwrite" (bool, optional): If true, the file will be overwritten instead of appended to. +// +// If any of the required configuration parameters are missing or invalid, an error +// is returned. +func (f *File) Config(cm map[string]any) error { if cm["file"] == nil { return nil } @@ -27,22 +42,40 @@ func (f *File) Process(lm parsesyslog.LogMsg, mg []string, cm map[string]any) er if !ok { return fmt.Errorf("missing configuration for file action") } + f.Enabled = true + + fp, ok := c["output_filepath"].(string) + if !ok || fp == "" { + return fmt.Errorf("no output_filename configured for file action") + } + f.FilePath = fp + ot, ok := c["output_template"].(string) if !ok || ot == "" { return fmt.Errorf("not output_template configured for file action") } + f.OutputTemplate = ot - fn, ok := c["output_filepath"].(string) - if !ok || fn == "" { - return fmt.Errorf("no output_filename configured for file action") + if ow, ok := c["overwrite"].(bool); ok && ow { + f.Overwrite = true + } + + return nil +} + +// Process satisfies the plugins.Action interface for the File type +// It takes in the log message (lm), match groups (mg), and configuration map (cm). +func (f *File) Process(lm parsesyslog.LogMsg, mg []string) error { + if !f.Enabled { + return nil } of := os.O_APPEND | os.O_CREATE | os.O_WRONLY - if ow, ok := c["overwrite"].(bool); ok && ow { - of = os.O_WRONLY | os.O_CREATE + if f.Overwrite { + of = os.O_TRUNC | os.O_CREATE | os.O_WRONLY } - fh, err := os.OpenFile(fn, of, 0o600) + fh, err := os.OpenFile(f.FilePath, of, 0o600) if err != nil { return fmt.Errorf("failed to open file for writing in file action: %w", err) } @@ -50,16 +83,18 @@ func (f *File) Process(lm parsesyslog.LogMsg, mg []string, cm map[string]any) er _ = fh.Close() }() - t, err := template.Compile(lm, mg, ot) + t, err := template.Compile(lm, mg, f.OutputTemplate) if err != nil { return err } _, err = fh.WriteString(t) if err != nil { - return fmt.Errorf("failed to write log message to file %q: %w", fn, err) + return fmt.Errorf("failed to write log message to file %q: %w", + f.FilePath, err) } - if err := fh.Sync(); err != nil { - return fmt.Errorf("failed to sync memory to file %q: %w", fn, err) + if err = fh.Sync(); err != nil { + return fmt.Errorf("failed to sync memory to file %q: %w", + f.FilePath, err) } return nil diff --git a/server.go b/server.go index b84ed4e..8c1d241 100644 --- a/server.go +++ b/server.go @@ -189,7 +189,11 @@ func (s *Server) processMessage(lm parsesyslog.LogMsg) error { mg := r.Regexp.FindStringSubmatch(lm.Message.String()) for n, a := range actions.Actions { s.log.Debug("trying to execute action", slog.String("action_name", n)) - if err := a.Process(lm, mg, r.Actions); err != nil { + if err := a.Config(r.Actions); err != nil { + s.log.Error("failed to config action", LogErrKey, err, + slog.String("action", n), slog.String("rule_id", r.ID)) + } + if err := a.Process(lm, mg); err != nil { s.log.Error("failed to process action", LogErrKey, err, slog.String("action", n), slog.String("rule_id", r.ID)) }