...
1 package middleware
2
3 import (
4 "fmt"
5 "strings"
6 "time"
7
8 "github.com/99designs/gqlgen/graphql"
9 "github.com/gin-gonic/gin"
10 "github.com/penglongli/gin-metrics/ginmetrics"
11 "github.com/vektah/gqlparser/v2/ast"
12 )
13
14 const (
15 metricPath = "/metrics"
16 maxLatencySeconds = 1
17 graphQLRequests = "graphql_request_total"
18 graphQlOperations = "graphql_query_total"
19 graphQLSlowReQuests = "graphql_slow_request_total"
20 graphQLResponseTime = "graphql_query_duration"
21 )
22
23 func UseMetrics(r gin.IRoutes) {
24
25 m := ginmetrics.GetMonitor()
26 m.SetMetricPath(metricPath)
27 m.SetSlowTime(maxLatencySeconds)
28
29 _ = m.AddMetric(&ginmetrics.Metric{
30 Type: ginmetrics.Counter,
31 Name: graphQLRequests,
32 Description: "count all graphql requests.",
33 Labels: nil,
34 })
35
36 _ = m.AddMetric(&ginmetrics.Metric{
37 Type: ginmetrics.Counter,
38 Name: graphQlOperations,
39 Description: "count all graphql requests by name, operation and status.",
40 Labels: []string{"name", "operation", "status"},
41 })
42
43 _ = m.AddMetric(&ginmetrics.Metric{
44 Type: ginmetrics.Counter,
45 Name: graphQLSlowReQuests,
46 Description: fmt.Sprintf("count all graphql slow requests by name, operation and status. max_lentency=%d sec.", maxLatencySeconds),
47 Labels: []string{"name", "operation", "status"},
48 })
49
50 m.SetDuration([]float64{0.1, 0.3, 1.2, 5, 10})
51 _ = m.AddMetric(&ginmetrics.Metric{
52 Type: ginmetrics.Histogram,
53 Name: graphQLResponseTime,
54 Description: "the time graphql took handle the request by name.",
55 Labels: []string{"name"},
56
57
58 Buckets: []float64{0.1, 0.3, 1.2, 5, 10},
59 })
60
61 m.Use(r)
62 }
63
64 func HandleGraphQlMetrics(ctx *graphql.OperationContext, resp *graphql.Response, start time.Time) {
65 latency := time.Since(start)
66
67
68 _ = ginmetrics.GetMonitor().GetMetric(graphQLRequests).Inc(nil)
69
70 name := "no_selection"
71 selections := GetSelectionNames(ctx)
72 if len(selections) > 0 {
73 name = strings.Join(selections, ",")
74 }
75
76 operation := ctx.OperationName
77 if operation == "" && ctx != nil && ctx.Operation != nil {
78 operation = string(ctx.Operation.Operation)
79 }
80 if operation == "" {
81 operation = "unknown"
82 }
83
84 status := "success"
85 if resp != nil && len(resp.Errors) > 0 {
86 status = "error"
87 }
88
89 labels := []string{name, operation, status}
90
91
92 _ = ginmetrics.GetMonitor().GetMetric(graphQlOperations).Inc(labels)
93
94
95 if int32(latency.Seconds()) > maxLatencySeconds {
96 _ = ginmetrics.GetMonitor().GetMetric(graphQLSlowReQuests).Inc(labels)
97 }
98
99
100 _ = ginmetrics.GetMonitor().GetMetric(graphQLResponseTime).Observe([]string{name}, latency.Seconds())
101 }
102
103
104 func GetSelectionNames(rc *graphql.OperationContext) []string {
105 var selections []string
106 if rc.Operation != nil {
107 for _, selection := range rc.Operation.SelectionSet {
108 selectionName := selection.(*ast.Field).Name
109
110 if !strings.HasPrefix(selectionName, "_") {
111 selections = append(selections, selectionName)
112 }
113 }
114 }
115 return selections
116 }
117
View as plain text