You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

209 lines
6.1 KiB

  1. // Copyright 2017, OpenCensus Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. package ocgrpc
  16. import (
  17. "context"
  18. "strconv"
  19. "strings"
  20. "sync/atomic"
  21. "time"
  22. ocstats "go.opencensus.io/stats"
  23. "go.opencensus.io/stats/view"
  24. "go.opencensus.io/tag"
  25. "google.golang.org/grpc/codes"
  26. "google.golang.org/grpc/grpclog"
  27. "google.golang.org/grpc/stats"
  28. "google.golang.org/grpc/status"
  29. )
  30. type grpcInstrumentationKey string
  31. // rpcData holds the instrumentation RPC data that is needed between the start
  32. // and end of an call. It holds the info that this package needs to keep track
  33. // of between the various GRPC events.
  34. type rpcData struct {
  35. // reqCount and respCount has to be the first words
  36. // in order to be 64-aligned on 32-bit architectures.
  37. sentCount, sentBytes, recvCount, recvBytes int64 // access atomically
  38. // startTime represents the time at which TagRPC was invoked at the
  39. // beginning of an RPC. It is an appoximation of the time when the
  40. // application code invoked GRPC code.
  41. startTime time.Time
  42. method string
  43. }
  44. // The following variables define the default hard-coded auxiliary data used by
  45. // both the default GRPC client and GRPC server metrics.
  46. var (
  47. DefaultBytesDistribution = view.Distribution(1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296)
  48. DefaultMillisecondsDistribution = view.Distribution(0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000)
  49. DefaultMessageCountDistribution = view.Distribution(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536)
  50. )
  51. // Server tags are applied to the context used to process each RPC, as well as
  52. // the measures at the end of each RPC.
  53. var (
  54. KeyServerMethod, _ = tag.NewKey("grpc_server_method")
  55. KeyServerStatus, _ = tag.NewKey("grpc_server_status")
  56. )
  57. // Client tags are applied to measures at the end of each RPC.
  58. var (
  59. KeyClientMethod, _ = tag.NewKey("grpc_client_method")
  60. KeyClientStatus, _ = tag.NewKey("grpc_client_status")
  61. )
  62. var (
  63. rpcDataKey = grpcInstrumentationKey("opencensus-rpcData")
  64. )
  65. func methodName(fullname string) string {
  66. return strings.TrimLeft(fullname, "/")
  67. }
  68. // statsHandleRPC processes the RPC events.
  69. func statsHandleRPC(ctx context.Context, s stats.RPCStats) {
  70. switch st := s.(type) {
  71. case *stats.Begin, *stats.OutHeader, *stats.InHeader, *stats.InTrailer, *stats.OutTrailer:
  72. // do nothing for client
  73. case *stats.OutPayload:
  74. handleRPCOutPayload(ctx, st)
  75. case *stats.InPayload:
  76. handleRPCInPayload(ctx, st)
  77. case *stats.End:
  78. handleRPCEnd(ctx, st)
  79. default:
  80. grpclog.Infof("unexpected stats: %T", st)
  81. }
  82. }
  83. func handleRPCOutPayload(ctx context.Context, s *stats.OutPayload) {
  84. d, ok := ctx.Value(rpcDataKey).(*rpcData)
  85. if !ok {
  86. if grpclog.V(2) {
  87. grpclog.Infoln("Failed to retrieve *rpcData from context.")
  88. }
  89. return
  90. }
  91. atomic.AddInt64(&d.sentBytes, int64(s.Length))
  92. atomic.AddInt64(&d.sentCount, 1)
  93. }
  94. func handleRPCInPayload(ctx context.Context, s *stats.InPayload) {
  95. d, ok := ctx.Value(rpcDataKey).(*rpcData)
  96. if !ok {
  97. if grpclog.V(2) {
  98. grpclog.Infoln("Failed to retrieve *rpcData from context.")
  99. }
  100. return
  101. }
  102. atomic.AddInt64(&d.recvBytes, int64(s.Length))
  103. atomic.AddInt64(&d.recvCount, 1)
  104. }
  105. func handleRPCEnd(ctx context.Context, s *stats.End) {
  106. d, ok := ctx.Value(rpcDataKey).(*rpcData)
  107. if !ok {
  108. if grpclog.V(2) {
  109. grpclog.Infoln("Failed to retrieve *rpcData from context.")
  110. }
  111. return
  112. }
  113. elapsedTime := time.Since(d.startTime)
  114. var st string
  115. if s.Error != nil {
  116. s, ok := status.FromError(s.Error)
  117. if ok {
  118. st = statusCodeToString(s)
  119. }
  120. } else {
  121. st = "OK"
  122. }
  123. latencyMillis := float64(elapsedTime) / float64(time.Millisecond)
  124. if s.Client {
  125. ocstats.RecordWithTags(ctx,
  126. []tag.Mutator{
  127. tag.Upsert(KeyClientMethod, methodName(d.method)),
  128. tag.Upsert(KeyClientStatus, st),
  129. },
  130. ClientSentBytesPerRPC.M(atomic.LoadInt64(&d.sentBytes)),
  131. ClientSentMessagesPerRPC.M(atomic.LoadInt64(&d.sentCount)),
  132. ClientReceivedMessagesPerRPC.M(atomic.LoadInt64(&d.recvCount)),
  133. ClientReceivedBytesPerRPC.M(atomic.LoadInt64(&d.recvBytes)),
  134. ClientRoundtripLatency.M(latencyMillis))
  135. } else {
  136. ocstats.RecordWithTags(ctx,
  137. []tag.Mutator{
  138. tag.Upsert(KeyServerStatus, st),
  139. },
  140. ServerSentBytesPerRPC.M(atomic.LoadInt64(&d.sentBytes)),
  141. ServerSentMessagesPerRPC.M(atomic.LoadInt64(&d.sentCount)),
  142. ServerReceivedMessagesPerRPC.M(atomic.LoadInt64(&d.recvCount)),
  143. ServerReceivedBytesPerRPC.M(atomic.LoadInt64(&d.recvBytes)),
  144. ServerLatency.M(latencyMillis))
  145. }
  146. }
  147. func statusCodeToString(s *status.Status) string {
  148. // see https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
  149. switch c := s.Code(); c {
  150. case codes.OK:
  151. return "OK"
  152. case codes.Canceled:
  153. return "CANCELLED"
  154. case codes.Unknown:
  155. return "UNKNOWN"
  156. case codes.InvalidArgument:
  157. return "INVALID_ARGUMENT"
  158. case codes.DeadlineExceeded:
  159. return "DEADLINE_EXCEEDED"
  160. case codes.NotFound:
  161. return "NOT_FOUND"
  162. case codes.AlreadyExists:
  163. return "ALREADY_EXISTS"
  164. case codes.PermissionDenied:
  165. return "PERMISSION_DENIED"
  166. case codes.ResourceExhausted:
  167. return "RESOURCE_EXHAUSTED"
  168. case codes.FailedPrecondition:
  169. return "FAILED_PRECONDITION"
  170. case codes.Aborted:
  171. return "ABORTED"
  172. case codes.OutOfRange:
  173. return "OUT_OF_RANGE"
  174. case codes.Unimplemented:
  175. return "UNIMPLEMENTED"
  176. case codes.Internal:
  177. return "INTERNAL"
  178. case codes.Unavailable:
  179. return "UNAVAILABLE"
  180. case codes.DataLoss:
  181. return "DATA_LOSS"
  182. case codes.Unauthenticated:
  183. return "UNAUTHENTICATED"
  184. default:
  185. return "CODE_" + strconv.FormatInt(int64(c), 10)
  186. }
  187. }