Skip to content

Commit 5894b0c

Browse files
committed
Add support for markers
1 parent dda71f6 commit 5894b0c

File tree

4 files changed

+59
-10
lines changed

4 files changed

+59
-10
lines changed

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ Above capture is jplot monitoring a Go service's [expvar](https://golang.org/pkg
99

1010
```
1111
jplot --url http://:8080/debug/vars \
12-
memstats.HeapSys+memstats.HeapAlloc+memstats.HeapIdle \
12+
memstats.HeapAlloc+memstats.HeapSys+memstats.HeapAlloc+memstats.HeapIdle+marker:counter:memstats.NumGC \
1313
counter:memstats.TotalAlloc \
1414
memstats.HeapObjects \
15-
memstats.StackSys+memstats.StackInuse \
16-
counter:memstats.NumGC
15+
memstats.StackSys+memstats.StackInuse
1716
```
1817

1918
## Install
@@ -70,20 +69,28 @@ jplot --url http://:8080/debug/vars mem.Heap+mem.Sys+mem.Stack counter:cpu.STime
7069

7170
![all](doc/all.png)
7271

73-
See [gojq](github.com/elgs/gojq) for more details on the JSON query syntax.
72+
### Spec Syntax
73+
74+
Each positional arguments given to jplot create a stacked graph with the specified values. To reference the values, use [gojq](github.com/elgs/gojq) JSON query syntax. Several value paths can be referenced for the same graph by using the `+` character to separate them.
75+
76+
In addition, each value path can be prefixed with options separated from the path by a column. Several options can be used for the same command by separating them with a comma like so: `option1,option2:value.path`.
77+
78+
Supported options are:
79+
* `counter`: Computes the difference with the last value. The value must increase monotonically.
80+
* `marker`: When the value is none-zero, a vertical line is drawn.
7481

7582
### Memstats
7683

7784
Here is an example command to graph a Go program memstats:
7885

7986
```
8087
jplot --url http://:8080/debug/vars \
81-
memstats.HeapSys+memstats.HeapAlloc+memstats.HeapIdle \
88+
memstats.HeapAlloc+memstats.HeapSys+memstats.HeapAlloc+memstats.HeapIdle+marker:counter:memstats.NumGC \
8289
counter:memstats.TotalAlloc \
8390
memstats.HeapObjects \
84-
memstats.StackSys+memstats.StackInuse \
85-
counter:memstats.NumGC
91+
memstats.StackSys+memstats.StackInuse
8692
```
93+
8794
![all](doc/memstats.png)
8895

8996

doc/demo.gif

90.5 KB
Loading

doc/memstats.png

50.9 KB
Loading

main.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ type field struct {
3333
id string
3434
name string
3535
counter bool
36+
marker bool
3637
}
3738

3839
func main() {
3940
url := flag.String("url", "", "URL to fetch every second. Read JSON objects from stdin if not specified.")
4041
steps := flag.Int("steps", 100, "Number of values to plot.")
4142
width := flag.Int("width", 2500, "Canvas width")
42-
height := flag.Int("height", 1300, "Canvas height")
43+
height := flag.Int("height", 1400, "Canvas height")
4344
dpi := flag.Float64("dpi", 220, "Canvas definition")
4445
flag.Parse()
4546

@@ -106,6 +107,20 @@ func parseSpec(args []string, width, height int, dpi float64) []graphSpec {
106107
}
107108
for j, name := range strings.Split(v, "+") {
108109
var isCounter bool
110+
var isMarker bool
111+
n := strings.Split(name, ":")
112+
for len(n) > 1 {
113+
switch n[0] {
114+
case "counter":
115+
isCounter = true
116+
case "marker":
117+
isMarker = true
118+
default:
119+
log.Fatalf("Invalid field option: %s", n[0])
120+
}
121+
n = n[1:]
122+
}
123+
name = n[0]
109124
if strings.HasPrefix(name, "counter:") {
110125
isCounter = true
111126
name = name[8:]
@@ -114,6 +129,7 @@ func parseSpec(args []string, width, height int, dpi float64) []graphSpec {
114129
id: fmt.Sprintf("%d.%d.%s", i, j, name),
115130
name: name,
116131
counter: isCounter,
132+
marker: isMarker,
117133
})
118134
}
119135
specs = append(specs, gs)
@@ -125,14 +141,23 @@ func render(specs []graphSpec, dp *data.Points) {
125141
graphs := make([]chart.Chart, 0, len(specs))
126142
for _, gs := range specs {
127143
series := []chart.Series{}
144+
markers := []chart.GridLine{}
128145
for _, f := range gs.fields {
129146
vals := dp.Get(f.id)
147+
if f.marker {
148+
for i, v := range vals {
149+
if v > 0 {
150+
markers = append(markers, chart.GridLine{Value: float64(i)})
151+
}
152+
}
153+
continue
154+
}
130155
series = append(series, chart.ContinuousSeries{
131156
Name: fmt.Sprintf("%s: %s", f.name, siValueFormater(vals[len(vals)-1])),
132157
YValues: vals,
133158
})
134159
}
135-
graphs = append(graphs, graph(series, gs.width, gs.height, gs.dpi))
160+
graphs = append(graphs, graph(series, markers, gs.width, gs.height, gs.dpi))
136161
}
137162
printGraphs(graphs)
138163
}
@@ -155,7 +180,7 @@ func reset() {
155180
}
156181

157182
// graph generate a line graph with series.
158-
func graph(series []chart.Series, width, height int, dpi float64) chart.Chart {
183+
func graph(series []chart.Series, markers []chart.GridLine, width, height int, dpi float64) chart.Chart {
159184
for i, s := range series {
160185
if s, ok := s.(chart.ContinuousSeries); ok {
161186
s.XValues = seq.Range(0, float64(len(s.YValues)-1))
@@ -194,6 +219,23 @@ func graph(series []chart.Series, width, height int, dpi float64) chart.Chart {
194219
},
195220
Series: series,
196221
}
222+
if len(markers) > 0 {
223+
graph.Background.Padding.Bottom = 0 // compensate transparent tick space
224+
graph.XAxis = chart.XAxis{
225+
Style: chart.StyleShow(),
226+
TickStyle: chart.Style{
227+
StrokeColor: chart.ColorTransparent,
228+
},
229+
TickPosition: 10, // hide text with non-existing position
230+
GridMajorStyle: chart.Style{
231+
Show: true,
232+
StrokeColor: chart.ColorAlternateGray,
233+
StrokeWidth: 3.0,
234+
StrokeDashArray: []float64{5.0, 5.0},
235+
},
236+
GridLines: markers,
237+
}
238+
}
197239
graph.Elements = []chart.Renderable{
198240
chart.Legend(&graph, chart.Style{
199241
FillColor: drawing.Color{A: 100},

0 commit comments

Comments
 (0)