1
0
mirror of https://github.com/divan/expvarmon.git synced 2025-04-25 13:48:54 +08:00

Code cleanups

This commit is contained in:
Ivan Daniluk 2015-05-01 20:12:23 +03:00
parent 0114f247c8
commit 41c8dfdc9a
7 changed files with 42 additions and 61 deletions

16
data.go
View File

@ -4,7 +4,7 @@ import "time"
// UIData represents data to be passed to UI. // UIData represents data to be passed to UI.
type UIData struct { type UIData struct {
Services Services Services []*Service
Vars []VarName Vars []VarName
LastTimestamp time.Time LastTimestamp time.Time
} }
@ -15,17 +15,3 @@ func NewUIData(vars []VarName) *UIData {
Vars: vars, Vars: vars,
} }
} }
// FindService returns existing service by port.
func (d *UIData) FindService(port string) *Service {
if d.Services == nil {
return nil
}
for _, service := range d.Services {
if service.Port == port {
return service
}
}
return nil
}

40
main.go
View File

@ -3,6 +3,7 @@ package main
import ( import (
"flag" "flag"
"log" "log"
"sync"
"time" "time"
"github.com/divan/termui" "github.com/divan/termui"
@ -10,9 +11,8 @@ import (
var ( var (
interval = flag.Duration("i", 5*time.Second, "Polling interval") interval = flag.Duration("i", 5*time.Second, "Polling interval")
portsArg = flag.String("ports", "40001,40002,40000,40004,1233,1234,1235", "Ports for accessing services expvars") portsArg = flag.String("ports", "1234", "Ports for accessing services expvars")
defaultVars = flag.String("vars", "memstats.Alloc,memstats.Sys", "Default vars to monitor") varsArg = flag.String("vars", "memstats.Alloc,memstats.Sys", "Default vars to monitor")
extraVars = flag.String("extravars", "", "Comma-separated extra vars exported with expvars")
dummy = flag.Bool("dummy", false, "Use dummy (console) output") dummy = flag.Bool("dummy", false, "Use dummy (console) output")
) )
@ -23,7 +23,7 @@ func main() {
log.Fatal("cannot parse ports:", err) log.Fatal("cannot parse ports:", err)
} }
vars, err := ParseVars(*defaultVars, *extraVars) vars, err := ParseVars(*varsArg)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -46,25 +46,11 @@ func main() {
tick := time.NewTicker(*interval) tick := time.NewTicker(*interval)
evtCh := termui.EventCh() evtCh := termui.EventCh()
update := func() { UpdateAll(ui, data)
for _, port := range ports {
service := data.FindService(port)
if service == nil {
continue
}
service.Update()
}
data.LastTimestamp = time.Now()
ui.Update(*data)
}
update()
for { for {
select { select {
case <-tick.C: case <-tick.C:
update() UpdateAll(ui, data)
case e := <-evtCh: case e := <-evtCh:
if e.Type == termui.EventKey && e.Ch == 'q' { if e.Type == termui.EventKey && e.Ch == 'q' {
return return
@ -76,3 +62,17 @@ func main() {
} }
} }
} }
// UpdateAll collects data from expvars and refreshes UI.
func UpdateAll(ui UI, data *UIData) {
var wg sync.WaitGroup
for _, service := range data.Services {
wg.Add(1)
go service.Update(&wg)
}
wg.Wait()
data.LastTimestamp = time.Now()
ui.Update(*data)
}

View File

@ -5,11 +5,9 @@ import (
"net" "net"
"strconv" "strconv"
"strings" "strings"
"sync"
) )
// Services is just a slice of Service.
type Services []*Service
// Service represents constantly updating info about single service. // Service represents constantly updating info about single service.
type Service struct { type Service struct {
Port string Port string
@ -36,7 +34,8 @@ func NewService(port string, vars []VarName) *Service {
} }
// Update updates Service info from Expvar variable. // Update updates Service info from Expvar variable.
func (s *Service) Update() { func (s *Service) Update(wg *sync.WaitGroup) {
defer wg.Done()
expvar, err := FetchExpvar(s.Addr()) expvar, err := FetchExpvar(s.Addr())
s.Err = err s.Err = err

View File

@ -74,7 +74,7 @@ func (t *TermUI) Init(data UIData) error {
s := termui.NewSparklines(sparklines...) s := termui.NewSparklines(sparklines...)
s.Height = 2*len(data.Services) + 2 s.Height = 2*len(data.Services) + 2
s.HasBorder = true s.HasBorder = true
s.Border.Label = fmt.Sprintf("Sparklines for %s", data.Vars[0]) s.Border.Label = fmt.Sprintf("Monitoring %s", data.Vars[0])
return s return s
}() }()

View File

@ -7,22 +7,15 @@ import (
// ParseVars returns parsed and validated slice of strings with // ParseVars returns parsed and validated slice of strings with
// variables names that will be used for monitoring. // variables names that will be used for monitoring.
func ParseVars(def, extra string) ([]VarName, error) { func ParseVars(vars string) ([]VarName, error) {
if def == "" && extra == "" { if vars == "" {
return nil, errors.New("no vars specified") return nil, errors.New("no vars specified")
} }
fields := func(s string) []VarName { ss := strings.FieldsFunc(vars, func(r rune) bool { return r == ',' })
ss := strings.FieldsFunc(s, func(r rune) bool { return r == ',' })
ret := []VarName{}
for _, str := range ss {
ret = append(ret, VarName(str))
}
return ret
}
var ret []VarName var ret []VarName
ret = append(ret, fields(def)...) for _, s := range ss {
ret = append(ret, fields(extra)...) ret = append(ret, VarName(s))
}
return ret, nil return ret, nil
} }

View File

@ -3,10 +3,9 @@ package main
import "testing" import "testing"
func TestUtils(t *testing.T) { func TestUtils(t *testing.T) {
def := "memstats.Alloc,memstats.Sys" str := "memstats.Alloc,memstats.Sys"
extra := ""
vars, err := ParseVars(def, extra) vars, err := ParseVars(str)
if err != nil { if err != nil {
t.Fatalf("Err not nil: %v", err) t.Fatalf("Err not nil: %v", err)
} }
@ -15,10 +14,9 @@ func TestUtils(t *testing.T) {
t.Fatalf("vars should contain 2 elements, but has %d", len(vars)) t.Fatalf("vars should contain 2 elements, but has %d", len(vars))
} }
def = "memstats.Alloc,memstats.Sys" str = "memstats.Alloc,memstats.Sys,goroutines,Counter.A"
extra = "goroutines,counter.A"
vars, err = ParseVars(def, extra) vars, err = ParseVars(str)
if err != nil { if err != nil {
t.Fatalf("Err not nil: %v", err) t.Fatalf("Err not nil: %v", err)
} }

View File

@ -11,4 +11,9 @@ func TestVarName(t *testing.T) {
if len(slice) != 2 || slice[0] != "memstats" || slice[1] != "Alloc" { if len(slice) != 2 || slice[0] != "memstats" || slice[1] != "Alloc" {
t.Fatalf("ToSlice failed: %v", slice) t.Fatalf("ToSlice failed: %v", slice)
} }
short := v.Short()
if short != "Alloc" {
t.Fatalf("Expecting Short() to be 'Alloc', but got: %s", short)
}
} }