mirror of
https://github.com/containrrr/watchtower.git
synced 2025-01-17 18:26:19 +02:00
feat: add template support for shoutrrr notifications (#515)
This commit is contained in:
parent
7052346570
commit
10fd81a2c1
@ -179,6 +179,14 @@ To send notifications via shoutrrr, the following command-line options, or their
|
||||
Go to [containrrr.github.io/shoutrrr/services/overview](https://containrrr.github.io/shoutrrr/services/overview) to learn more about the different service URLs you can use.
|
||||
You can define multiple services by space separating the URLs. (See example below)
|
||||
|
||||
You can customize the message posted by setting a template.
|
||||
|
||||
- `--notification-template` (env. `WATCHTOWER_NOTIFICATION_TEMPLATE`): The template used for the message.
|
||||
|
||||
The template is a Go [template](https://golang.org/pkg/text/template/) and the you format a list of [log entries](https://pkg.go.dev/github.com/sirupsen/logrus?tab=doc#Entry).
|
||||
|
||||
The default value if not set is `{{range .}}{{.Message}}{{println}}{{end}}`. The example below uses a template that also outputs timestamp and log level.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
@ -187,5 +195,6 @@ docker run -d \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-e WATCHTOWER_NOTIFICATIONS=shoutrrr \
|
||||
-e WATCHTOWER_NOTIFICATION_URL="discord://token@channel slack://watchtower@token-a/token-b/token-c" \
|
||||
-e WATCHTOWER_NOTIFICATION_TEMPLATE="{{range .}}{{.Time.Format \"2006-01-02 15:04:05\"}} ({{.Level}}): {{.Message}}{{println}}{{end}}" \
|
||||
containrrr/watchtower
|
||||
```
|
@ -259,6 +259,12 @@ Should only be used for testing.
|
||||
viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN"),
|
||||
"The Gotify Application required to query the Gotify API")
|
||||
|
||||
flags.StringP(
|
||||
"notification-template",
|
||||
"",
|
||||
viper.GetString("WATCHTOWER_NOTIFICATION_TEMPLATE"),
|
||||
"The shoutrrr text/template for the messages")
|
||||
|
||||
flags.StringArrayP(
|
||||
"notification-url",
|
||||
"",
|
||||
|
@ -1,7 +1,10 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/containrrr/shoutrrr"
|
||||
"github.com/containrrr/shoutrrr/pkg/router"
|
||||
t "github.com/containrrr/watchtower/pkg/types"
|
||||
@ -10,7 +13,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
shoutrrrType = "shoutrrr"
|
||||
shoutrrrDefaultTemplate = "{{range .}}{{.Message}}{{println}}{{end}}"
|
||||
shoutrrrType = "shoutrrr"
|
||||
)
|
||||
|
||||
// Implements Notifier, logrus.Hook
|
||||
@ -19,6 +23,7 @@ type shoutrrrTypeNotifier struct {
|
||||
Router *router.ServiceRouter
|
||||
entries []*log.Entry
|
||||
logLevels []log.Level
|
||||
template *template.Template
|
||||
}
|
||||
|
||||
func newShoutrrrNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier {
|
||||
@ -31,6 +36,7 @@ func newShoutrrrNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Noti
|
||||
Urls: urls,
|
||||
Router: r,
|
||||
logLevels: acceptedLogLevels,
|
||||
template: getShoutrrrTemplate(c),
|
||||
}
|
||||
|
||||
log.AddHook(n)
|
||||
@ -39,13 +45,10 @@ func newShoutrrrNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Noti
|
||||
}
|
||||
|
||||
func (e *shoutrrrTypeNotifier) buildMessage(entries []*log.Entry) string {
|
||||
body := ""
|
||||
for _, entry := range entries {
|
||||
body += entry.Time.Format("2006-01-02 15:04:05") + " (" + entry.Level.String() + "): " + entry.Message + "\r\n"
|
||||
// We don't use fields in watchtower, so don't bother sending them.
|
||||
}
|
||||
var body bytes.Buffer
|
||||
e.template.Execute(&body, entries)
|
||||
|
||||
return body
|
||||
return body.String()
|
||||
}
|
||||
|
||||
func (e *shoutrrrTypeNotifier) sendEntries(entries []*log.Entry) {
|
||||
@ -91,3 +94,35 @@ func (e *shoutrrrTypeNotifier) Fire(entry *log.Entry) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getShoutrrrTemplate(c *cobra.Command) *template.Template {
|
||||
var tpl *template.Template
|
||||
|
||||
flags := c.PersistentFlags()
|
||||
|
||||
tplString, err := flags.GetString("notification-template")
|
||||
|
||||
// If we succeed in getting a non-empty template configuration
|
||||
// try to parse the template string.
|
||||
if tplString != "" && err == nil {
|
||||
tpl, err = template.New("").Parse(tplString)
|
||||
}
|
||||
|
||||
// In case of errors (either from parsing the template string
|
||||
// or from getting the template configuration) log an error
|
||||
// message about this and the fact that we'll use the default
|
||||
// template instead.
|
||||
if err != nil {
|
||||
log.Errorf("Could not use configured notification template: %s. Using default template", err)
|
||||
}
|
||||
|
||||
// If we had an error (either from parsing the template string
|
||||
// or from getting the template configuration) or we a
|
||||
// template wasn't configured (the empty template string)
|
||||
// fallback to using the default template.
|
||||
if err != nil || tplString == "" {
|
||||
tpl = template.Must(template.New("").Parse(shoutrrrDefaultTemplate))
|
||||
}
|
||||
|
||||
return tpl
|
||||
}
|
||||
|
80
pkg/notifications/shoutrrr_test.go
Normal file
80
pkg/notifications/shoutrrr_test.go
Normal file
@ -0,0 +1,80 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/containrrr/watchtower/internal/flags"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestShoutrrrDefaultTemplate(t *testing.T) {
|
||||
cmd := new(cobra.Command)
|
||||
|
||||
shoutrrr := &shoutrrrTypeNotifier{
|
||||
template: getShoutrrrTemplate(cmd),
|
||||
}
|
||||
|
||||
entries := []*log.Entry{
|
||||
{
|
||||
Message: "foo bar",
|
||||
},
|
||||
}
|
||||
|
||||
s := shoutrrr.buildMessage(entries)
|
||||
|
||||
require.Equal(t, "foo bar\n", s)
|
||||
}
|
||||
|
||||
func TestShoutrrrTemplate(t *testing.T) {
|
||||
cmd := new(cobra.Command)
|
||||
flags.RegisterNotificationFlags(cmd)
|
||||
err := cmd.ParseFlags([]string{"--notification-template={{range .}}{{.Level}}: {{.Message}}{{println}}{{end}}"})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
shoutrrr := &shoutrrrTypeNotifier{
|
||||
template: getShoutrrrTemplate(cmd),
|
||||
}
|
||||
|
||||
entries := []*log.Entry{
|
||||
{
|
||||
Level: log.InfoLevel,
|
||||
Message: "foo bar",
|
||||
},
|
||||
}
|
||||
|
||||
s := shoutrrr.buildMessage(entries)
|
||||
|
||||
require.Equal(t, "info: foo bar\n", s)
|
||||
}
|
||||
|
||||
func TestShoutrrrInvalidTemplateUsesTemplate(t *testing.T) {
|
||||
cmd := new(cobra.Command)
|
||||
|
||||
flags.RegisterNotificationFlags(cmd)
|
||||
err := cmd.ParseFlags([]string{"--notification-template={{"})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
shoutrrr := &shoutrrrTypeNotifier{
|
||||
template: getShoutrrrTemplate(cmd),
|
||||
}
|
||||
|
||||
shoutrrrDefault := &shoutrrrTypeNotifier{
|
||||
template: template.Must(template.New("").Parse(shoutrrrDefaultTemplate)),
|
||||
}
|
||||
|
||||
entries := []*log.Entry{
|
||||
{
|
||||
Message: "foo bar",
|
||||
},
|
||||
}
|
||||
|
||||
s := shoutrrr.buildMessage(entries)
|
||||
sd := shoutrrrDefault.buildMessage(entries)
|
||||
|
||||
require.Equal(t, sd, s)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user