1
1
package analyse
2
2
3
+ import (
4
+ "fmt"
5
+ "regexp"
6
+ "sort"
7
+ "strings"
8
+
9
+ log "github.com/sirupsen/logrus"
10
+
11
+ "github.com/grafana-tools/sdk"
12
+ "github.com/pkg/errors"
13
+ "github.com/prometheus/prometheus/promql/parser"
14
+ )
15
+
3
16
type MetricsInGrafana struct {
4
17
MetricsUsed []string `json:"metricsUsed"`
5
18
OverallMetrics map [string ]struct {} `json:"-"`
@@ -13,3 +26,138 @@ type DashboardMetrics struct {
13
26
Metrics []string `json:"metrics"`
14
27
ParseErrors []string `json:"parse_errors"`
15
28
}
29
+
30
+ func ParseMetricsInBoard (mig * MetricsInGrafana , board sdk.Board ) {
31
+ var parseErrors []error
32
+ metrics := make (map [string ]struct {})
33
+
34
+ // Iterate through all the panels and collect metrics
35
+ for _ , panel := range board .Panels {
36
+ parseErrors = append (parseErrors , metricsFromPanel (* panel , metrics )... )
37
+ if panel .RowPanel != nil {
38
+ for _ , subPanel := range panel .RowPanel .Panels {
39
+ parseErrors = append (parseErrors , metricsFromPanel (subPanel , metrics )... )
40
+ }
41
+ }
42
+ }
43
+
44
+ // Iterate through all the rows and collect metrics
45
+ for _ , row := range board .Rows {
46
+ for _ , panel := range row .Panels {
47
+ parseErrors = append (parseErrors , metricsFromPanel (panel , metrics )... )
48
+ }
49
+ }
50
+
51
+ // Process metrics in templating
52
+ parseErrors = append (parseErrors , metricsFromTemplating (board .Templating , metrics )... )
53
+
54
+ var parseErrs []string
55
+ for _ , err := range parseErrors {
56
+ parseErrs = append (parseErrs , err .Error ())
57
+ }
58
+
59
+ var metricsInBoard []string
60
+ for metric := range metrics {
61
+ if metric == "" {
62
+ continue
63
+ }
64
+
65
+ metricsInBoard = append (metricsInBoard , metric )
66
+ mig .OverallMetrics [metric ] = struct {}{}
67
+ }
68
+ sort .Strings (metricsInBoard )
69
+
70
+ mig .Dashboards = append (mig .Dashboards , DashboardMetrics {
71
+ Slug : board .Slug ,
72
+ UID : board .UID ,
73
+ Title : board .Title ,
74
+ Metrics : metricsInBoard ,
75
+ ParseErrors : parseErrs ,
76
+ })
77
+
78
+ }
79
+
80
+ func metricsFromTemplating (templating sdk.Templating , metrics map [string ]struct {}) []error {
81
+ parseErrors := []error {}
82
+ for _ , templateVar := range templating .List {
83
+ if templateVar .Type != "query" {
84
+ continue
85
+ }
86
+ if query , ok := templateVar .Query .(string ); ok {
87
+ // label_values
88
+ if strings .Contains (query , "label_values" ) {
89
+ re := regexp .MustCompile (`label_values\(([a-zA-Z0-9_]+)` )
90
+ sm := re .FindStringSubmatch (query )
91
+ // In case of really gross queries, like - https://github.com/grafana/jsonnet-libs/blob/e97ab17f67ab40d5fe3af7e59151dd43be03f631/hass-mixin/dashboard.libsonnet#L93
92
+ if len (sm ) > 0 {
93
+ query = sm [1 ]
94
+ }
95
+ }
96
+ // query_result
97
+ if strings .Contains (query , "query_result" ) {
98
+ re := regexp .MustCompile (`query_result\((.+)\)` )
99
+ query = re .FindStringSubmatch (query )[1 ]
100
+ }
101
+ err := parseQuery (query , metrics )
102
+ if err != nil {
103
+ parseErrors = append (parseErrors , errors .Wrapf (err , "query=%v" , query ))
104
+ log .Debugln ("msg" , "promql parse error" , "err" , err , "query" , query )
105
+ continue
106
+ }
107
+ } else {
108
+ err := fmt .Errorf ("templating type error: name=%v" , templateVar .Name )
109
+ parseErrors = append (parseErrors , err )
110
+ log .Debugln ("msg" , "templating parse error" , "err" , err )
111
+ continue
112
+ }
113
+ }
114
+ return parseErrors
115
+ }
116
+
117
+ func metricsFromPanel (panel sdk.Panel , metrics map [string ]struct {}) []error {
118
+ var parseErrors []error
119
+
120
+ if panel .GetTargets () == nil {
121
+ return parseErrors
122
+ }
123
+
124
+ for _ , target := range * panel .GetTargets () {
125
+ // Prometheus has this set.
126
+ if target .Expr == "" {
127
+ continue
128
+ }
129
+ query := target .Expr
130
+ err := parseQuery (query , metrics )
131
+ if err != nil {
132
+ parseErrors = append (parseErrors , errors .Wrapf (err , "query=%v" , query ))
133
+ log .Debugln ("msg" , "promql parse error" , "err" , err , "query" , query )
134
+ continue
135
+ }
136
+ }
137
+
138
+ return parseErrors
139
+ }
140
+
141
+ func parseQuery (query string , metrics map [string ]struct {}) error {
142
+ query = strings .ReplaceAll (query , `$__interval` , "5m" )
143
+ query = strings .ReplaceAll (query , `$interval` , "5m" )
144
+ query = strings .ReplaceAll (query , `$resolution` , "5s" )
145
+ query = strings .ReplaceAll (query , "$__rate_interval" , "15s" )
146
+ query = strings .ReplaceAll (query , "$__range" , "1d" )
147
+ query = strings .ReplaceAll (query , "${__range_s:glob}" , "30" )
148
+ query = strings .ReplaceAll (query , "${__range_s}" , "30" )
149
+ expr , err := parser .ParseExpr (query )
150
+ if err != nil {
151
+ return err
152
+ }
153
+
154
+ parser .Inspect (expr , func (node parser.Node , path []parser.Node ) error {
155
+ if n , ok := node .(* parser.VectorSelector ); ok {
156
+ metrics [n .Name ] = struct {}{}
157
+ }
158
+
159
+ return nil
160
+ })
161
+
162
+ return nil
163
+ }
0 commit comments