mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
125 lines
3.6 KiB
Go
125 lines
3.6 KiB
Go
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/bufbuild/connect-go"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
type ConnectMetrics struct {
|
|
ServerRequestsStarted *prometheus.CounterVec
|
|
ServerRequestsHandled *prometheus.HistogramVec
|
|
|
|
ClientRequestsStarted *prometheus.CounterVec
|
|
ClientRequestsHandled *prometheus.HistogramVec
|
|
}
|
|
|
|
func (m *ConnectMetrics) Register(registry *prometheus.Registry) error {
|
|
metrics := []prometheus.Collector{
|
|
m.ServerRequestsStarted,
|
|
m.ServerRequestsHandled,
|
|
m.ClientRequestsStarted,
|
|
m.ClientRequestsHandled,
|
|
}
|
|
|
|
for _, metric := range metrics {
|
|
err := registry.Register(metric)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to register metric: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func NewConnectMetrics() *ConnectMetrics {
|
|
return &ConnectMetrics{
|
|
ServerRequestsStarted: prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
Name: "connect_server_started_total",
|
|
Help: "Counter of server connect (gRPC/HTTP) requests started",
|
|
}, []string{"package", "call", "call_type"}),
|
|
ServerRequestsHandled: prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
|
Name: "connect_server_handled_seconds",
|
|
Help: "Histogram of server connect (gRPC/HTTP) requests completed",
|
|
}, []string{"package", "call", "call_type", "code"}),
|
|
|
|
ClientRequestsStarted: prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
Name: "connect_client_started_total",
|
|
Help: "Counter of client connect (gRPC/HTTP) requests started",
|
|
}, []string{"package", "call", "call_type"}),
|
|
ClientRequestsHandled: prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
|
Name: "connect_client_handled_seconds",
|
|
Help: "Histogram of client connect (gRPC/HTTP) requests completed",
|
|
}, []string{"package", "call", "call_type", "code"}),
|
|
}
|
|
}
|
|
|
|
func NewMetricsInterceptor(metrics *ConnectMetrics) connect.UnaryInterceptorFunc {
|
|
interceptor := func(next connect.UnaryFunc) connect.UnaryFunc {
|
|
return connect.UnaryFunc(func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
|
|
now := time.Now()
|
|
callPackage, callName := splitServiceCall(req.Spec().Procedure)
|
|
callType := streamType(req.Spec().StreamType)
|
|
isClient := req.Spec().IsClient
|
|
|
|
if isClient {
|
|
metrics.ClientRequestsStarted.WithLabelValues(callPackage, callName, callType).Inc()
|
|
} else {
|
|
metrics.ServerRequestsStarted.WithLabelValues(callPackage, callName, callType).Inc()
|
|
}
|
|
|
|
resp, err := next(ctx, req)
|
|
|
|
code := codeOf(err)
|
|
if isClient {
|
|
metrics.ClientRequestsHandled.WithLabelValues(callPackage, callName, callType, code).Observe(time.Since(now).Seconds())
|
|
} else {
|
|
metrics.ServerRequestsHandled.WithLabelValues(callPackage, callName, callType, code).Observe(time.Since(now).Seconds())
|
|
}
|
|
|
|
return resp, err
|
|
})
|
|
}
|
|
|
|
return connect.UnaryInterceptorFunc(interceptor)
|
|
}
|
|
|
|
func splitServiceCall(procedure string) (string, string) {
|
|
procedure = strings.TrimPrefix(procedure, "/") // remove leading slash
|
|
if i := strings.Index(procedure, "/"); i >= 0 {
|
|
return procedure[:i], procedure[i+1:]
|
|
}
|
|
|
|
return "unknown", "unknown"
|
|
}
|
|
|
|
func streamType(st connect.StreamType) string {
|
|
switch st {
|
|
case connect.StreamTypeUnary:
|
|
return "unary"
|
|
case connect.StreamTypeClient:
|
|
return "client_stream"
|
|
case connect.StreamTypeServer:
|
|
return "server_stream"
|
|
case connect.StreamTypeBidi:
|
|
return "bidi"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
func codeOf(err error) string {
|
|
if err == nil {
|
|
return "ok"
|
|
}
|
|
return connect.CodeOf(err).String()
|
|
}
|