Path: blob/main/components/supervisor/pkg/serverapi/metrics.go
2500 views
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package serverapi56import (7"context"8"errors"9"time"1011protocol "github.com/gitpod-io/gitpod/gitpod-protocol"12"github.com/prometheus/client_golang/prometheus"13"github.com/sourcegraph/jsonrpc2"14"google.golang.org/grpc/codes"15"google.golang.org/grpc/status"16)1718const (19ServerTypePublicAPI = "public-api"20)2122type ClientMetrics struct {23clientHandledCounter *prometheus.CounterVec24clientHandledHistogram *prometheus.HistogramVec25}2627func NewClientMetrics() *ClientMetrics {28return &ClientMetrics{29clientHandledCounter: prometheus.NewCounterVec(30prometheus.CounterOpts{31Name: "supervisor_client_handled_total",32Help: "Total number of supervisor outgoing services completed by the client, regardless of success or failure.",33// add server label to identify it's going for server api or public api34}, []string{"method", "server", "err_code"}),35clientHandledHistogram: prometheus.NewHistogramVec(36prometheus.HistogramOpts{37Name: "supervisor_client_handling_seconds",38Help: "Histogram of response latency (seconds) of the supervisor outgoing services until it is finished by the application.",39// it should be aligned with https://github.com/gitpod-io/gitpod/blob/84ed1a0672d91446ba33cb7b504cfada769271a8/install/installer/pkg/components/ide-metrics/configmap.go#L31540Buckets: []float64{0.1, 0.2, 0.5, 1, 2, 5, 10},41}, []string{"method", "server", "err_code"}),42}43}4445func (c *ClientMetrics) ProcessMetrics(method string, err error, startTime time.Time) {46code := status.Code(normalizeError(err))47server := ServerTypePublicAPI48c.clientHandledCounter.WithLabelValues(method, server, code.String()).Inc()49c.clientHandledHistogram.WithLabelValues(method, server, code.String()).Observe(time.Since(startTime).Seconds())50}5152// guard to make sure ClientMetrics implement Collector interface53var _ prometheus.Collector = (*ClientMetrics)(nil)5455func (c *ClientMetrics) Collect(ch chan<- prometheus.Metric) {56c.clientHandledCounter.Collect(ch)57c.clientHandledHistogram.Collect(ch)58}5960func (c *ClientMetrics) Describe(ch chan<- *prometheus.Desc) {61c.clientHandledCounter.Describe(ch)62c.clientHandledHistogram.Describe(ch)63}6465func normalizeError(err error) error {66if err == nil {67return nil68}6970if errors.Is(err, context.Canceled) {71return status.Error(codes.Canceled, context.Canceled.Error())72}7374if rpcErr := new(jsonrpc2.Error); errors.As(err, &rpcErr) {75switch rpcErr.Code {76case 400:77return status.Error(codes.InvalidArgument, rpcErr.Message)78case 401:79return status.Error(codes.Unauthenticated, rpcErr.Message)80case 403:81return status.Error(codes.PermissionDenied, rpcErr.Message)82case 404:83return status.Error(codes.NotFound, rpcErr.Message)84case 409:85return status.Error(codes.AlreadyExists, rpcErr.Message)86case -32603:87return status.Error(codes.Internal, rpcErr.Message)88case 470:89return status.Error(codes.PermissionDenied, rpcErr.Message)9091default:92return status.Error(codes.Internal, rpcErr.Message)93}94}9596if handshakeErr := new(protocol.ErrBadHandshake); errors.As(err, &handshakeErr) {97return status.Error(codes.Unauthenticated, "Failed to establish caller identity")98}99100return err101}102103104