mirror of https://github.com/grafana/grafana
If notifications are enabled in the config, Adds a eventHandler accepting Notification{} payloads to the internal Bus. The eventHandler then marshals the payload into json and sends it to a rabbitmq topic exchange using the Notification.Priority+Noticiation.EventType as the routing key. eg. INFO.account.created Currently, notifications are only being emitted for INFO.account.created INFO.account.updated INFO.user.created INFO.user.updatedpull/1442/head
parent
68a6a1bc15
commit
a712f1a231
@ -1 +1 @@ |
|||||||
Subproject commit 7fef460fa2b6034429b205dc63d9b9f298f43fb4 |
Subproject commit 0fe83d51981333600f1e3801044fc1cfd5acf1ae |
@ -0,0 +1,21 @@ |
|||||||
|
package models |
||||||
|
|
||||||
|
import ( |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type EventPriority string |
||||||
|
|
||||||
|
const ( |
||||||
|
PRIO_DEBUG EventPriority = "DEBUG" |
||||||
|
PRIO_INFO EventPriority = "INFO" |
||||||
|
PRIO_ERROR EventPriority = "ERROR" |
||||||
|
) |
||||||
|
|
||||||
|
type Notification struct { |
||||||
|
EventType string `json:"event_type"` |
||||||
|
Timestamp time.Time `json:"timestamp"` |
||||||
|
Priority EventPriority `json:"priority"` |
||||||
|
Payload interface{} `json:"payload"` |
||||||
|
} |
||||||
|
|
@ -0,0 +1,130 @@ |
|||||||
|
package notification |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"time" |
||||||
|
"encoding/json" |
||||||
|
"github.com/streadway/amqp" |
||||||
|
"github.com/torkelo/grafana-pro/pkg/bus" |
||||||
|
m "github.com/torkelo/grafana-pro/pkg/models" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
url string |
||||||
|
exchange string |
||||||
|
conn *amqp.Connection |
||||||
|
channel *amqp.Channel |
||||||
|
) |
||||||
|
|
||||||
|
func getConnection() (*amqp.Connection, error) { |
||||||
|
c, err := amqp.Dial(url) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return c, err |
||||||
|
} |
||||||
|
|
||||||
|
func getChannel() (*amqp.Channel, error) { |
||||||
|
ch, err := conn.Channel() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
err = ch.ExchangeDeclare( |
||||||
|
exchange, // name
|
||||||
|
"topic", // type
|
||||||
|
true, // durable
|
||||||
|
false, // auto-deleted
|
||||||
|
false, // internal
|
||||||
|
false, // no-wait
|
||||||
|
nil, // arguments
|
||||||
|
) |
||||||
|
if (err != nil) { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return ch, err |
||||||
|
} |
||||||
|
|
||||||
|
func Init(rabbitUrl string, exchangeName string) error { |
||||||
|
url = rabbitUrl |
||||||
|
exchange = exchangeName |
||||||
|
bus.AddEventListener(NotificationHandler) |
||||||
|
return Setup() |
||||||
|
} |
||||||
|
|
||||||
|
// Every connection should declare the topology they expect
|
||||||
|
func Setup() error { |
||||||
|
c, err := getConnection() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
conn = c |
||||||
|
ch, err := getChannel() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
channel = ch |
||||||
|
|
||||||
|
// listen for close events so we can reconnect.
|
||||||
|
errChan := channel.NotifyClose(make(chan *amqp.Error)) |
||||||
|
go func() { |
||||||
|
for e := range errChan { |
||||||
|
fmt.Println("connection to rabbitmq lost.") |
||||||
|
fmt.Println(e) |
||||||
|
fmt.Println("attempting to create new rabbitmq channel.") |
||||||
|
ch, err := getChannel() |
||||||
|
if err == nil { |
||||||
|
channel = ch |
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
//could not create channel, so lets close the connection
|
||||||
|
// and re-create.
|
||||||
|
_ = conn.Close() |
||||||
|
|
||||||
|
for err != nil { |
||||||
|
time.Sleep(2 * time.Second) |
||||||
|
fmt.Println("attempting to reconnect to rabbitmq.") |
||||||
|
err = Setup() |
||||||
|
} |
||||||
|
fmt.Println("Connected to rabbitmq again.") |
||||||
|
} |
||||||
|
}() |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func Publish(routingKey string, msgString []byte) { |
||||||
|
err := channel.Publish( |
||||||
|
exchange, //exchange
|
||||||
|
routingKey, // routing key
|
||||||
|
false, // mandatory
|
||||||
|
false, // immediate
|
||||||
|
amqp.Publishing{ |
||||||
|
ContentType: "application/json", |
||||||
|
Body: msgString, |
||||||
|
}, |
||||||
|
) |
||||||
|
if err != nil { |
||||||
|
// failures are most likely because the connection was lost.
|
||||||
|
// the connection will be re-established, so just keep
|
||||||
|
// retrying every 2seconds until we successfully publish.
|
||||||
|
time.Sleep(2 * time.Second) |
||||||
|
fmt.Println("publish failed, retrying."); |
||||||
|
Publish(routingKey, msgString) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func NotificationHandler(event *m.Notification) error { |
||||||
|
msgString, err := json.Marshal(event) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
routingKey := fmt.Sprintf("%s.%s", event.Priority, event.EventType) |
||||||
|
// this is run in a greenthread and we expect that publish will keep
|
||||||
|
// retrying until the message gets sent.
|
||||||
|
go Publish(routingKey, msgString) |
||||||
|
return nil |
||||||
|
} |
Loading…
Reference in new issue