github.com/mongodb/grip@v0.0.0-20240213223901-f906268d82b9/send/syslog.go (about)

     1  //go:build linux || freebsd || solaris || darwin
     2  // +build linux freebsd solaris darwin
     3  
     4  package send
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"log"
    11  	"log/syslog"
    12  	"os"
    13  
    14  	"github.com/mongodb/grip/level"
    15  	"github.com/mongodb/grip/message"
    16  )
    17  
    18  type syslogger struct {
    19  	logger *syslog.Writer
    20  	*Base
    21  }
    22  
    23  // NewSyslogLogger creates a new Sender object that writes all
    24  // loggable messages to a syslog instance on the specified
    25  // network. Uses the Go standard library syslog implementation that is
    26  // only available on Unix systems. Use this constructor to return a
    27  // connection to a remote Syslog interface, but will fall back first
    28  // to the local syslog interface before writing messages to standard
    29  // output.
    30  func NewSyslogLogger(name, network, raddr string, l LevelInfo) (Sender, error) {
    31  	return setup(MakeSysLogger(network, raddr), name, l)
    32  }
    33  
    34  // MakeSysLogger constructs a minimal and unconfigured logger that
    35  // posts to systemd's journal.
    36  // Pass to Journaler.SetSender or call SetName before using.
    37  func MakeSysLogger(network, raddr string) Sender {
    38  	s := &syslogger{Base: NewBase("")}
    39  
    40  	fallback := log.New(os.Stdout, "", log.LstdFlags)
    41  	_ = s.SetErrorHandler(ErrorHandlerFromLogger(fallback))
    42  
    43  	s.reset = func() {
    44  		fallback.SetPrefix(fmt.Sprintf("[%s] ", s.Name()))
    45  
    46  		if s.logger != nil {
    47  			if err := s.logger.Close(); err != nil {
    48  				s.ErrorHandler()(err, message.NewErrorWrapMessage(level.Error, err, "closing syslogger"))
    49  			}
    50  		}
    51  
    52  		w, err := syslog.Dial(network, raddr, syslog.LOG_DEBUG, s.Name())
    53  		if err != nil {
    54  			s.ErrorHandler()(err, message.NewErrorWrapMessage(level.Error, err, "restarting syslog for logger '%s'", s.Name()))
    55  			return
    56  		}
    57  
    58  		s.closer = func() error {
    59  			return w.Close()
    60  		}
    61  
    62  		s.logger = w
    63  	}
    64  
    65  	return s
    66  }
    67  
    68  // MakeLocalSyslogLogger is a constructor for creating the same kind of
    69  // Sender instance as NewSyslogLogger, except connecting directly to
    70  // the local syslog service. If there is no local syslog service, or
    71  // there are issues connecting to it, writes logging messages to
    72  // standard error. Pass to Journaler.SetSender or call SetName before using.
    73  func MakeLocalSyslogLogger() Sender {
    74  	return MakeSysLogger("", "")
    75  }
    76  
    77  func (s *syslogger) Close() error { return s.logger.Close() }
    78  
    79  func (s *syslogger) Send(m message.Composer) {
    80  	defer func() {
    81  		if err := recover(); err != nil {
    82  			s.ErrorHandler()(fmt.Errorf("panic: %v", err), m)
    83  		}
    84  	}()
    85  
    86  	if s.Level().ShouldLog(m) {
    87  		if err := s.sendToSysLog(m.Priority(), m.String()); err != nil {
    88  			s.ErrorHandler()(err, m)
    89  		}
    90  	}
    91  }
    92  
    93  func (s *syslogger) sendToSysLog(p level.Priority, message string) error {
    94  	switch p {
    95  	case level.Emergency:
    96  		return s.logger.Emerg(message)
    97  	case level.Alert:
    98  		return s.logger.Alert(message)
    99  	case level.Critical:
   100  		return s.logger.Crit(message)
   101  	case level.Error:
   102  		return s.logger.Err(message)
   103  	case level.Warning:
   104  		return s.logger.Warning(message)
   105  	case level.Notice:
   106  		return s.logger.Notice(message)
   107  	case level.Info:
   108  		return s.logger.Info(message)
   109  	case level.Debug, level.Trace:
   110  		return s.logger.Debug(message)
   111  	}
   112  
   113  	return errors.New("invalid priority")
   114  }
   115  
   116  func (s *syslogger) Flush(_ context.Context) error { return nil }