diff --git a/data.go b/data.go index ea0d1fe..368edb7 100644 --- a/data.go +++ b/data.go @@ -4,7 +4,7 @@ import "time" // UIData represents data to be passed to UI. type UIData struct { - Services Services + Services []*Service Vars []VarName LastTimestamp time.Time } @@ -15,17 +15,3 @@ func NewUIData(vars []VarName) *UIData { 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 -} diff --git a/main.go b/main.go index c03b412..96e6216 100644 --- a/main.go +++ b/main.go @@ -3,17 +3,17 @@ package main import ( "flag" "log" + "sync" "time" "github.com/divan/termui" ) var ( - 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") - defaultVars = 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") + interval = flag.Duration("i", 5*time.Second, "Polling interval") + portsArg = flag.String("ports", "1234", "Ports for accessing services expvars") + varsArg = flag.String("vars", "memstats.Alloc,memstats.Sys", "Default vars to monitor") + dummy = flag.Bool("dummy", false, "Use dummy (console) output") ) func main() { @@ -23,7 +23,7 @@ func main() { log.Fatal("cannot parse ports:", err) } - vars, err := ParseVars(*defaultVars, *extraVars) + vars, err := ParseVars(*varsArg) if err != nil { log.Fatal(err) } @@ -46,25 +46,11 @@ func main() { tick := time.NewTicker(*interval) evtCh := termui.EventCh() - update := func() { - for _, port := range ports { - service := data.FindService(port) - if service == nil { - continue - } - - service.Update() - } - - data.LastTimestamp = time.Now() - - ui.Update(*data) - } - update() + UpdateAll(ui, data) for { select { case <-tick.C: - update() + UpdateAll(ui, data) case e := <-evtCh: if e.Type == termui.EventKey && e.Ch == 'q' { 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) +} diff --git a/service.go b/service.go index dc0a052..474e7d6 100644 --- a/service.go +++ b/service.go @@ -5,11 +5,9 @@ import ( "net" "strconv" "strings" + "sync" ) -// Services is just a slice of Service. -type Services []*Service - // Service represents constantly updating info about single service. type Service struct { Port string @@ -36,7 +34,8 @@ func NewService(port string, vars []VarName) *Service { } // 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()) s.Err = err diff --git a/ui_termui.go b/ui_termui.go index 46ceea7..2ff8602 100644 --- a/ui_termui.go +++ b/ui_termui.go @@ -74,7 +74,7 @@ func (t *TermUI) Init(data UIData) error { s := termui.NewSparklines(sparklines...) s.Height = 2*len(data.Services) + 2 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 }() diff --git a/utils.go b/utils.go index 0c38c85..8db6f5c 100644 --- a/utils.go +++ b/utils.go @@ -7,22 +7,15 @@ import ( // ParseVars returns parsed and validated slice of strings with // variables names that will be used for monitoring. -func ParseVars(def, extra string) ([]VarName, error) { - if def == "" && extra == "" { +func ParseVars(vars string) ([]VarName, error) { + if vars == "" { return nil, errors.New("no vars specified") } - fields := func(s string) []VarName { - ss := strings.FieldsFunc(s, func(r rune) bool { return r == ',' }) - ret := []VarName{} - for _, str := range ss { - ret = append(ret, VarName(str)) - } - return ret - } - + ss := strings.FieldsFunc(vars, func(r rune) bool { return r == ',' }) var ret []VarName - ret = append(ret, fields(def)...) - ret = append(ret, fields(extra)...) + for _, s := range ss { + ret = append(ret, VarName(s)) + } return ret, nil } diff --git a/utils_test.go b/utils_test.go index 0c44730..dc1b4c1 100644 --- a/utils_test.go +++ b/utils_test.go @@ -3,10 +3,9 @@ package main import "testing" func TestUtils(t *testing.T) { - def := "memstats.Alloc,memstats.Sys" - extra := "" + str := "memstats.Alloc,memstats.Sys" - vars, err := ParseVars(def, extra) + vars, err := ParseVars(str) if err != nil { 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)) } - def = "memstats.Alloc,memstats.Sys" - extra = "goroutines,counter.A" + str = "memstats.Alloc,memstats.Sys,goroutines,Counter.A" - vars, err = ParseVars(def, extra) + vars, err = ParseVars(str) if err != nil { t.Fatalf("Err not nil: %v", err) } diff --git a/var_test.go b/var_test.go index c496b58..343e10e 100644 --- a/var_test.go +++ b/var_test.go @@ -11,4 +11,9 @@ func TestVarName(t *testing.T) { if len(slice) != 2 || slice[0] != "memstats" || slice[1] != "Alloc" { t.Fatalf("ToSlice failed: %v", slice) } + + short := v.Short() + if short != "Alloc" { + t.Fatalf("Expecting Short() to be 'Alloc', but got: %s", short) + } }