Skip to content

Commit

Permalink
feat: add alert feature
Browse files Browse the repository at this point in the history
  • Loading branch information
macrat committed Apr 15, 2021
1 parent 1b97a22 commit 86f55f1
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 13 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ Easiest status page to check something service is dead:skull: or alive:heavy_che

## Features

- status checking with
- status checking with:
* HTTP/HTTPS
* ICMP echo (ping)
* TCP connect
* DNS resolve
* execute external command (or script file)
- view status page in browser.
- view status page via cURL.
- kick alert if target failure.

### Good at
- Make status page for temprary usage. (You can start it via one command! And, stop via just Ctrl-C!)
Expand All @@ -21,4 +22,3 @@ Easiest status page to check something service is dead:skull: or alive:heavy_che
### Not good at
- Complex customize, extension. (There is nothing options for customize.)
- Investigate more detail. (This is just for check dead or alive.)
- Alert sending. (This is just status page.)
70 changes: 70 additions & 0 deletions alert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"net/url"
"time"

"github.com/macrat/ayd/probe"
"github.com/macrat/ayd/store"
)

type Alert struct {
target *url.URL
}

func NewAlert(target string) (*Alert, error) {
p, err := probe.Get(target)
if err != nil {
return nil, err
}

return &Alert{p.Target()}, nil
}

func (a *Alert) Target() *url.URL {
return &url.URL{
Scheme: "alert",
Opaque: a.target.String(),
}
}

func (a *Alert) Trigger(r store.Record) []store.Record {
qs := a.target.Query()
qs.Set("ayd_target", r.Target.String())
qs.Set("ayd_checked_at", r.CheckedAt.Format(time.RFC3339))
qs.Set("ayd_status", r.Status.String())

u := *a.target
u.RawQuery = qs.Encode()

p, err := probe.GetByURL(&u)
if err != nil {
return []store.Record{{
CheckedAt: time.Now(),
Target: a.Target(),
Status: store.STATUS_UNKNOWN,
Message: err.Error(),
}}
}

result := p.Check()
for i := range result {
result[i].Target = a.Target()
}

return result
}

func (a *Alert) TriggerIfNeed(rs []store.Record) []store.Record {
if a == nil {
return nil
}

var result []store.Record
for _, r := range rs {
if r.Status != store.STATUS_HEALTHY {
result = append(result, a.Trigger(r)...)
}
}
return result
}
24 changes: 19 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var (
listenPort = flag.Int("p", 9000, "Listen port of status page.")
storePath = flag.String("o", "./ayd.log", "Path to log file. Log file is also use for restore status history.")
oneshot = flag.Bool("1", false, "Check status only once and exit. Exit with 0 if all check passed, otherwise exit with code 1.")
alertURI = flag.String("a", "", "The alert URI that the same format as target URI.")
)

func Usage() {
Expand Down Expand Up @@ -155,7 +156,7 @@ func ParseArgs(args []string) ([]Task, []error) {
return result, errors
}

func RunOneshot(tasks []Task) {
func RunOneshot(tasks []Task, alert *Alert) {
var failed atomic.Value

s, err := store.New(*storePath)
Expand All @@ -173,6 +174,7 @@ func RunOneshot(tasks []Task) {
go func() {
rs := f()
s.Append(rs...)
s.Append(alert.TriggerIfNeed(rs)...)
for _, r := range rs {
if r.Status == store.STATUS_FAILURE {
failed.Store(true)
Expand All @@ -188,7 +190,7 @@ func RunOneshot(tasks []Task) {
}
}

func RunServer(tasks []Task) {
func RunServer(tasks []Task, alert *Alert) {
listen := fmt.Sprintf("0.0.0.0:%d", *listenPort)
fmt.Printf("starts Ayd on http://%s\n", listen)

Expand All @@ -212,7 +214,9 @@ func RunServer(tasks []Task) {

f := t.Probe.Check
job := func() {
s.Append(f()...)
rs := f()
s.Append(rs...)
s.Append(alert.TriggerIfNeed(rs)...)
}

if t.Schedule.NeedKickWhenStart() {
Expand All @@ -234,6 +238,16 @@ func main() {
flag.Usage = Usage
flag.Parse()

var alert *Alert
if *alertURI != "" {
var err error
alert, err = NewAlert(*alertURI)
if err != nil {
fmt.Fprintln(os.Stderr, "Invalid alert target:", err)
os.Exit(2)
}
}

tasks, errors := ParseArgs(flag.Args())
if errors != nil {
fmt.Fprintln(os.Stderr, "Invalid argument:")
Expand All @@ -249,8 +263,8 @@ func main() {
}

if *oneshot {
RunOneshot(tasks)
RunOneshot(tasks, alert)
} else {
RunServer(tasks)
RunServer(tasks, alert)
}
}
14 changes: 8 additions & 6 deletions store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@ func (s *Store) Append(rs ...Record) {
fmt.Println(str)
_, s.lastError = fmt.Fprintln(s.file, str)

s.ProbeHistory.append(r)

s.setIncidentIfNeed(r)
if r.Target.Scheme != "alert" {
s.ProbeHistory.append(r)
s.setIncidentIfNeed(r)
}
}
}

Expand All @@ -151,9 +152,10 @@ func (s *Store) Restore() error {
continue
}

s.ProbeHistory.append(r)

s.setIncidentIfNeed(r)
if r.Target.Scheme != "alert" {
s.ProbeHistory.append(r)
s.setIncidentIfNeed(r)
}
}

return nil
Expand Down

0 comments on commit 86f55f1

Please sign in to comment.