diff --git a/main.go b/main.go index 2c4bbce..9642f78 100644 --- a/main.go +++ b/main.go @@ -77,8 +77,8 @@ func GetOutboxPath(iniFile *ini.File) (string, error) { } func main() { - var logger log.Logger - logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) + logger := log.NewLogfmtLogger(os.Stdout) + logger = level.NewFilter(logger, level.AllowInfo()) logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller, "service", "mailsrv") iniFile, err := LoadIni() diff --git a/services/sender.go b/services/sender.go index b2cef34..31ea70a 100644 --- a/services/sender.go +++ b/services/sender.go @@ -1,6 +1,7 @@ package services import ( + "fmt" cfg "mailsrv/config" "mailsrv/mail" "mailsrv/runtime" @@ -19,6 +20,7 @@ import ( const ( TickerInterval time.Duration = 10 * time.Second JSONSuffix string = ".json" + ErrorSuffix string = ".err" ) type Sender struct { @@ -41,23 +43,25 @@ func NewSender(logger log.Logger, config cfg.SMTPConfig, outboxPath string) Send func (s Sender) SendMail(email mail.Email) error { auth := smtp.PlainAuth("", s.smtpConfig.User, s.smtpConfig.Password, s.smtpConfig.Url) - s.logger.Log("action", "authentication succeed") + level.Debug(s.logger).Log("msg", "SMTP authentication succeed") if err := smtp.SendMail(s.smtpConfig.GetFullUrl(), auth, email.Sender, email.Receivers, email.Generate()); err != nil { level.Error(s.logger).Log("msg", "error while sending email", "err", err) return err } - s.logger.Log("msg", "mail send successfully") + level.Debug(s.logger).Log("msg", "mail send successfully") return nil } // watchOutbox reads the `outbox` directory every `TickInterval` and put JSON format e-mail in the queue func (s Sender) watchOutbox() { + s.logger.Log("msg", "start watching outbox directory", "outbox", s.outboxPath) ticker := time.NewTicker(TickerInterval) + go func() { for _ = range ticker.C { - s.logger.Log("action", "retrieving json e-mail format...", "path", s.outboxPath) + level.Debug(s.logger).Log("action", "retrieving json e-mail format...", "path", s.outboxPath) files, err := os.ReadDir(s.outboxPath) if err != nil && !os.IsExist(err) { @@ -71,7 +75,7 @@ func (s Sender) watchOutbox() { s.queue.Add(path.Join(s.outboxPath, filename)) continue } - level.Warn(s.logger).Log("msg", "incorrect suffix", "filename", filename) + level.Debug(s.logger).Log("msg", "incorrect suffix", "filename", filename) } } }() @@ -87,20 +91,31 @@ func (s Sender) processNextEmail() bool { path, ok := item.(string) if !ok { - level.Error(s.logger).Log("msg", "unable to cast queue item into mail.Email") + level.Error(s.logger).Log("msg", "unable to cast queue item into mail.Email", "item", item) return true } email, err := mail.FromJSON(path) if err != nil { - level.Error(s.logger).Log("msg", "unable to parse JSON email", "err", err) + level.Error(s.logger).Log("msg", "unable to parse JSON email", "path", path, "err", err) + // if JSON parsing failed the `path` is renamed with an error suffix to avoid enqueued it again + newPath := fmt.Sprintf("%s%s", path, ErrorSuffix) + if err := os.Rename(path, newPath); err != nil { + level.Error(s.logger).Log("msg", "unable to rename bad JSON email path", "path", path, "newPath", newPath) + s.queue.Shutdown() + } return true } + // whatever the return, the email will be not enqueued again s.SendMail(email) if err := os.Remove(path); err != nil { - level.Error(s.logger).Log("msg", "unable to remove the JSON email", "path", path, "err", err) + // this is a fatal error, can't send same e-mail indefinitely + if !os.IsExist(err) { + level.Error(s.logger).Log("msg", "unable to remove the JSON email", "path", path, "err", err) + s.queue.Shutdown() + } } return true @@ -128,10 +143,14 @@ func (s Sender) Run() { s.watchOutbox() queueCh := s.run() - <-sigCh - s.logger.Log("msg", "stop signal received, stopping e-mail queue...") - s.queue.Shutdown() + select { + case <-sigCh: + s.logger.Log("msg", "stop signal received, stopping e-mail queue...") + s.queue.Shutdown() + case <-queueCh: + s.logger.Log("msg", "e-mail queue stopped successfully") + + } - <-queueCh s.logger.Log("msg", "sender service stopped successfully") }