-
Notifications
You must be signed in to change notification settings - Fork 291
/
Copy pathinitialize_metrics.go
161 lines (139 loc) · 5.29 KB
/
initialize_metrics.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
// Package metric provides functions to initialize the controller specific
// collectors and hooks to measure metrics and update the relevant collectors.
package metric
import (
"net/http"
"strconv"
"strings"
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)
const (
LabelConnectionType = "conn_type"
LabelGrpcService = "grpc_service"
LabelGrpcMethod = "grpc_method"
LabelGrpcCode = "grpc_code"
LabelHttpPath = "path"
LabelHttpMethod = "method"
LabelHttpCode = "code"
invalidPathValue = "invalid"
)
var (
ListGrpcLabels = []string{LabelGrpcService, LabelGrpcMethod, LabelGrpcCode}
ListHttpLabels = []string{LabelHttpPath, LabelHttpMethod, LabelHttpCode}
)
/* The following methods are used to initialize Prometheus histogram vectors for gRPC connections. */
// rangeProtoFiles returns true while there are services with associated methods in the proto package.
// It relies on RangeFilesByPackage to range through the package, and it adds them into map m.
// Services and methods for which filter() returns true are not added into the map.
func rangeProtoFiles(m map[string][]string, fd protoreflect.FileDescriptor, filter func(string, string) bool) bool {
if fd.Services().Len() == 0 {
return true
}
for i := 0; i < fd.Services().Len(); i++ {
s := fd.Services().Get(i)
if s.Methods().Len() == 0 {
continue
}
serviceName := string(s.FullName())
methods := []string{}
for j := 0; j < s.Methods().Len(); j++ {
methodName := string(s.Methods().Get(j).Name())
if filter(serviceName, methodName) {
continue
}
methods = append(methods, methodName)
}
if len(methods) > 0 {
m[serviceName] = methods
}
}
return true
}
// appendServicesAndMethods ranges through all registered files in a specified proto package
// and appends service and method names to the provided map m.
func appendServicesAndMethods(m map[string][]string, pkg protoreflect.FileDescriptor, filter func(string, string) bool) {
protoregistry.GlobalFiles.RangeFilesByPackage(
pkg.Package(),
func(fd protoreflect.FileDescriptor) bool { return rangeProtoFiles(m, fd, filter) },
)
}
// InitializeGrpcCollectorsFromPackage registers and zeroes a Prometheus
// histogram, populating all service and method labels by ranging through
// the package containing the provided FileDescriptor.
// The filter function takes in a service name and method name and skips adding them as labels
// upon returning true.
// Note: inputting a protoreflect.FileDescriptor will populate all services and methods
// found in its package, not just methods associated with that specific FileDescriptor.
func InitializeGrpcCollectorsFromPackage(r prometheus.Registerer, v prometheus.ObserverVec,
pkgs []protoreflect.FileDescriptor, codes []codes.Code, filter func(string, string) bool,
) {
if r == nil {
return
}
r.MustRegister(v)
serviceNamesToMethodNames := make(map[string][]string, 0)
for _, p := range pkgs {
appendServicesAndMethods(serviceNamesToMethodNames, p, filter)
}
for serviceName, serviceMethods := range serviceNamesToMethodNames {
for _, sm := range serviceMethods {
for _, c := range codes {
v.With(prometheus.Labels{LabelGrpcService: serviceName, LabelGrpcMethod: sm, LabelGrpcCode: c.String()})
}
}
}
}
func InitializeConnectionCounters(r prometheus.Registerer, counters []prometheus.CounterVec) {
if r == nil {
return
}
for _, c := range counters {
r.MustRegister(c)
}
}
// InitializeGrpcCollectorsFromServer registers and zeroes a Prometheus
// histogram, finding all service and method labels from the provided gRPC
// server.
func InitializeGrpcCollectorsFromServer(r prometheus.Registerer, v prometheus.ObserverVec, server *grpc.Server, codes []codes.Code) {
if r == nil {
return
}
r.MustRegister(v)
for serviceName, info := range server.GetServiceInfo() {
for _, mInfo := range info.Methods {
for _, c := range codes {
v.With(prometheus.Labels{LabelGrpcService: serviceName, LabelGrpcMethod: mInfo.Name, LabelGrpcCode: c.String()})
}
}
}
}
/* The following methods are used to initialize Prometheus histogram vectors for http requests. */
// InitializeApiCollectors registers and zeroes a Prometheus
// histogram, populating all path, code, and method labels from the
// provided maps in its parameters.
func InitializeApiCollectors(r prometheus.Registerer, v prometheus.ObserverVec, expectedPathsToMethods map[string][]string, expectedStatusCodesPerMethod map[string][]int) {
if r == nil {
return
}
r.MustRegister(v)
for p, methods := range expectedPathsToMethods {
for _, m := range methods {
for _, sc := range expectedStatusCodesPerMethod[m] {
v.With(prometheus.Labels{LabelHttpPath: p, LabelHttpMethod: strings.ToLower(m), LabelHttpCode: strconv.Itoa(sc)})
}
}
}
// When an invalid path is found, any method is possible, but we expect an error response.
p := invalidPathValue
for m := range expectedStatusCodesPerMethod {
for _, sc := range []int{http.StatusNotFound, http.StatusMethodNotAllowed} {
v.With(prometheus.Labels{LabelHttpPath: p, LabelHttpMethod: strings.ToLower(m), LabelHttpCode: strconv.Itoa(sc)})
}
}
}