chain_exporter/alerter/main.go

137 lines
3.4 KiB
Go

package main
import (
"fmt"
"github.com/certusone/chain_exporter/types"
"github.com/getsentry/raven-go"
"github.com/go-pg/pg"
"github.com/pkg/errors"
"os"
"os/signal"
"strconv"
"time"
)
type (
Monitor struct {
db *pg.DB
address string
}
)
func main() {
if os.Getenv("DB_HOST") == "" {
panic(errors.New("DB_HOST needs to be set"))
}
if os.Getenv("DB_USER") == "" {
panic(errors.New("DB_USER needs to be set"))
}
if os.Getenv("DB_PW") == "" {
panic(errors.New("DB_PW needs to be set"))
}
if os.Getenv("RAVEN_DSN") == "" {
panic(errors.New("RAVEN_DSN needs to be set"))
}
if os.Getenv("ADDRESS") == "" {
panic(errors.New("ADDRESS needs to be set"))
}
// Set Raven URL for alerts
raven.SetDSN(os.Getenv("RAVEN_DSN"))
// Connect to the postgres datastore
db := pg.Connect(&pg.Options{
Addr: os.Getenv("DB_HOST"),
User: os.Getenv("DB_USER"),
Password: os.Getenv("DB_PW"),
})
defer db.Close()
// Start the monitor
monitor := &Monitor{db, os.Getenv("ADDRESS")}
go func() {
for range time.Tick(time.Second) {
fmt.Println("start - alerting on misses")
err := monitor.AlertMisses()
if err != nil {
fmt.Printf("error - alerting on misses: %v\n", err)
}
fmt.Println("finish - alerting on misses")
}
}()
go func() {
for range time.Tick(time.Second) {
fmt.Println("start - alerting on governance")
err := monitor.AlertGovernance()
if err != nil {
fmt.Printf("error - alerting on governance: %v\n", err)
}
fmt.Println("finish - alerting on governance")
}
}()
// Allow graceful closing of the process
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, os.Interrupt)
<-signalCh
}
// AlertMisses queries misses from the database and sends the relevant alert to sentry
func (m *Monitor) AlertMisses() error {
// Query block misses from the DB
var misses []*types.MissInfo
err := m.db.Model(&types.MissInfo{}).Where("alerted = FALSE and address = ?", m.address).Select(&misses)
if err != nil {
return err
}
// Iterate misses and send alerts
for _, miss := range misses {
raven.CaptureMessage("Missed block", map[string]string{"height": strconv.FormatInt(miss.Height, 10), "time": miss.Time.String(), "address": miss.Address})
// Mark miss as alerted in the db
miss.Alerted = true
_, err = m.db.Model(miss).Where("id = ?", miss.ID).Update()
if err != nil {
return err
}
fmt.Printf("alerted on miss #height: %d\n", miss.Height)
}
return nil
}
// AlertGovernance queries active governance proposals from the database and sends the relevant alert to sentry
func (m *Monitor) AlertGovernance() error {
// Query proposals from the DB
var proposals []*types.Proposal
err := m.db.Model(&types.Proposal{}).
Where("alerted = FALSE and proposal_status = ?", "Active").
Select(&proposals)
if err != nil {
return err
}
// Send alerts for every proposal
for _, proposal := range proposals {
raven.CaptureMessage(fmt.Sprintf("New governance proposal: %s\nDescription: %s\nStartHeight: %s", proposal.Title, proposal.Description, proposal.VotingStartBlock),
map[string]string{
"height": strconv.FormatInt(proposal.Height, 10),
"type": proposal.Type,
})
// Mark proposal as alerted in the db
proposal.Alerted = true
_, err = m.db.Model(proposal).Where("id = ?", proposal.ID).Update()
if err != nil {
return err
}
fmt.Printf("alerted on proposal #%s\n", proposal.ID)
}
return nil
}