mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-21 21:30:48 +02:00
fix(notifications): default templates and logic (#1010)
* fix(notifications): default templates and logic * fix multi-entry report notifs and add test * add tests for log queueing
This commit is contained in:
parent
fc31c6eb26
commit
cd0ec88764
5 changed files with 195 additions and 57 deletions
|
@ -2,7 +2,6 @@ package notifications
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
@ -13,23 +12,33 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// LocalLog is a logrus logger that does not send entries as notifications
|
||||
var LocalLog = log.WithField("notify", "no")
|
||||
|
||||
const (
|
||||
shoutrrrDefaultLegacyTemplate = "{{range .}}{{.Message}}{{println}}{{end}}"
|
||||
shoutrrrDefaultTemplate = `{{- with .Report -}}
|
||||
shoutrrrDefaultTemplate = `
|
||||
{{- if .Report -}}
|
||||
{{- with .Report -}}
|
||||
{{- if ( or .Updated .Failed ) -}}
|
||||
{{len .Scanned}} Scanned, {{len .Updated}} Updated, {{len .Failed}} Failed
|
||||
{{range .Updated -}}
|
||||
{{- range .Updated}}
|
||||
- {{.Name}} ({{.ImageName}}): {{.CurrentImageID.ShortID}} updated to {{.LatestImageID.ShortID}}
|
||||
{{end -}}
|
||||
{{range .Fresh -}}
|
||||
{{- end -}}
|
||||
{{- range .Fresh}}
|
||||
- {{.Name}} ({{.ImageName}}): {{.State}}
|
||||
{{end -}}
|
||||
{{range .Skipped -}}
|
||||
{{- end -}}
|
||||
{{- range .Skipped}}
|
||||
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||
{{end -}}
|
||||
{{range .Failed -}}
|
||||
{{- end -}}
|
||||
{{- range .Failed}}
|
||||
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||
{{end -}}
|
||||
{{end -}}`
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{range .Entries -}}{{.Message}}{{"\n"}}{{- end -}}
|
||||
{{- end -}}`
|
||||
shoutrrrType = "shoutrrr"
|
||||
)
|
||||
|
||||
|
@ -47,6 +56,7 @@ type shoutrrrTypeNotifier struct {
|
|||
messages chan string
|
||||
done chan bool
|
||||
legacyTemplate bool
|
||||
params *types.Params
|
||||
}
|
||||
|
||||
// GetScheme returns the scheme part of a Shoutrrr URL
|
||||
|
@ -58,6 +68,7 @@ func GetScheme(url string) string {
|
|||
return url[:schemeEnd]
|
||||
}
|
||||
|
||||
// GetNames returns a list of notification services that has been added
|
||||
func (n *shoutrrrTypeNotifier) GetNames() []string {
|
||||
names := make([]string, len(n.Urls))
|
||||
for i, u := range n.Urls {
|
||||
|
@ -66,9 +77,10 @@ func (n *shoutrrrTypeNotifier) GetNames() []string {
|
|||
return names
|
||||
}
|
||||
|
||||
func newShoutrrrNotifier(tplString string, acceptedLogLevels []log.Level, legacy bool, urls ...string) t.Notifier {
|
||||
func newShoutrrrNotifier(tplString string, acceptedLogLevels []log.Level, legacy bool, title string, urls ...string) t.Notifier {
|
||||
|
||||
notifier := createNotifier(urls, acceptedLogLevels, tplString, legacy)
|
||||
notifier.params = &types.Params{"title": title}
|
||||
log.AddHook(notifier)
|
||||
|
||||
// Do the sending in a separate goroutine so we don't block the main process.
|
||||
|
@ -102,13 +114,16 @@ func createNotifier(urls []string, levels []log.Level, tplString string, legacy
|
|||
|
||||
func sendNotifications(n *shoutrrrTypeNotifier) {
|
||||
for msg := range n.messages {
|
||||
errs := n.Router.Send(msg, nil)
|
||||
errs := n.Router.Send(msg, n.params)
|
||||
|
||||
for i, err := range errs {
|
||||
if err != nil {
|
||||
scheme := GetScheme(n.Urls[i])
|
||||
// Use fmt so it doesn't trigger another notification.
|
||||
fmt.Printf("Failed to send shoutrrr notification (#%d, %s): %v\n", i, scheme, err)
|
||||
LocalLog.WithFields(log.Fields{
|
||||
"service": scheme,
|
||||
"index": i,
|
||||
}).WithError(err).Error("Failed to send shoutrrr notification")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,53 +131,70 @@ func sendNotifications(n *shoutrrrTypeNotifier) {
|
|||
n.done <- true
|
||||
}
|
||||
|
||||
func (n *shoutrrrTypeNotifier) buildMessage(data Data) string {
|
||||
func (n *shoutrrrTypeNotifier) buildMessage(data Data) (string, error) {
|
||||
var body bytes.Buffer
|
||||
var templateData interface{} = data
|
||||
if n.legacyTemplate {
|
||||
templateData = data.Entries
|
||||
}
|
||||
if err := n.template.Execute(&body, templateData); err != nil {
|
||||
fmt.Printf("Failed to execute Shoutrrrr template: %s\n", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
return body.String()
|
||||
return body.String(), nil
|
||||
}
|
||||
|
||||
func (n *shoutrrrTypeNotifier) sendEntries(entries []*log.Entry, report t.Report) {
|
||||
msg := n.buildMessage(Data{entries, report})
|
||||
msg, err := n.buildMessage(Data{entries, report})
|
||||
|
||||
if msg == "" {
|
||||
// Log in go func in case we entered from Fire to avoid stalling
|
||||
go func() {
|
||||
if err != nil {
|
||||
LocalLog.WithError(err).Fatal("Notification template error")
|
||||
} else {
|
||||
LocalLog.Info("Skipping notification due to empty message")
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
n.messages <- msg
|
||||
}
|
||||
|
||||
// StartNotification begins queueing up messages to send them as a batch
|
||||
func (n *shoutrrrTypeNotifier) StartNotification() {
|
||||
if n.entries == nil {
|
||||
n.entries = make([]*log.Entry, 0, 10)
|
||||
}
|
||||
}
|
||||
|
||||
// SendNotification sends the queued up messages as a notification
|
||||
func (n *shoutrrrTypeNotifier) SendNotification(report t.Report) {
|
||||
//if n.entries == nil || len(n.entries) <= 0 {
|
||||
// return
|
||||
//}
|
||||
|
||||
n.sendEntries(n.entries, report)
|
||||
n.entries = nil
|
||||
}
|
||||
|
||||
// Close prevents further messages from being queued and waits until all the currently queued up messages have been sent
|
||||
func (n *shoutrrrTypeNotifier) Close() {
|
||||
close(n.messages)
|
||||
|
||||
// Use fmt so it doesn't trigger another notification.
|
||||
fmt.Println("Waiting for the notification goroutine to finish")
|
||||
LocalLog.Info("Waiting for the notification goroutine to finish")
|
||||
|
||||
_ = <-n.done
|
||||
}
|
||||
|
||||
// Levels return what log levels trigger notifications
|
||||
func (n *shoutrrrTypeNotifier) Levels() []log.Level {
|
||||
return n.logLevels
|
||||
}
|
||||
|
||||
// Fire is the hook that logrus calls on a new log message
|
||||
func (n *shoutrrrTypeNotifier) Fire(entry *log.Entry) error {
|
||||
if entry.Data["notify"] == "no" {
|
||||
// Skip logging if explicitly tagged as non-notify
|
||||
return nil
|
||||
}
|
||||
if n.entries != nil {
|
||||
n.entries = append(n.entries, entry)
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue