2023-12-23 20:29:38 +01:00
|
|
|
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
package file
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/wneessen/go-parsesyslog"
|
|
|
|
|
2024-03-21 15:39:31 +01:00
|
|
|
"github.com/wneessen/logranger/plugins/actions"
|
|
|
|
"github.com/wneessen/logranger/template"
|
2023-12-23 20:29:38 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// File represents a file action that can be performed on a log message.
|
2023-12-25 21:08:03 +01:00
|
|
|
type File struct {
|
|
|
|
Enabled bool
|
|
|
|
FilePath string
|
|
|
|
OutputTemplate string
|
|
|
|
Overwrite bool
|
|
|
|
}
|
2023-12-23 20:29:38 +01:00
|
|
|
|
2023-12-25 21:08:03 +01:00
|
|
|
// 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.
|
2024-03-21 20:22:33 +01:00
|
|
|
func (f *File) Config(configMap map[string]any) error {
|
|
|
|
if configMap["file"] == nil {
|
2023-12-23 20:29:38 +01:00
|
|
|
return nil
|
|
|
|
}
|
2024-03-21 20:22:33 +01:00
|
|
|
config, ok := configMap["file"].(map[string]any)
|
2023-12-23 20:29:38 +01:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("missing configuration for file action")
|
|
|
|
}
|
2023-12-25 21:08:03 +01:00
|
|
|
f.Enabled = true
|
|
|
|
|
2024-03-21 20:22:33 +01:00
|
|
|
filePath, ok := config["output_filepath"].(string)
|
|
|
|
if !ok || filePath == "" {
|
2023-12-25 21:08:03 +01:00
|
|
|
return fmt.Errorf("no output_filename configured for file action")
|
|
|
|
}
|
2024-03-21 20:22:33 +01:00
|
|
|
f.FilePath = filePath
|
2023-12-25 21:08:03 +01:00
|
|
|
|
2024-03-21 20:22:33 +01:00
|
|
|
outputTpl, ok := config["output_template"].(string)
|
|
|
|
if !ok || outputTpl == "" {
|
2023-12-23 20:29:38 +01:00
|
|
|
return fmt.Errorf("not output_template configured for file action")
|
|
|
|
}
|
2024-03-21 20:22:33 +01:00
|
|
|
f.OutputTemplate = outputTpl
|
2023-12-23 20:29:38 +01:00
|
|
|
|
2024-03-21 20:22:33 +01:00
|
|
|
if hasOverwrite, ok := config["overwrite"].(bool); ok && hasOverwrite {
|
2023-12-25 21:08:03 +01:00
|
|
|
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).
|
2024-03-21 20:22:33 +01:00
|
|
|
func (f *File) Process(logMessage parsesyslog.LogMsg, matchGroup []string) error {
|
2023-12-25 21:08:03 +01:00
|
|
|
if !f.Enabled {
|
|
|
|
return nil
|
2023-12-23 20:29:38 +01:00
|
|
|
}
|
|
|
|
|
2024-03-21 20:22:33 +01:00
|
|
|
openFlags := os.O_APPEND | os.O_CREATE | os.O_WRONLY
|
2023-12-25 21:08:03 +01:00
|
|
|
if f.Overwrite {
|
2024-03-21 20:22:33 +01:00
|
|
|
openFlags = os.O_TRUNC | os.O_CREATE | os.O_WRONLY
|
2023-12-23 20:29:38 +01:00
|
|
|
}
|
|
|
|
|
2024-03-21 20:22:33 +01:00
|
|
|
fileHandle, err := os.OpenFile(f.FilePath, openFlags, 0o600)
|
2023-12-23 20:29:38 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open file for writing in file action: %w", err)
|
|
|
|
}
|
|
|
|
defer func() {
|
2024-03-21 20:22:33 +01:00
|
|
|
_ = fileHandle.Close()
|
2023-12-23 20:29:38 +01:00
|
|
|
}()
|
|
|
|
|
2024-03-21 20:22:33 +01:00
|
|
|
tpl, err := template.Compile(logMessage, matchGroup, f.OutputTemplate)
|
2023-12-23 20:29:38 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-21 20:22:33 +01:00
|
|
|
_, err = fileHandle.WriteString(tpl)
|
2023-12-23 20:29:38 +01:00
|
|
|
if err != nil {
|
2023-12-25 21:08:03 +01:00
|
|
|
return fmt.Errorf("failed to write log message to file %q: %w",
|
|
|
|
f.FilePath, err)
|
2023-12-23 20:29:38 +01:00
|
|
|
}
|
2024-03-21 20:22:33 +01:00
|
|
|
if err = fileHandle.Sync(); err != nil {
|
2023-12-25 21:08:03 +01:00
|
|
|
return fmt.Errorf("failed to sync memory to file %q: %w",
|
|
|
|
f.FilePath, err)
|
2023-12-23 20:29:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// init registers the "file" action with the Actions map.
|
|
|
|
func init() {
|
|
|
|
actions.Add("file", &File{})
|
|
|
|
}
|