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 }