server_reporter.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // Copyright 2016 Michal Witkowski. All Rights Reserved.
  2. // See LICENSE for licensing terms.
  3. package grpc_prometheus
  4. import (
  5. "time"
  6. "google.golang.org/grpc/codes"
  7. prom "github.com/prometheus/client_golang/prometheus"
  8. "google.golang.org/grpc"
  9. )
  10. type grpcType string
  11. const (
  12. Unary grpcType = "unary"
  13. ClientStream grpcType = "client_stream"
  14. ServerStream grpcType = "server_stream"
  15. BidiStream grpcType = "bidi_stream"
  16. )
  17. var (
  18. serverStartedCounter = prom.NewCounterVec(
  19. prom.CounterOpts{
  20. Namespace: "grpc",
  21. Subsystem: "server",
  22. Name: "started_total",
  23. Help: "Total number of RPCs started on the server.",
  24. }, []string{"grpc_type", "grpc_service", "grpc_method"})
  25. serverHandledCounter = prom.NewCounterVec(
  26. prom.CounterOpts{
  27. Namespace: "grpc",
  28. Subsystem: "server",
  29. Name: "handled_total",
  30. Help: "Total number of RPCs completed on the server, regardless of success or failure.",
  31. }, []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"})
  32. serverStreamMsgReceived = prom.NewCounterVec(
  33. prom.CounterOpts{
  34. Namespace: "grpc",
  35. Subsystem: "server",
  36. Name: "msg_received_total",
  37. Help: "Total number of RPC stream messages received on the server.",
  38. }, []string{"grpc_type", "grpc_service", "grpc_method"})
  39. serverStreamMsgSent = prom.NewCounterVec(
  40. prom.CounterOpts{
  41. Namespace: "grpc",
  42. Subsystem: "server",
  43. Name: "msg_sent_total",
  44. Help: "Total number of gRPC stream messages sent by the server.",
  45. }, []string{"grpc_type", "grpc_service", "grpc_method"})
  46. serverHandledHistogramEnabled = false
  47. serverHandledHistogramOpts = prom.HistogramOpts{
  48. Namespace: "grpc",
  49. Subsystem: "server",
  50. Name: "handling_seconds",
  51. Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.",
  52. Buckets: prom.DefBuckets,
  53. }
  54. serverHandledHistogram *prom.HistogramVec
  55. )
  56. func init() {
  57. prom.MustRegister(serverStartedCounter)
  58. prom.MustRegister(serverHandledCounter)
  59. prom.MustRegister(serverStreamMsgReceived)
  60. prom.MustRegister(serverStreamMsgSent)
  61. }
  62. type HistogramOption func(*prom.HistogramOpts)
  63. // WithHistogramBuckets allows you to specify custom bucket ranges for histograms if EnableHandlingTimeHistogram is on.
  64. func WithHistogramBuckets(buckets []float64) HistogramOption {
  65. return func(o *prom.HistogramOpts) { o.Buckets = buckets }
  66. }
  67. // EnableHandlingTimeHistogram turns on recording of handling time of RPCs for server-side interceptors.
  68. // Histogram metrics can be very expensive for Prometheus to retain and query.
  69. func EnableHandlingTimeHistogram(opts ...HistogramOption) {
  70. for _, o := range opts {
  71. o(&serverHandledHistogramOpts)
  72. }
  73. if !serverHandledHistogramEnabled {
  74. serverHandledHistogram = prom.NewHistogramVec(
  75. serverHandledHistogramOpts,
  76. []string{"grpc_type", "grpc_service", "grpc_method"},
  77. )
  78. prom.Register(serverHandledHistogram)
  79. }
  80. serverHandledHistogramEnabled = true
  81. }
  82. type serverReporter struct {
  83. rpcType grpcType
  84. serviceName string
  85. methodName string
  86. startTime time.Time
  87. }
  88. func newServerReporter(rpcType grpcType, fullMethod string) *serverReporter {
  89. r := &serverReporter{rpcType: rpcType}
  90. if serverHandledHistogramEnabled {
  91. r.startTime = time.Now()
  92. }
  93. r.serviceName, r.methodName = splitMethodName(fullMethod)
  94. serverStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
  95. return r
  96. }
  97. func (r *serverReporter) ReceivedMessage() {
  98. serverStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
  99. }
  100. func (r *serverReporter) SentMessage() {
  101. serverStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
  102. }
  103. func (r *serverReporter) Handled(code codes.Code) {
  104. serverHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc()
  105. if serverHandledHistogramEnabled {
  106. serverHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds())
  107. }
  108. }
  109. // preRegisterMethod is invoked on Register of a Server, allowing all gRPC services labels to be pre-populated.
  110. func preRegisterMethod(serviceName string, mInfo *grpc.MethodInfo) {
  111. methodName := mInfo.Name
  112. methodType := string(typeFromMethodInfo(mInfo))
  113. // These are just references (no increments), as just referencing will create the labels but not set values.
  114. serverStartedCounter.GetMetricWithLabelValues(methodType, serviceName, methodName)
  115. serverStreamMsgReceived.GetMetricWithLabelValues(methodType, serviceName, methodName)
  116. serverStreamMsgSent.GetMetricWithLabelValues(methodType, serviceName, methodName)
  117. if serverHandledHistogramEnabled {
  118. serverHandledHistogram.GetMetricWithLabelValues(methodType, serviceName, methodName)
  119. }
  120. for _, code := range allCodes {
  121. serverHandledCounter.GetMetricWithLabelValues(methodType, serviceName, methodName, code.String())
  122. }
  123. }
  124. func typeFromMethodInfo(mInfo *grpc.MethodInfo) grpcType {
  125. if mInfo.IsClientStream == false && mInfo.IsServerStream == false {
  126. return Unary
  127. }
  128. if mInfo.IsClientStream == true && mInfo.IsServerStream == false {
  129. return ClientStream
  130. }
  131. if mInfo.IsClientStream == false && mInfo.IsServerStream == true {
  132. return ServerStream
  133. }
  134. return BidiStream
  135. }