Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/supervisor/pkg/serverapi/metrics.go
2500 views
1
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package serverapi
6
7
import (
8
"context"
9
"errors"
10
"time"
11
12
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
13
"github.com/prometheus/client_golang/prometheus"
14
"github.com/sourcegraph/jsonrpc2"
15
"google.golang.org/grpc/codes"
16
"google.golang.org/grpc/status"
17
)
18
19
const (
20
ServerTypePublicAPI = "public-api"
21
)
22
23
type ClientMetrics struct {
24
clientHandledCounter *prometheus.CounterVec
25
clientHandledHistogram *prometheus.HistogramVec
26
}
27
28
func NewClientMetrics() *ClientMetrics {
29
return &ClientMetrics{
30
clientHandledCounter: prometheus.NewCounterVec(
31
prometheus.CounterOpts{
32
Name: "supervisor_client_handled_total",
33
Help: "Total number of supervisor outgoing services completed by the client, regardless of success or failure.",
34
// add server label to identify it's going for server api or public api
35
}, []string{"method", "server", "err_code"}),
36
clientHandledHistogram: prometheus.NewHistogramVec(
37
prometheus.HistogramOpts{
38
Name: "supervisor_client_handling_seconds",
39
Help: "Histogram of response latency (seconds) of the supervisor outgoing services until it is finished by the application.",
40
// it should be aligned with https://github.com/gitpod-io/gitpod/blob/84ed1a0672d91446ba33cb7b504cfada769271a8/install/installer/pkg/components/ide-metrics/configmap.go#L315
41
Buckets: []float64{0.1, 0.2, 0.5, 1, 2, 5, 10},
42
}, []string{"method", "server", "err_code"}),
43
}
44
}
45
46
func (c *ClientMetrics) ProcessMetrics(method string, err error, startTime time.Time) {
47
code := status.Code(normalizeError(err))
48
server := ServerTypePublicAPI
49
c.clientHandledCounter.WithLabelValues(method, server, code.String()).Inc()
50
c.clientHandledHistogram.WithLabelValues(method, server, code.String()).Observe(time.Since(startTime).Seconds())
51
}
52
53
// guard to make sure ClientMetrics implement Collector interface
54
var _ prometheus.Collector = (*ClientMetrics)(nil)
55
56
func (c *ClientMetrics) Collect(ch chan<- prometheus.Metric) {
57
c.clientHandledCounter.Collect(ch)
58
c.clientHandledHistogram.Collect(ch)
59
}
60
61
func (c *ClientMetrics) Describe(ch chan<- *prometheus.Desc) {
62
c.clientHandledCounter.Describe(ch)
63
c.clientHandledHistogram.Describe(ch)
64
}
65
66
func normalizeError(err error) error {
67
if err == nil {
68
return nil
69
}
70
71
if errors.Is(err, context.Canceled) {
72
return status.Error(codes.Canceled, context.Canceled.Error())
73
}
74
75
if rpcErr := new(jsonrpc2.Error); errors.As(err, &rpcErr) {
76
switch rpcErr.Code {
77
case 400:
78
return status.Error(codes.InvalidArgument, rpcErr.Message)
79
case 401:
80
return status.Error(codes.Unauthenticated, rpcErr.Message)
81
case 403:
82
return status.Error(codes.PermissionDenied, rpcErr.Message)
83
case 404:
84
return status.Error(codes.NotFound, rpcErr.Message)
85
case 409:
86
return status.Error(codes.AlreadyExists, rpcErr.Message)
87
case -32603:
88
return status.Error(codes.Internal, rpcErr.Message)
89
case 470:
90
return status.Error(codes.PermissionDenied, rpcErr.Message)
91
92
default:
93
return status.Error(codes.Internal, rpcErr.Message)
94
}
95
}
96
97
if handshakeErr := new(protocol.ErrBadHandshake); errors.As(err, &handshakeErr) {
98
return status.Error(codes.Unauthenticated, "Failed to establish caller identity")
99
}
100
101
return err
102
}
103
104