diff --git a/README.md b/README.md index 6c3fee0..5ff6b4c 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ However, multiple signals can map to the same channel. The channel type `notify` is for sending messages to popular messaging platforms. +You must specify a URL and, optionally, a message template. + `orb-ag` uses `shoutrrr` for sending notifications. Use the following URL formats for these different services. Additional details are available from the [`shoutrrr` @@ -113,7 +115,29 @@ documentation](https://containrrr.dev/shoutrrr/v0.8/services/overview/). | Telegram | `telegram://token@telegram?chats=@channel-1[,chat-id-1,...]` | | Zulip Chat | `zulip://bot-mail:bot-key@zulip-domain/?stream=name-or-id&topic=name` | -The URL format for generic webhooks is described at +URLs are actually go templates that are processed before use. Data +that you can provide to the template engine includes: +- `.Timestamp` : the RFC3339 formatted timestamp for the matching log entry +- `.PID`: the process ID for the observed process +- `.Logline`: the matching log line + +Similarly, the message sent may also be a template. If no `template` +is specified in the channel definition, then the logline is used as +the message. If a `template` is specified, then the template is +processed with the same data as above before sending. + +As an example, here's a channel that sends an email containing json +data, and the observed PID is in the email subject line: + +``` + - name: "email-on-startup" + type: "notify" + url: "smtp://EMAIL@gmail.com:PASSWORD@smtp.gmail.com:587/?from=EMAIL@gmail.com&to=EMAIL@gmail.com&subject=Starting%20process%20{{.PID}}!" + template: "{ \"timestamp\": \"{{.Timestamp}}\", \"message\": \"{{.Logline}}\" }" +``` + +Generic webhooks and handled specially by `shoutrrr`. Their URL +format is described at [https://containrrr.dev/shoutrrr/v0.8/services/generic/](https://containrrr.dev/shoutrrr/v0.8/services/generic/). ### Sending Kafka messages diff --git a/orb-ag.go b/orb-ag.go index e08b2dc..4522df7 100644 --- a/orb-ag.go +++ b/orb-ag.go @@ -41,6 +41,7 @@ import ( "regexp" "sync" "syscall" + "text/template" "time" ) @@ -49,12 +50,13 @@ var restart bool = true var observed_cmd *exec.Cmd type Channel struct { - Name string `yaml:"name"` - Type string `yaml:"type"` - URL string `yaml:"url"` - Topic string `yaml:"topic"` - Broker string `yaml:"broker"` - Shell string `yaml:"shell"` + Name string `yaml:"name"` + Type string `yaml:"type"` + URL string `yaml:"url"` + Template string `yaml:"template"` + Topic string `yaml:"topic"` + Broker string `yaml:"broker"` + Shell string `yaml:"shell"` } type Signal struct { @@ -120,6 +122,12 @@ type Notification struct { Message string } +type TemplateData struct { + PID int + Logline string + Timestamp string +} + func loadYAMLConfig(filename string, config *Config) error { bytes, err := ioutil.ReadFile(filename) if err != nil { @@ -135,14 +143,44 @@ func loadYAMLConfig(filename string, config *Config) error { } func startWorkers(notificationQueue <-chan Notification, numWorkers int, wg *sync.WaitGroup) { + var messageString string + for i := 0; i < numWorkers; i++ { wg.Add(1) go func(workerID int) { defer wg.Done() for notification := range notificationQueue { + td := TemplateData{PID: notification.PID, + Logline: notification.Message, + Timestamp: time.Now().Format(time.RFC3339)} switch notification.Channel.Type { case "notify": - err := shoutrrr.Send(notification.Channel.URL, notification.Message) + tmpl, err := template.New("url").Parse(notification.Channel.URL) + if err != nil { + log.Fatal("org-ab error: can't parse URL template: ", err) + } + var buffer bytes.Buffer + err = tmpl.Execute(&buffer, td) + if err != nil { + log.Fatal("org-ab error: can't execute URL template: ", err) + } + urlString := buffer.String() + if notification.Channel.Template != "" { + tmpl, err := template.New("msg").Parse(notification.Channel.Template) + if err != nil { + log.Fatal("org-ab error: can't parse template: ", err) + } + var buffer bytes.Buffer + err = tmpl.Execute(&buffer, td) + if err != nil { + log.Fatal("org-ab error: can't execute URL template: ", err) + } + messageString = buffer.String() + } else { + messageString = notification.Message + } + + err = shoutrrr.Send(urlString, messageString) if err != nil { log.Println("org-ab warning: failed sending notification: ", err) }