11package analyse
22
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+
316type MetricsInGrafana struct {
417 MetricsUsed []string `json:"metricsUsed"`
518 OverallMetrics map [string ]struct {} `json:"-"`
@@ -13,3 +26,138 @@ type DashboardMetrics struct {
1326 Metrics []string `json:"metrics"`
1427 ParseErrors []string `json:"parse_errors"`
1528}
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