158 lines
5.2 KiB
Go
158 lines
5.2 KiB
Go
// Copyright 2016 Michal Witkowski. All Rights Reserved.
|
|
// See LICENSE for licensing terms.
|
|
|
|
package grpc_prometheus
|
|
|
|
import (
|
|
"time"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
prom "github.com/prometheus/client_golang/prometheus"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
type grpcType string
|
|
|
|
const (
|
|
Unary grpcType = "unary"
|
|
ClientStream grpcType = "client_stream"
|
|
ServerStream grpcType = "server_stream"
|
|
BidiStream grpcType = "bidi_stream"
|
|
)
|
|
|
|
var (
|
|
serverStartedCounter = prom.NewCounterVec(
|
|
prom.CounterOpts{
|
|
Namespace: "grpc",
|
|
Subsystem: "server",
|
|
Name: "started_total",
|
|
Help: "Total number of RPCs started on the server.",
|
|
}, []string{"grpc_type", "grpc_service", "grpc_method"})
|
|
|
|
serverHandledCounter = prom.NewCounterVec(
|
|
prom.CounterOpts{
|
|
Namespace: "grpc",
|
|
Subsystem: "server",
|
|
Name: "handled_total",
|
|
Help: "Total number of RPCs completed on the server, regardless of success or failure.",
|
|
}, []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"})
|
|
|
|
serverStreamMsgReceived = prom.NewCounterVec(
|
|
prom.CounterOpts{
|
|
Namespace: "grpc",
|
|
Subsystem: "server",
|
|
Name: "msg_received_total",
|
|
Help: "Total number of RPC stream messages received on the server.",
|
|
}, []string{"grpc_type", "grpc_service", "grpc_method"})
|
|
|
|
serverStreamMsgSent = prom.NewCounterVec(
|
|
prom.CounterOpts{
|
|
Namespace: "grpc",
|
|
Subsystem: "server",
|
|
Name: "msg_sent_total",
|
|
Help: "Total number of gRPC stream messages sent by the server.",
|
|
}, []string{"grpc_type", "grpc_service", "grpc_method"})
|
|
|
|
serverHandledHistogramEnabled = false
|
|
serverHandledHistogramOpts = prom.HistogramOpts{
|
|
Namespace: "grpc",
|
|
Subsystem: "server",
|
|
Name: "handling_seconds",
|
|
Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.",
|
|
Buckets: prom.DefBuckets,
|
|
}
|
|
serverHandledHistogram *prom.HistogramVec
|
|
)
|
|
|
|
func init() {
|
|
prom.MustRegister(serverStartedCounter)
|
|
prom.MustRegister(serverHandledCounter)
|
|
prom.MustRegister(serverStreamMsgReceived)
|
|
prom.MustRegister(serverStreamMsgSent)
|
|
}
|
|
|
|
type HistogramOption func(*prom.HistogramOpts)
|
|
|
|
// WithHistogramBuckets allows you to specify custom bucket ranges for histograms if EnableHandlingTimeHistogram is on.
|
|
func WithHistogramBuckets(buckets []float64) HistogramOption {
|
|
return func(o *prom.HistogramOpts) { o.Buckets = buckets }
|
|
}
|
|
|
|
// EnableHandlingTimeHistogram turns on recording of handling time of RPCs for server-side interceptors.
|
|
// Histogram metrics can be very expensive for Prometheus to retain and query.
|
|
func EnableHandlingTimeHistogram(opts ...HistogramOption) {
|
|
for _, o := range opts {
|
|
o(&serverHandledHistogramOpts)
|
|
}
|
|
if !serverHandledHistogramEnabled {
|
|
serverHandledHistogram = prom.NewHistogramVec(
|
|
serverHandledHistogramOpts,
|
|
[]string{"grpc_type", "grpc_service", "grpc_method"},
|
|
)
|
|
prom.Register(serverHandledHistogram)
|
|
}
|
|
serverHandledHistogramEnabled = true
|
|
}
|
|
|
|
type serverReporter struct {
|
|
rpcType grpcType
|
|
serviceName string
|
|
methodName string
|
|
startTime time.Time
|
|
}
|
|
|
|
func newServerReporter(rpcType grpcType, fullMethod string) *serverReporter {
|
|
r := &serverReporter{rpcType: rpcType}
|
|
if serverHandledHistogramEnabled {
|
|
r.startTime = time.Now()
|
|
}
|
|
r.serviceName, r.methodName = splitMethodName(fullMethod)
|
|
serverStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
|
|
return r
|
|
}
|
|
|
|
func (r *serverReporter) ReceivedMessage() {
|
|
serverStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
|
|
}
|
|
|
|
func (r *serverReporter) SentMessage() {
|
|
serverStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
|
|
}
|
|
|
|
func (r *serverReporter) Handled(code codes.Code) {
|
|
serverHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc()
|
|
if serverHandledHistogramEnabled {
|
|
serverHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds())
|
|
}
|
|
}
|
|
|
|
// preRegisterMethod is invoked on Register of a Server, allowing all gRPC services labels to be pre-populated.
|
|
func preRegisterMethod(serviceName string, mInfo *grpc.MethodInfo) {
|
|
methodName := mInfo.Name
|
|
methodType := string(typeFromMethodInfo(mInfo))
|
|
// These are just references (no increments), as just referencing will create the labels but not set values.
|
|
serverStartedCounter.GetMetricWithLabelValues(methodType, serviceName, methodName)
|
|
serverStreamMsgReceived.GetMetricWithLabelValues(methodType, serviceName, methodName)
|
|
serverStreamMsgSent.GetMetricWithLabelValues(methodType, serviceName, methodName)
|
|
if serverHandledHistogramEnabled {
|
|
serverHandledHistogram.GetMetricWithLabelValues(methodType, serviceName, methodName)
|
|
}
|
|
for _, code := range allCodes {
|
|
serverHandledCounter.GetMetricWithLabelValues(methodType, serviceName, methodName, code.String())
|
|
}
|
|
}
|
|
|
|
func typeFromMethodInfo(mInfo *grpc.MethodInfo) grpcType {
|
|
if mInfo.IsClientStream == false && mInfo.IsServerStream == false {
|
|
return Unary
|
|
}
|
|
if mInfo.IsClientStream == true && mInfo.IsServerStream == false {
|
|
return ClientStream
|
|
}
|
|
if mInfo.IsClientStream == false && mInfo.IsServerStream == true {
|
|
return ServerStream
|
|
}
|
|
return BidiStream
|
|
}
|