Merge pull request #9 from wneessen/readability

Refactor variable names for improved code readability
This commit is contained in:
Winni Neessen 2024-03-21 20:24:23 +01:00 committed by GitHub
commit 166878714d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 204 additions and 203 deletions

View file

@ -20,49 +20,49 @@ const (
) )
func main() { func main() {
l := slog.New(slog.NewJSONHandler(os.Stdout, nil)).With(slog.String("context", "logranger")) logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)).With(slog.String("context", "logranger"))
cp := "logranger.toml" confPath := "logranger.toml"
cpe := os.Getenv("LOGRANGER_CONFIG") confPathEnv := os.Getenv("LOGRANGER_CONFIG")
if cpe != "" { if confPathEnv != "" {
cp = cpe confPath = confPathEnv
} }
p := filepath.Dir(cp) path := filepath.Dir(confPath)
f := filepath.Base(cp) file := filepath.Base(confPath)
c, err := logranger.NewConfig(p, f) config, err := logranger.NewConfig(path, file)
if err != nil { if err != nil {
l.Error("failed to read/parse config", LogErrKey, err) logger.Error("failed to read/parse config", LogErrKey, err)
os.Exit(1) os.Exit(1)
} }
s, err := logranger.New(c) server, err := logranger.New(config)
if err != nil { if err != nil {
l.Error("failed to create new server", LogErrKey, err) logger.Error("failed to create new server", LogErrKey, err)
os.Exit(1) os.Exit(1)
} }
go func() { go func() {
if err = s.Run(); err != nil { if err = server.Run(); err != nil {
l.Error("failed to start logranger", LogErrKey, err) logger.Error("failed to start logranger", LogErrKey, err)
os.Exit(1) os.Exit(1)
} }
}() }()
sc := make(chan os.Signal, 1) signalChan := make(chan os.Signal, 1)
signal.Notify(sc) signal.Notify(signalChan)
for rc := range sc { for recvSig := range signalChan {
if rc == syscall.SIGKILL || rc == syscall.SIGABRT || rc == syscall.SIGINT || rc == syscall.SIGTERM { if recvSig == syscall.SIGKILL || recvSig == syscall.SIGABRT || recvSig == syscall.SIGINT || recvSig == syscall.SIGTERM {
l.Warn("received signal. shutting down server", slog.String("signal", rc.String())) logger.Warn("received signal. shutting down server", slog.String("signal", recvSig.String()))
// s.Stop() // server.Stop()
l.Info("server gracefully shut down") logger.Info("server gracefully shut down")
os.Exit(0) os.Exit(0)
} }
if rc == syscall.SIGHUP { if recvSig == syscall.SIGHUP {
l.Info(`received signal`, logger.Info(`received signal`,
slog.String("signal", "SIGHUP"), slog.String("signal", "SIGHUP"),
slog.String("action", "reloading config/ruleset")) slog.String("action", "reloading config/ruleset"))
if err = s.ReloadConfig(p, f); err != nil { if err = server.ReloadConfig(path, file); err != nil {
l.Error("failed to reload config", LogErrKey, err) logger.Error("failed to reload config", LogErrKey, err)
} }
} }
} }

View file

@ -56,25 +56,25 @@ type Config struct {
// configuration values. It takes in the file path and file name of the configuration // configuration values. It takes in the file path and file name of the configuration
// file as parameters. It returns a pointer to the Config object and an error if // file as parameters. It returns a pointer to the Config object and an error if
// there was a problem reading or loading the configuration. // there was a problem reading or loading the configuration.
func NewConfig(p, f string) (*Config, error) { func NewConfig(path, file string) (*Config, error) {
co := Config{} config := Config{}
_, err := os.Stat(fmt.Sprintf("%s/%s", p, f)) _, err := os.Stat(fmt.Sprintf("%s/%s", path, file))
if err != nil { if err != nil {
return &co, fmt.Errorf("failed to read config: %w", err) return &config, fmt.Errorf("failed to read config: %w", err)
} }
if err := fig.Load(&co, fig.Dirs(p), fig.File(f), fig.UseEnv("logranger")); err != nil { if err := fig.Load(&config, fig.Dirs(path), fig.File(file), fig.UseEnv("logranger")); err != nil {
return &co, fmt.Errorf("failed to load config: %w", err) return &config, fmt.Errorf("failed to load config: %w", err)
} }
switch { switch {
case strings.EqualFold(co.Parser.Type, "rfc3164"): case strings.EqualFold(config.Parser.Type, "rfc3164"):
co.internal.ParserType = rfc3164.Type config.internal.ParserType = rfc3164.Type
case strings.EqualFold(co.Parser.Type, "rfc5424"): case strings.EqualFold(config.Parser.Type, "rfc5424"):
co.internal.ParserType = rfc5424.Type config.internal.ParserType = rfc5424.Type
default: default:
return nil, fmt.Errorf("unknown parser type: %s", co.Parser.Type) return nil, fmt.Errorf("unknown parser type: %s", config.Parser.Type)
} }
return &co, nil return &config, nil
} }

View file

@ -22,14 +22,14 @@ type Connection struct {
// NewConnection creates a new Connection object with the provided net.Conn. // NewConnection creates a new Connection object with the provided net.Conn.
// The Connection object holds a reference to the provided net.Conn, along with an ID string, // The Connection object holds a reference to the provided net.Conn, along with an ID string,
// bufio.Reader, and bufio.Writer. It returns a pointer to the created Connection object. // bufio.Reader, and bufio.Writer. It returns a pointer to the created Connection object.
func NewConnection(nc net.Conn) *Connection { func NewConnection(netConn net.Conn) *Connection {
c := &Connection{ connection := &Connection{
conn: nc, conn: netConn,
id: NewConnectionID(), id: NewConnectionID(),
rb: bufio.NewReader(nc), rb: bufio.NewReader(netConn),
wb: bufio.NewWriter(nc), wb: bufio.NewWriter(netConn),
} }
return c return connection
} }
// NewConnectionID generates a new unique message ID using a random number generator // NewConnectionID generates a new unique message ID using a random number generator

View file

@ -26,42 +26,43 @@ const (
// NewListener initializes and returns a net.Listener based on the provided // NewListener initializes and returns a net.Listener based on the provided
// configuration. It takes a pointer to a Config struct as a parameter. // configuration. It takes a pointer to a Config struct as a parameter.
// Returns the net.Listener and an error if any occurred during initialization. // Returns the net.Listener and an error if any occurred during initialization.
func NewListener(c *Config) (net.Listener, error) { func NewListener(config *Config) (net.Listener, error) {
var l net.Listener var listener net.Listener
var lerr error var listenerErr error
switch c.Listener.Type { switch config.Listener.Type {
case ListenerUnix: case ListenerUnix:
rua, err := net.ResolveUnixAddr("unix", c.Listener.ListenerUnix.Path) resolveUnixAddr, err := net.ResolveUnixAddr("unix", config.Listener.ListenerUnix.Path)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to resolve UNIX listener socket: %w", err) return nil, fmt.Errorf("failed to resolve UNIX listener socket: %w", err)
} }
l, lerr = net.Listen("unix", rua.String()) listener, listenerErr = net.Listen("unix", resolveUnixAddr.String())
case ListenerTCP: case ListenerTCP:
la := net.JoinHostPort(c.Listener.ListenerTCP.Addr, fmt.Sprintf("%d", c.Listener.ListenerTCP.Port)) listenAddr := net.JoinHostPort(config.Listener.ListenerTCP.Addr,
l, lerr = net.Listen("tcp", la) fmt.Sprintf("%d", config.Listener.ListenerTCP.Port))
listener, listenerErr = net.Listen("tcp", listenAddr)
case ListenerTLS: case ListenerTLS:
if c.Listener.ListenerTLS.CertPath == "" || c.Listener.ListenerTLS.KeyPath == "" { if config.Listener.ListenerTLS.CertPath == "" || config.Listener.ListenerTLS.KeyPath == "" {
return nil, ErrCertConfigEmpty return nil, ErrCertConfigEmpty
} }
ce, err := tls.LoadX509KeyPair(c.Listener.ListenerTLS.CertPath, c.Listener.ListenerTLS.KeyPath) cert, err := tls.LoadX509KeyPair(config.Listener.ListenerTLS.CertPath, config.Listener.ListenerTLS.KeyPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load X509 certificate: %w", err) return nil, fmt.Errorf("failed to load X509 certificate: %w", err)
} }
la := net.JoinHostPort(c.Listener.ListenerTLS.Addr, fmt.Sprintf("%d", c.Listener.ListenerTLS.Port)) listenAddr := net.JoinHostPort(config.Listener.ListenerTLS.Addr, fmt.Sprintf("%d", config.Listener.ListenerTLS.Port))
lc := &tls.Config{Certificates: []tls.Certificate{ce}} listenConf := &tls.Config{Certificates: []tls.Certificate{cert}}
l, lerr = tls.Listen("tcp", la, lc) listener, listenerErr = tls.Listen("tcp", listenAddr, listenConf)
default: default:
return nil, fmt.Errorf("failed to initialize listener: unknown listener type in config") return nil, fmt.Errorf("failed to initialize listener: unknown listener type in config")
} }
if lerr != nil { if listenerErr != nil {
return nil, fmt.Errorf("failed to initialize listener: %w", lerr) return nil, fmt.Errorf("failed to initialize listener: %w", listenerErr)
} }
return l, nil return listener, nil
} }
// UnmarshalString satisfies the fig.StringUnmarshaler interface for the ListenerType type // UnmarshalString satisfies the fig.StringUnmarshaler interface for the ListenerType type
func (l *ListenerType) UnmarshalString(v string) error { func (l *ListenerType) UnmarshalString(value string) error {
switch strings.ToLower(v) { switch strings.ToLower(value) {
case "unix": case "unix":
*l = ListenerUnix *l = ListenerUnix
case "tcp": case "tcp":
@ -69,7 +70,7 @@ func (l *ListenerType) UnmarshalString(v string) error {
case "tls": case "tls":
*l = ListenerTLS *l = ListenerTLS
default: default:
return fmt.Errorf("unknown listener type: %s", v) return fmt.Errorf("unknown listener type: %s", value)
} }
return nil return nil
} }

View file

@ -34,29 +34,29 @@ type File struct {
// //
// If any of the required configuration parameters are missing or invalid, an error // If any of the required configuration parameters are missing or invalid, an error
// is returned. // is returned.
func (f *File) Config(cm map[string]any) error { func (f *File) Config(configMap map[string]any) error {
if cm["file"] == nil { if configMap["file"] == nil {
return nil return nil
} }
c, ok := cm["file"].(map[string]any) config, ok := configMap["file"].(map[string]any)
if !ok { if !ok {
return fmt.Errorf("missing configuration for file action") return fmt.Errorf("missing configuration for file action")
} }
f.Enabled = true f.Enabled = true
fp, ok := c["output_filepath"].(string) filePath, ok := config["output_filepath"].(string)
if !ok || fp == "" { if !ok || filePath == "" {
return fmt.Errorf("no output_filename configured for file action") return fmt.Errorf("no output_filename configured for file action")
} }
f.FilePath = fp f.FilePath = filePath
ot, ok := c["output_template"].(string) outputTpl, ok := config["output_template"].(string)
if !ok || ot == "" { if !ok || outputTpl == "" {
return fmt.Errorf("not output_template configured for file action") return fmt.Errorf("not output_template configured for file action")
} }
f.OutputTemplate = ot f.OutputTemplate = outputTpl
if ow, ok := c["overwrite"].(bool); ok && ow { if hasOverwrite, ok := config["overwrite"].(bool); ok && hasOverwrite {
f.Overwrite = true f.Overwrite = true
} }
@ -65,34 +65,34 @@ func (f *File) Config(cm map[string]any) error {
// Process satisfies the plugins.Action interface for the File type // Process satisfies the plugins.Action interface for the File type
// It takes in the log message (lm), match groups (mg), and configuration map (cm). // It takes in the log message (lm), match groups (mg), and configuration map (cm).
func (f *File) Process(lm parsesyslog.LogMsg, mg []string) error { func (f *File) Process(logMessage parsesyslog.LogMsg, matchGroup []string) error {
if !f.Enabled { if !f.Enabled {
return nil return nil
} }
of := os.O_APPEND | os.O_CREATE | os.O_WRONLY openFlags := os.O_APPEND | os.O_CREATE | os.O_WRONLY
if f.Overwrite { if f.Overwrite {
of = os.O_TRUNC | os.O_CREATE | os.O_WRONLY openFlags = os.O_TRUNC | os.O_CREATE | os.O_WRONLY
} }
fh, err := os.OpenFile(f.FilePath, of, 0o600) fileHandle, err := os.OpenFile(f.FilePath, openFlags, 0o600)
if err != nil { if err != nil {
return fmt.Errorf("failed to open file for writing in file action: %w", err) return fmt.Errorf("failed to open file for writing in file action: %w", err)
} }
defer func() { defer func() {
_ = fh.Close() _ = fileHandle.Close()
}() }()
t, err := template.Compile(lm, mg, f.OutputTemplate) tpl, err := template.Compile(logMessage, matchGroup, f.OutputTemplate)
if err != nil { if err != nil {
return err return err
} }
_, err = fh.WriteString(t) _, err = fileHandle.WriteString(tpl)
if err != nil { if err != nil {
return fmt.Errorf("failed to write log message to file %q: %w", return fmt.Errorf("failed to write log message to file %q: %w",
f.FilePath, err) f.FilePath, err)
} }
if err = fh.Sync(); err != nil { if err = fileHandle.Sync(); err != nil {
return fmt.Errorf("failed to sync memory to file %q: %w", return fmt.Errorf("failed to sync memory to file %q: %w",
f.FilePath, err) f.FilePath, err)
} }

30
rule.go
View file

@ -32,28 +32,28 @@ type Rule struct {
// existence, and loads the Ruleset using the fig library. // existence, and loads the Ruleset using the fig library.
// It checks for duplicate rules and returns an error if any duplicates are found. // It checks for duplicate rules and returns an error if any duplicates are found.
// If all operations are successful, it returns the created Ruleset and no error. // If all operations are successful, it returns the created Ruleset and no error.
func NewRuleset(c *Config) (*Ruleset, error) { func NewRuleset(config *Config) (*Ruleset, error) {
rs := &Ruleset{} ruleset := &Ruleset{}
p := filepath.Dir(c.Server.RuleFile) path := filepath.Dir(config.Server.RuleFile)
f := filepath.Base(c.Server.RuleFile) file := filepath.Base(config.Server.RuleFile)
_, err := os.Stat(fmt.Sprintf("%s/%s", p, f)) _, err := os.Stat(fmt.Sprintf("%s/%s", path, file))
if err != nil { if err != nil {
return rs, fmt.Errorf("failed to read config: %w", err) return ruleset, fmt.Errorf("failed to read config: %w", err)
} }
if err = fig.Load(rs, fig.Dirs(p), fig.File(f), fig.UseStrict()); err != nil { if err = fig.Load(ruleset, fig.Dirs(path), fig.File(file), fig.UseStrict()); err != nil {
return rs, fmt.Errorf("failed to load ruleset: %w", err) return ruleset, fmt.Errorf("failed to load ruleset: %w", err)
} }
rna := make([]string, 0) rules := make([]string, 0)
for _, r := range rs.Rule { for _, rule := range ruleset.Rule {
for _, rn := range rna { for _, rulename := range rules {
if strings.EqualFold(r.ID, rn) { if strings.EqualFold(rule.ID, rulename) {
return nil, fmt.Errorf("duplicate rule found: %s", r.ID) return nil, fmt.Errorf("duplicate rule found: %s", rule.ID)
} }
} }
rna = append(rna, r.ID) rules = append(rules, rule.ID)
} }
return rs, nil return ruleset, nil
} }

122
server.go
View file

@ -45,62 +45,62 @@ type Server struct {
} }
// New creates a new instance of Server based on the provided Config // New creates a new instance of Server based on the provided Config
func New(c *Config) (*Server, error) { func New(config *Config) (*Server, error) {
s := &Server{ server := &Server{
conf: c, conf: config,
} }
s.setLogLevel() server.setLogLevel()
if err := s.setRules(); err != nil { if err := server.setRules(); err != nil {
return s, err return server, err
} }
p, err := parsesyslog.New(s.conf.internal.ParserType) parser, err := parsesyslog.New(server.conf.internal.ParserType)
if err != nil { if err != nil {
return s, fmt.Errorf("failed to initialize syslog parser: %w", err) return server, fmt.Errorf("failed to initialize syslog parser: %w", err)
} }
s.parser = p server.parser = parser
if len(actions.Actions) <= 0 { if len(actions.Actions) <= 0 {
return s, fmt.Errorf("no action plugins found/configured") return server, fmt.Errorf("no action plugins found/configured")
} }
return s, nil return server, nil
} }
// Run starts the logranger Server by creating a new listener using the NewListener // Run starts the logranger Server by creating a new listener using the NewListener
// method and calling RunWithListener with the obtained listener. // method and calling RunWithListener with the obtained listener.
func (s *Server) Run() error { func (s *Server) Run() error {
l, err := NewListener(s.conf) listener, err := NewListener(s.conf)
if err != nil { if err != nil {
return err return err
} }
return s.RunWithListener(l) return s.RunWithListener(listener)
} }
// RunWithListener sets the listener for the server and performs some additional // RunWithListener sets the listener for the server and performs some additional
// tasks for initializing the server. It creates a PID file, writes the process ID // tasks for initializing the server. It creates a PID file, writes the process ID
// to the file, and listens for connections. It returns an error if any of the // to the file, and listens for connections. It returns an error if any of the
// initialization steps fail. // initialization steps fail.
func (s *Server) RunWithListener(l net.Listener) error { func (s *Server) RunWithListener(listener net.Listener) error {
s.listener = l s.listener = listener
// Create PID file // Create PID file
pf, err := os.Create(s.conf.Server.PIDFile) pidFile, err := os.Create(s.conf.Server.PIDFile)
if err != nil { if err != nil {
s.log.Error("failed to create PID file", LogErrKey, err) s.log.Error("failed to create PID file", LogErrKey, err)
os.Exit(1) os.Exit(1)
} }
pid := os.Getpid() pid := os.Getpid()
s.log.Debug("creating PID file", slog.String("pid_file", pf.Name()), s.log.Debug("creating PID file", slog.String("pid_file", pidFile.Name()),
slog.Int("pid", pid)) slog.Int("pid", pid))
_, err = pf.WriteString(fmt.Sprintf("%d", pid)) _, err = pidFile.WriteString(fmt.Sprintf("%d", pid))
if err != nil { if err != nil {
s.log.Error("failed to write PID to PID file", LogErrKey, err) s.log.Error("failed to write PID to PID file", LogErrKey, err)
_ = pf.Close() _ = pidFile.Close()
} }
if err = pf.Close(); err != nil { if err = pidFile.Close(); err != nil {
s.log.Error("failed to close PID file", LogErrKey, err) s.log.Error("failed to close PID file", LogErrKey, err)
} }
@ -116,47 +116,47 @@ func (s *Server) Listen() {
defer s.wg.Done() defer s.wg.Done()
s.log.Info("listening for new connections", slog.String("listen_addr", s.listener.Addr().String())) s.log.Info("listening for new connections", slog.String("listen_addr", s.listener.Addr().String()))
for { for {
c, err := s.listener.Accept() acceptConn, err := s.listener.Accept()
if err != nil { if err != nil {
s.log.Error("failed to accept new connection", LogErrKey, err) s.log.Error("failed to accept new connection", LogErrKey, err)
continue continue
} }
s.log.Debug("accepted new connection", s.log.Debug("accepted new connection",
slog.String("remote_addr", c.RemoteAddr().String())) slog.String("remote_addr", acceptConn.RemoteAddr().String()))
conn := NewConnection(c) connection := NewConnection(acceptConn)
s.wg.Add(1) s.wg.Add(1)
go func(co *Connection) { go func(co *Connection) {
s.HandleConnection(co) s.HandleConnection(co)
s.wg.Done() s.wg.Done()
}(conn) }(connection)
} }
} }
// HandleConnection handles a single connection by parsing and processing log messages. // HandleConnection handles a single connection by parsing and processing log messages.
// It logs debug information about the connection and measures the processing time. // It logs debug information about the connection and measures the processing time.
// It closes the connection when done, and logs any error encountered during the process. // It closes the connection when done, and logs any error encountered during the process.
func (s *Server) HandleConnection(c *Connection) { func (s *Server) HandleConnection(connection *Connection) {
defer func() { defer func() {
if err := c.conn.Close(); err != nil { if err := connection.conn.Close(); err != nil {
s.log.Error("failed to close connection", LogErrKey, err) s.log.Error("failed to close connection", LogErrKey, err)
} }
}() }()
ReadLoop: ReadLoop:
for { for {
if err := c.conn.SetDeadline(time.Now().Add(s.conf.Parser.Timeout)); err != nil { if err := connection.conn.SetDeadline(time.Now().Add(s.conf.Parser.Timeout)); err != nil {
s.log.Error("failed to set processing deadline", LogErrKey, err, s.log.Error("failed to set processing deadline", LogErrKey, err,
slog.Duration("timeout", s.conf.Parser.Timeout)) slog.Duration("timeout", s.conf.Parser.Timeout))
return return
} }
lm, err := s.parser.ParseReader(c.rb) logMessage, err := s.parser.ParseReader(connection.rb)
if err != nil { if err != nil {
var ne *net.OpError var netErr *net.OpError
switch { switch {
case errors.As(err, &ne): case errors.As(err, &netErr):
if s.conf.Log.Extended { if s.conf.Log.Extended {
s.log.Error("network error while processing message", LogErrKey, s.log.Error("network error while processing message", LogErrKey,
ne.Error()) netErr.Error())
} }
return return
case errors.Is(err, io.EOF): case errors.Is(err, io.EOF):
@ -172,7 +172,7 @@ ReadLoop:
} }
} }
s.wg.Add(1) s.wg.Add(1)
go s.processMessage(lm) go s.processMessage(logMessage)
} }
} }
@ -182,36 +182,36 @@ ReadLoop:
// The method first checks if the ruleset is not nil. If it is nil, no actions will be // The method first checks if the ruleset is not nil. If it is nil, no actions will be
// executed. For each rule in the ruleset, it checks if the log message matches the // executed. For each rule in the ruleset, it checks if the log message matches the
// rule's regular expression. // rule's regular expression.
func (s *Server) processMessage(lm parsesyslog.LogMsg) { func (s *Server) processMessage(logMessage parsesyslog.LogMsg) {
defer s.wg.Done() defer s.wg.Done()
if s.ruleset != nil { if s.ruleset != nil {
for _, r := range s.ruleset.Rule { for _, rule := range s.ruleset.Rule {
if !r.Regexp.MatchString(lm.Message.String()) { if !rule.Regexp.MatchString(logMessage.Message.String()) {
continue continue
} }
if r.HostMatch != nil && !r.HostMatch.MatchString(lm.Hostname) { if rule.HostMatch != nil && !rule.HostMatch.MatchString(logMessage.Hostname) {
continue continue
} }
mg := r.Regexp.FindStringSubmatch(lm.Message.String()) matchGroup := rule.Regexp.FindStringSubmatch(logMessage.Message.String())
for n, a := range actions.Actions { for name, action := range actions.Actions {
bt := time.Now() startTime := time.Now()
if err := a.Config(r.Actions); err != nil { if err := action.Config(rule.Actions); err != nil {
s.log.Error("failed to config action", LogErrKey, err, s.log.Error("failed to config action", LogErrKey, err,
slog.String("action", n), slog.String("rule_id", r.ID)) slog.String("action", name), slog.String("rule_id", rule.ID))
continue continue
} }
s.log.Debug("log message matches rule, executing action", s.log.Debug("log message matches rule, executing action",
slog.String("action", n), slog.String("rule_id", r.ID)) slog.String("action", name), slog.String("rule_id", rule.ID))
if err := a.Process(lm, mg); err != nil { if err := action.Process(logMessage, matchGroup); err != nil {
s.log.Error("failed to process action", LogErrKey, err, s.log.Error("failed to process action", LogErrKey, err,
slog.String("action", n), slog.String("rule_id", r.ID)) slog.String("action", name), slog.String("rule_id", rule.ID))
} }
if s.conf.Log.Extended { if s.conf.Log.Extended {
pt := time.Since(bt) procTime := time.Since(startTime)
s.log.Debug("action processing benchmark", s.log.Debug("action processing benchmark",
slog.Duration("processing_time", pt), slog.Duration("processing_time", procTime),
slog.String("processing_time_human", pt.String()), slog.String("processing_time_human", procTime.String()),
slog.String("action", n), slog.String("rule_id", r.ID)) slog.String("action", name), slog.String("rule_id", rule.ID))
} }
} }
} }
@ -226,21 +226,21 @@ func (s *Server) processMessage(lm parsesyslog.LogMsg) {
// Finally, it creates a new `slog.Logger` with the JSON handler and sets the `s.log` field // Finally, it creates a new `slog.Logger` with the JSON handler and sets the `s.log` field
// of the `Server` struct to the logger, with a context value of "logranger". // of the `Server` struct to the logger, with a context value of "logranger".
func (s *Server) setLogLevel() { func (s *Server) setLogLevel() {
lo := slog.HandlerOptions{} logOpts := slog.HandlerOptions{}
switch strings.ToLower(s.conf.Log.Level) { switch strings.ToLower(s.conf.Log.Level) {
case "debug": case "debug":
lo.Level = slog.LevelDebug logOpts.Level = slog.LevelDebug
case "info": case "info":
lo.Level = slog.LevelInfo logOpts.Level = slog.LevelInfo
case "warn": case "warn":
lo.Level = slog.LevelWarn logOpts.Level = slog.LevelWarn
case "error": case "error":
lo.Level = slog.LevelError logOpts.Level = slog.LevelError
default: default:
lo.Level = slog.LevelInfo logOpts.Level = slog.LevelInfo
} }
lh := slog.NewJSONHandler(os.Stdout, &lo) logHandler := slog.NewJSONHandler(os.Stdout, &logOpts)
s.log = slog.New(lh).With(slog.String("context", "logranger")) s.log = slog.New(logHandler).With(slog.String("context", "logranger"))
} }
// setRules initializes/updates the ruleset for the logranger Server by // setRules initializes/updates the ruleset for the logranger Server by
@ -248,11 +248,11 @@ func (s *Server) setLogLevel() {
// to the Server's ruleset field. // to the Server's ruleset field.
// It returns an error if there is a failure in reading or loading the ruleset. // It returns an error if there is a failure in reading or loading the ruleset.
func (s *Server) setRules() error { func (s *Server) setRules() error {
rs, err := NewRuleset(s.conf) ruleset, err := NewRuleset(s.conf)
if err != nil { if err != nil {
return fmt.Errorf("failed to read ruleset: %w", err) return fmt.Errorf("failed to read ruleset: %w", err)
} }
s.ruleset = rs s.ruleset = ruleset
return nil return nil
} }
@ -261,12 +261,12 @@ func (s *Server) setRules() error {
// It creates a new Config using the NewConfig method and updates the Server's // It creates a new Config using the NewConfig method and updates the Server's
// conf field. It also reloads the configured Ruleset. // conf field. It also reloads the configured Ruleset.
// If an error occurs while reloading the configuration, an error is returned. // If an error occurs while reloading the configuration, an error is returned.
func (s *Server) ReloadConfig(p, f string) error { func (s *Server) ReloadConfig(path, file string) error {
c, err := NewConfig(p, f) config, err := NewConfig(path, file)
if err != nil { if err != nil {
return fmt.Errorf("failed to reload config: %w", err) return fmt.Errorf("failed to reload config: %w", err)
} }
s.conf = c s.conf = config
if err := s.setRules(); err != nil { if err := s.setRules(); err != nil {
return fmt.Errorf("failed to reload ruleset: %w", err) return fmt.Errorf("failed to reload ruleset: %w", err)

View file

@ -44,99 +44,99 @@ type FuncMap struct{}
// the FuncMap. It then populates a map with values from the LogMsg // the FuncMap. It then populates a map with values from the LogMsg
// and current time and executes the template using the map as the // and current time and executes the template using the map as the
// data source. The compiled template result or an error is returned. // data source. The compiled template result or an error is returned.
func Compile(lm parsesyslog.LogMsg, mg []string, ot string) (string, error) { func Compile(logMessage parsesyslog.LogMsg, matchGroup []string, outputTpl string) (string, error) {
pt := strings.Builder{} procText := strings.Builder{}
fm := NewTemplateFuncMap() funcMap := NewTemplateFuncMap()
ot = strings.ReplaceAll(ot, `\n`, "\n") outputTpl = strings.ReplaceAll(outputTpl, `\n`, "\n")
ot = strings.ReplaceAll(ot, `\t`, "\t") outputTpl = strings.ReplaceAll(outputTpl, `\t`, "\t")
ot = strings.ReplaceAll(ot, `\r`, "\r") outputTpl = strings.ReplaceAll(outputTpl, `\r`, "\r")
tpl, err := template.New("template").Funcs(fm).Parse(ot) tpl, err := template.New("template").Funcs(funcMap).Parse(outputTpl)
if err != nil { if err != nil {
return pt.String(), fmt.Errorf("failed to create template: %w", err) return procText.String(), fmt.Errorf("failed to create template: %w", err)
} }
dm := make(map[string]any) dataMap := make(map[string]any)
dm["match"] = mg dataMap["match"] = matchGroup
dm["hostname"] = lm.Hostname dataMap["hostname"] = logMessage.Hostname
dm["timestamp"] = lm.Timestamp dataMap["timestamp"] = logMessage.Timestamp
dm["now_rfc3339"] = time.Now().Format(time.RFC3339) dataMap["now_rfc3339"] = time.Now().Format(time.RFC3339)
dm["now_unix"] = time.Now().Unix() dataMap["now_unix"] = time.Now().Unix()
dm["severity"] = lm.Severity.String() dataMap["severity"] = logMessage.Severity.String()
dm["facility"] = lm.Facility.String() dataMap["facility"] = logMessage.Facility.String()
dm["appname"] = lm.AppName dataMap["appname"] = logMessage.AppName
dm["original_message"] = lm.Message dataMap["original_message"] = logMessage.Message
if err = tpl.Execute(&pt, dm); err != nil { if err = tpl.Execute(&procText, dataMap); err != nil {
return pt.String(), fmt.Errorf("failed to compile template: %w", err) return procText.String(), fmt.Errorf("failed to compile template: %w", err)
} }
return pt.String(), nil return procText.String(), nil
} }
// NewTemplateFuncMap creates a new template function map by returning a // NewTemplateFuncMap creates a new template function map by returning a
// template.FuncMap. // template.FuncMap.
func NewTemplateFuncMap() template.FuncMap { func NewTemplateFuncMap() template.FuncMap {
fm := FuncMap{} funcMap := FuncMap{}
return template.FuncMap{ return template.FuncMap{
"_ToLower": fm.ToLower, "_ToLower": funcMap.ToLower,
"_ToUpper": fm.ToUpper, "_ToUpper": funcMap.ToUpper,
"_ToBase64": fm.ToBase64, "_ToBase64": funcMap.ToBase64,
"_ToSHA1": fm.ToSHA1, "_ToSHA1": funcMap.ToSHA1,
"_ToSHA256": fm.ToSHA256, "_ToSHA256": funcMap.ToSHA256,
"_ToSHA512": fm.ToSHA512, "_ToSHA512": funcMap.ToSHA512,
} }
} }
// ToLower returns a given string as lower-case representation // ToLower returns a given string as lower-case representation
func (*FuncMap) ToLower(s string) string { func (*FuncMap) ToLower(value string) string {
return strings.ToLower(s) return strings.ToLower(value)
} }
// ToUpper returns a given string as upper-case representation // ToUpper returns a given string as upper-case representation
func (*FuncMap) ToUpper(s string) string { func (*FuncMap) ToUpper(value string) string {
return strings.ToUpper(s) return strings.ToUpper(value)
} }
// ToBase64 returns the base64 encoding of a given string. // ToBase64 returns the base64 encoding of a given string.
func (*FuncMap) ToBase64(s string) string { func (*FuncMap) ToBase64(value string) string {
return base64.RawStdEncoding.EncodeToString([]byte(s)) return base64.RawStdEncoding.EncodeToString([]byte(value))
} }
// ToSHA1 returns the SHA-1 hash of the given string // ToSHA1 returns the SHA-1 hash of the given string
func (*FuncMap) ToSHA1(s string) string { func (*FuncMap) ToSHA1(value string) string {
return toSHA(s, SHA1) return toSHA(value, SHA1)
} }
// ToSHA256 returns the SHA-256 hash of the given string // ToSHA256 returns the SHA-256 hash of the given string
func (*FuncMap) ToSHA256(s string) string { func (*FuncMap) ToSHA256(value string) string {
return toSHA(s, SHA256) return toSHA(value, SHA256)
} }
// ToSHA512 returns the SHA-512 hash of the given string // ToSHA512 returns the SHA-512 hash of the given string
func (*FuncMap) ToSHA512(s string) string { func (*FuncMap) ToSHA512(value string) string {
return toSHA(s, SHA512) return toSHA(value, SHA512)
} }
// toSHA is a function that converts a string to a SHA hash. // toSHA is a function that converts a string to a SHA hash.
// //
// The function takes two parameters: a string 's' and a 'sa' of // The function takes two parameters: a string 's' and a 'sa' of
// type SHAAlgo which defines the SHA algorithm to be used. // type SHAAlgo which defines the SHA algorithm to be used.
func toSHA(s string, sa SHAAlgo) string { func toSHA(value string, algo SHAAlgo) string {
var h hash.Hash var dataHash hash.Hash
switch sa { switch algo {
case SHA1: case SHA1:
h = sha1.New() dataHash = sha1.New()
case SHA256: case SHA256:
h = sha256.New() dataHash = sha256.New()
case SHA512: case SHA512:
h = sha512.New() dataHash = sha512.New()
default: default:
return "" return ""
} }
_, err := io.WriteString(h, s) _, err := io.WriteString(dataHash, value)
if err != nil { if err != nil {
return "" return ""
} }
return fmt.Sprintf("%x", h.Sum(nil)) return fmt.Sprintf("%x", dataHash.Sum(nil))
} }