Skip to content

Commit ee141c6

Browse files
scottleppxnyo
andauthored
SLO - http request external duration and error source (#989)
* SLO - http request external duration and error source --------- Co-authored-by: Giuseppe Guerra <[email protected]>
1 parent 757e917 commit ee141c6

File tree

2 files changed

+104
-2
lines changed

2 files changed

+104
-2
lines changed

experimental/slo/slo_middleware.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package slo
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/http"
7+
"strings"
8+
"syscall"
9+
"time"
10+
11+
"github.com/grafana/grafana-plugin-sdk-go/backend"
12+
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
13+
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
14+
"github.com/prometheus/client_golang/prometheus"
15+
"github.com/prometheus/client_golang/prometheus/promauto"
16+
)
17+
18+
var Logger = log.DefaultLogger
19+
20+
var duration = promauto.NewHistogramVec(prometheus.HistogramOpts{
21+
Namespace: "plugins",
22+
Name: "plugin_external_requests_duration_seconds",
23+
Help: "Duration of requests to external services",
24+
}, []string{"datasource_name", "datasource_type", "error_source"})
25+
26+
const DataSourceSLOMiddlewareName = "slo"
27+
28+
// Middleware captures duration of requests to external services and the source of errors
29+
func Middleware() httpclient.Middleware {
30+
return httpclient.NamedMiddlewareFunc(DataSourceSLOMiddlewareName, RoundTripper)
31+
}
32+
33+
// RoundTripper captures duration of requests to external services and the source of errors
34+
func RoundTripper(opts httpclient.Options, next http.RoundTripper) http.RoundTripper {
35+
name, kind, err := getDSInfo(opts)
36+
if err != nil {
37+
Logger.Error("failed to get datasource info", "error", err)
38+
return next
39+
}
40+
return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
41+
start := time.Now()
42+
var errorSource = "none"
43+
44+
defer func() {
45+
duration.WithLabelValues(name, kind, errorSource).Observe(time.Since(start).Seconds())
46+
}()
47+
48+
res, err := next.RoundTrip(req)
49+
if res != nil && res.StatusCode >= 400 {
50+
errorSource = string(backend.ErrorSourceFromHTTPStatus(res.StatusCode))
51+
}
52+
if errors.Is(err, syscall.ECONNREFUSED) {
53+
errorSource = string(backend.ErrorSourceDownstream)
54+
}
55+
return res, err
56+
})
57+
}
58+
59+
func getDSInfo(opts httpclient.Options) (string, string, error) {
60+
datasourceName, exists := opts.Labels["datasource_name"]
61+
if !exists {
62+
return "", "", errors.New("datasource_name label not found")
63+
}
64+
65+
datasourceName, err := SanitizeLabelName(datasourceName)
66+
// if the datasource named cannot be turned into a prometheus
67+
// label we will skip instrumenting these metrics.
68+
if err != nil {
69+
return "", "", err
70+
}
71+
72+
datasourceType, exists := opts.Labels["datasource_type"]
73+
if !exists {
74+
return "", "", errors.New("datasource_type label not found")
75+
}
76+
77+
return datasourceName, datasourceType, nil
78+
}
79+
80+
// SanitizeLabelName removes all invalid chars from the label name.
81+
// If the label name is empty or contains only invalid chars, it
82+
// will return an error.
83+
func SanitizeLabelName(name string) (string, error) {
84+
if len(name) == 0 {
85+
return "", errors.New("label name cannot be empty")
86+
}
87+
88+
out := strings.Builder{}
89+
for i, b := range name {
90+
if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0) {
91+
out.WriteRune(b)
92+
} else if b == ' ' {
93+
out.WriteRune('_')
94+
}
95+
}
96+
97+
if out.Len() == 0 {
98+
return "", fmt.Errorf("label name only contains invalid chars: %q", name)
99+
}
100+
101+
return out.String(), nil
102+
}

experimental/testdata/folder.golden.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Frame[0] {
99
"pathSeparator": "/"
1010
}
1111
Name:
12-
Dimensions: 2 Fields by 21 Rows
12+
Dimensions: 2 Fields by 22 Rows
1313
+----------------+------------------+
1414
| Name: name | Name: media-type |
1515
| Labels: | Labels: |
@@ -29,4 +29,4 @@ Dimensions: 2 Fields by 21 Rows
2929

3030

3131
====== TEST DATA RESPONSE (arrow base64) ======
32-
FRAME=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
32+
FRAME=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

0 commit comments

Comments
 (0)