Skip to content

Commit c56bcf9

Browse files
Merge pull request #7 from henrywhitaker3/feat/extra-queries
2 parents a4292b2 + 8bfbd04 commit c56bcf9

File tree

9 files changed

+114
-35
lines changed

9 files changed

+114
-35
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/expr-lang/expr v1.16.7
77
github.com/go-echarts/go-echarts/v2 v2.3.3
88
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
9+
github.com/henrywhitaker3/flow v1.5.0
910
github.com/labstack/echo-contrib v0.17.1
1011
github.com/labstack/echo/v4 v4.12.0
1112
github.com/prometheus/client_golang v1.19.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
2020
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
2121
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4=
2222
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
23+
github.com/henrywhitaker3/flow v1.5.0 h1:jZjs3a2/2Ym7ge3tgSlh9I9iB2GVTGaTb5di2q917Bw=
24+
github.com/henrywhitaker3/flow v1.5.0/go.mod h1:5ELoprHvmkLtpgvzbRB6faq/8/jcdFwUkkckArAH+CU=
2325
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
2426
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
2527
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=

internal/collector/collector.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type Result struct {
3131
Uptime float32
3232
// The series of values for the range query
3333
Series Series
34+
// The servies of extra queries to graph
35+
Extras map[string]Series
3436
}
3537

3638
type Collector struct {
@@ -81,6 +83,7 @@ func (c *Collector) collectService(ctx context.Context, svc config.Service, ch c
8183
Status: false,
8284
Success: true,
8385
Uptime: 0,
86+
Extras: map[string]Series{},
8487
}
8588
log.Printf("collecting metrics for %s\n", svc.Name)
8689

@@ -95,6 +98,14 @@ func (c *Collector) collectService(ctx context.Context, svc config.Service, ch c
9598
res.Success = false
9699
}
97100

101+
for _, extra := range svc.Extras {
102+
_, series, err := c.q.Uptime(ctx, extra)
103+
if err != nil {
104+
log.Printf("ERROR - Failed to scrape uptime metric for %s query %s: %s", svc.Name, extra.Name, err)
105+
}
106+
res.Extras[extra.Name] = c.mapQuerierSeries(extra, series)
107+
}
108+
98109
res.Series = c.mapQuerierSeries(svc.Query, series)
99110
res.Status = status
100111
res.Uptime = uptime

internal/config/config.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ type Query struct {
2020
}
2121

2222
type Service struct {
23-
Name string `yaml:"name"`
24-
Query Query `yaml:"query"`
25-
Group string `yaml:"group"`
26-
// Extras []Query `yaml:"extras"`
23+
Name string `yaml:"name"`
24+
Query Query `yaml:"query"`
25+
Group string `yaml:"group"`
26+
Extras []Query `yaml:"extras"`
2727
}
2828

2929
type UI struct {
@@ -85,15 +85,18 @@ func setDefaults(conf *Config) {
8585

8686
for i, svc := range conf.Services {
8787
svc.Query.Name = "main"
88-
if svc.Query.Range == 0 {
89-
svc.Query.Range = time.Hour * 24
90-
}
91-
if svc.Query.Step == 0 {
92-
svc.Query.Step = time.Minute * 5
93-
}
9488
if svc.Group == "" {
9589
svc.Group = "default"
9690
}
91+
setDefaultQueryValues(&svc.Query)
92+
93+
for i, query := range svc.Extras {
94+
if query.Expression == "" {
95+
query.Expression = "float(result)"
96+
}
97+
setDefaultQueryValues(&query)
98+
svc.Extras[i] = query
99+
}
97100
conf.Services[i] = svc
98101
}
99102

@@ -112,10 +115,27 @@ func setDefaults(conf *Config) {
112115
}
113116
}
114117

118+
func setDefaultQueryValues(q *Query) {
119+
if q.Range == 0 {
120+
q.Range = time.Hour * 24
121+
}
122+
if q.Step == 0 {
123+
q.Step = time.Minute * 5
124+
}
125+
}
126+
115127
func (c *Config) Validate() error {
116128
if c.Prometheus == "" {
117129
return errors.New("prometheus cannot be empty")
118130
}
119131

132+
for _, svc := range c.Services {
133+
for _, extra := range svc.Extras {
134+
if extra.Name == "" {
135+
return errors.New("extra query name cannot be empty")
136+
}
137+
}
138+
}
139+
120140
return nil
121141
}

internal/http/http.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import (
44
"context"
55
"fmt"
66
"net/http"
7+
"time"
78

89
"github.com/henrywhitaker3/prompage/internal/app"
910
"github.com/henrywhitaker3/prompage/internal/resources/static"
1011
"github.com/labstack/echo-contrib/echoprometheus"
1112
"github.com/labstack/echo/v4"
13+
"github.com/labstack/echo/v4/middleware"
1214
)
1315

1416
type Http struct {
@@ -22,6 +24,9 @@ func NewHttp(app *app.App, cache *ResultCache) *Http {
2224
e.HideBanner = true
2325

2426
e.Use(echoprometheus.NewMiddleware("prompage"))
27+
e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
28+
Timeout: time.Second * 5,
29+
}))
2530

2631
e.GET("/", NewStatusPageHandler(app, cache))
2732
e.GET("/:name", NewGetServiceHandler(app, cache))

internal/http/service.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package http
22

33
import (
4+
"context"
45
"errors"
56
"html/template"
67
"log"
78
"net/http"
89
"time"
910

11+
"github.com/henrywhitaker3/flow"
1012
"github.com/henrywhitaker3/prompage/internal/app"
1113
"github.com/henrywhitaker3/prompage/internal/collector"
1214
"github.com/henrywhitaker3/prompage/internal/config"
@@ -22,6 +24,7 @@ type getServiceData struct {
2224
Version string
2325
Result collector.Result
2426
Graph template.HTML
27+
Extras map[string]template.HTML
2528
}
2629

2730
func NewGetServiceHandler(app *app.App, cache *ResultCache) echo.HandlerFunc {
@@ -35,14 +38,44 @@ func NewGetServiceHandler(app *app.App, cache *ResultCache) echo.HandlerFunc {
3538
log.Printf("ERROR - could not find service: %s\n", name)
3639
return err
3740
}
38-
graph, err := views.GenerateLineChart(svc.Series, app.Config.UI.Graphs.Points)
39-
if err != nil {
41+
42+
group := &flow.ResultGroup{}
43+
44+
graph := flow.Eventually(c.Request().Context(), func(ctx context.Context) (string, error) {
45+
return views.GenerateLineChart(svc.Series, app.Config.UI.Graphs.Points)
46+
})
47+
group.Add(graph)
48+
49+
extraGraphs := map[string]*flow.Result[string]{}
50+
51+
for _, extra := range svc.Extras {
52+
res := flow.Eventually(c.Request().Context(), func(ctx context.Context) (string, error) {
53+
return views.GenerateLineChart(extra, app.Config.UI.Graphs.Points)
54+
})
55+
group.Add(res)
56+
extraGraphs[extra.Query.Name] = res
57+
}
58+
59+
// Wait for all the graphs to generate
60+
group.Wait()
61+
62+
if graph.Err() != nil {
4063
log.Printf("ERROR - could not generate graph: %s\n", err)
4164
}
65+
4266
data := getServiceData{
4367
Age: time.Since(age).Round(time.Second),
4468
Result: svc,
45-
Graph: template.HTML(graph),
69+
Graph: template.HTML(graph.Out()),
70+
Extras: map[string]template.HTML{},
71+
}
72+
73+
for name, res := range extraGraphs {
74+
if res.Err() != nil {
75+
log.Printf("ERROR - could not generate graph: %s\n", res.Err())
76+
continue
77+
}
78+
data.Extras[name] = template.HTML(res.Out())
4679
}
4780

4881
out, err := views.Build(views.SERVICE, data)

internal/querier/querier.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,24 +64,25 @@ func (q *Querier) Uptime(ctx context.Context, query config.Query) (float32, []It
6464
total := 0
6565
series := []Item{}
6666
for _, val := range r[0].Values {
67-
res, err := q.vector(val.Value, query)
68-
if err != nil {
69-
return 0, nil, err
70-
}
71-
total++
72-
7367
value := float64(0)
74-
if res {
75-
passing++
76-
value = 1
77-
}
78-
if !query.BoolValue {
68+
if query.BoolValue {
69+
res, err := q.vector(val.Value, query)
70+
if err != nil {
71+
return 0, nil, err
72+
}
73+
if res {
74+
passing++
75+
value = 1
76+
}
77+
} else {
7978
f, err := q.asFloat(val.Value)
8079
if err != nil {
8180
return 0, nil, err
8281
}
8382
value = f
8483
}
84+
total++
85+
8586
series = append(series, Item{Time: val.Timestamp.Time(), Value: value})
8687
}
8788

internal/resources/views/charts.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,33 @@ func GenerateLineChart(series collector.Series, maxPoints int) (string, error) {
6767

6868
yopts := opts.YAxis{
6969
Show: false,
70+
Type: "value",
7071
}
7172
if series.Query.BoolValue {
7273
yopts.Max = 2
7374
yopts.Min = 0
7475
}
76+
if series.Query.Units != "" {
77+
yopts.Name = series.Query.Units
78+
}
7579

7680
line.SetGlobalOptions(
7781
charts.WithInitializationOpts(opts.Initialization{
7882
Theme: types.ThemeWesteros,
7983
}),
8084
charts.WithYAxisOpts(yopts),
81-
charts.WithXAxisOpts(opts.XAxis{Show: false}),
85+
charts.WithXAxisOpts(opts.XAxis{
86+
Show: false,
87+
Type: "time",
88+
}),
8289
charts.WithLegendOpts(opts.Legend{
8390
Show: false,
8491
}),
8592
)
8693

8794
cd := condense(series, maxPoints)
8895

89-
line.SetXAxis(getXAxis(cd)).
96+
line.SetXAxis(nil).
9097
AddSeries("Metric", getYAxis(cd)).
9198
SetSeriesOptions(
9299
charts.WithLineChartOpts(opts.LineChart{Smooth: true, Color: "#5c6848"}),
@@ -104,19 +111,11 @@ func GenerateLineChart(series collector.Series, maxPoints int) (string, error) {
104111
return chart, nil
105112
}
106113

107-
func getXAxis(series collector.Series) []string {
108-
out := []string{}
109-
for _, i := range series.Data {
110-
out = append(out, i.Time.String())
111-
}
112-
return out
113-
}
114-
115114
func getYAxis(series collector.Series) []opts.LineData {
116115
out := []opts.LineData{}
117116
for _, i := range series.Data {
118117
out = append(out, opts.LineData{
119-
Value: i.Value,
118+
Value: []interface{}{i.Time.Format(time.RFC3339), i.Value},
120119
})
121120
}
122121
return out

internal/resources/views/service.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ <h1 class="text-xl font-bold md:inline-block md:mr-2 text-avocado-800" >{{ .Resu
77
</div>
88

99
<div class="h-64 md:h-96 w-full">{{ .Graph }}</div>
10+
11+
<div class="w-full">
12+
{{- range $name, $graph := .Extras }}
13+
<h2 class="text-center text-avocado-800">{{ $name }}</h2>
14+
<div class="h-64 md:h-96 w-full">{{ $graph }}</div>
15+
{{- end }}
16+
</div>
1017
</div>
1118
{{- end }}
1219
{{- end }}

0 commit comments

Comments
 (0)