Skip to content

Commit 414dfa4

Browse files
committed
basic stats implementation
1 parent c135872 commit 414dfa4

File tree

3 files changed

+207
-0
lines changed

3 files changed

+207
-0
lines changed

beanstool.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package main
2+
3+
import (
4+
"os"
5+
6+
"github.com/tyba/beanstool/cli"
7+
8+
"github.com/jessevdk/go-flags"
9+
)
10+
11+
func main() {
12+
parser := flags.NewNamedParser("event-relay", flags.Default)
13+
parser.AddCommand("stats", "print stats on all tubes", "", &cli.Monitor{})
14+
15+
_, err := parser.Parse()
16+
if err != nil {
17+
if _, ok := err.(*flags.Error); ok {
18+
parser.WriteHelp(os.Stdout)
19+
}
20+
21+
os.Exit(1)
22+
}
23+
}

cli/monitor.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package cli
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/agtorre/gocolorize"
10+
"github.com/kr/beanstalk"
11+
"github.com/mcuadros/termtable"
12+
)
13+
14+
const (
15+
HighSeverity = iota
16+
NormalSeverity
17+
LowSeverity
18+
)
19+
20+
var TubeStatsRetrievalError = errors.New("Unable to retrieve tube stats")
21+
var HighSeverityStyle = gocolorize.NewColor("white:red")
22+
var NormalSeverityStyle = gocolorize.NewColor("green")
23+
24+
type Monitor struct {
25+
Host string `short:"h" long:"host" description:"beanstalkd host addr." required:"true" default:"localhost:11300"`
26+
27+
conn *beanstalk.Conn
28+
}
29+
30+
type TubeStats struct {
31+
JobsBuried int
32+
JobsDelayed int
33+
JobsReady int
34+
JobsReserved int
35+
JobsUrgent int
36+
Waiting int
37+
TotalJobs int
38+
}
39+
40+
func (m *Monitor) Execute(args []string) error {
41+
if err := m.Init(); err != nil {
42+
return err
43+
}
44+
45+
if err := m.PrintStats(); err != nil {
46+
return err
47+
}
48+
49+
return nil
50+
}
51+
52+
func (m *Monitor) Init() error {
53+
var err error
54+
m.conn, err = beanstalk.Dial("tcp", m.Host)
55+
if err != nil {
56+
return err
57+
}
58+
59+
return nil
60+
}
61+
62+
func (m *Monitor) PrintStats() error {
63+
stats, err := m.GetStats()
64+
if err != nil {
65+
return err
66+
}
67+
68+
table := termtable.NewTable(nil, &termtable.TableOptions{
69+
Padding: 1,
70+
UseSeparator: true,
71+
})
72+
73+
table.SetHeader([]string{
74+
"Name", "Buried", "Delayed", "Ready", "Reserved", "Urgent", "Waiting", "Total",
75+
})
76+
77+
for t, s := range stats {
78+
table.AddRow(m.buildLineFromTubeStats(t, s))
79+
}
80+
81+
fmt.Println(table.Render())
82+
return nil
83+
}
84+
85+
func (m *Monitor) buildLineFromTubeStats(name string, s *TubeStats) []string {
86+
l := make([]string, 0)
87+
88+
l = append(l, name)
89+
l = append(l, addStyle(s.JobsBuried, 10, HighSeverity))
90+
l = append(l, addStyle(s.JobsDelayed, 10, NormalSeverity))
91+
l = append(l, addStyle(s.JobsReady, 10, NormalSeverity))
92+
l = append(l, addStyle(s.JobsReserved, 10, NormalSeverity))
93+
l = append(l, addStyle(s.JobsUrgent, 10, NormalSeverity))
94+
l = append(l, addStyle(s.Waiting, 10, LowSeverity))
95+
l = append(l, addStyle(s.TotalJobs, 10, LowSeverity))
96+
97+
return l
98+
}
99+
100+
func addStyle(i int, l int, severity int) string {
101+
value := strconv.Itoa(i)
102+
needs := l - len(value)
103+
if needs <= 0 {
104+
return value
105+
}
106+
107+
padded := value + strings.Repeat(" ", needs)
108+
if i > 0 {
109+
switch severity {
110+
case HighSeverity:
111+
return HighSeverityStyle.Paint(padded)
112+
case NormalSeverity:
113+
return NormalSeverityStyle.Paint(padded)
114+
}
115+
}
116+
117+
return padded
118+
}
119+
120+
func (m *Monitor) GetStats() (map[string]*TubeStats, error) {
121+
tubes, err := m.conn.ListTubes()
122+
if err != nil {
123+
return nil, err
124+
}
125+
126+
stats := make(map[string]*TubeStats, 0)
127+
for _, tube := range tubes {
128+
s, err := m.GetStatsForTube(tube)
129+
if err != nil {
130+
return nil, err
131+
}
132+
133+
stats[tube] = s
134+
}
135+
136+
return stats, nil
137+
}
138+
139+
func (m *Monitor) GetStatsForTube(tube string) (*TubeStats, error) {
140+
t := &beanstalk.Tube{m.conn, tube}
141+
s, err := t.Stats()
142+
if err != nil {
143+
return nil, err
144+
}
145+
146+
if name, ok := s["name"]; !ok || name != tube {
147+
return nil, TubeStatsRetrievalError
148+
}
149+
150+
return &TubeStats{
151+
JobsBuried: mustConvertToInt(s["current-jobs-buried"]),
152+
JobsReady: mustConvertToInt(s["current-jobs-ready"]),
153+
JobsDelayed: mustConvertToInt(s["current-jobs-delayed"]),
154+
JobsReserved: mustConvertToInt(s["current-jobs-reserved"]),
155+
JobsUrgent: mustConvertToInt(s["current-jobs-urgent"]),
156+
Waiting: mustConvertToInt(s["current-waiting"]),
157+
TotalJobs: mustConvertToInt(s["total-jobs"]),
158+
}, nil
159+
}
160+
161+
func mustConvertToInt(s string) int {
162+
i, err := strconv.Atoi(s)
163+
if err != nil {
164+
panic(err)
165+
}
166+
167+
return i
168+
}

cli/monitor_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package cli
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestSomething(t *testing.T) {
10+
m := &Monitor{Host: "localhost:11300"}
11+
err := m.Init()
12+
assert.Nil(t, err)
13+
14+
m.PrintStats()
15+
16+
}

0 commit comments

Comments
 (0)