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.
 
 
 

112 lines
4.1 KiB

  1. // Copyright 2017 Google LLC
  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. package trace
  15. import (
  16. "context"
  17. "encoding/hex"
  18. "fmt"
  19. "cloud.google.com/go/internal/tracecontext"
  20. "google.golang.org/grpc"
  21. "google.golang.org/grpc/metadata"
  22. )
  23. const grpcMetadataKey = "grpc-trace-bin"
  24. // GRPCClientInterceptor returns a grpc.UnaryClientInterceptor that traces all outgoing requests from a gRPC client.
  25. // The calling context should already have a *trace.Span; a child span will be
  26. // created for the outgoing gRPC call. If the calling context doesn't have a span,
  27. // the call will not be traced. If the client is nil, then the interceptor just
  28. // passes through the request.
  29. //
  30. // The functionality in gRPC that this feature relies on is currently experimental.
  31. // Deprecated: see https://cloud.google.com/trace/docs/setup/go.
  32. func (c *Client) GRPCClientInterceptor() grpc.UnaryClientInterceptor {
  33. if c == nil {
  34. return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
  35. return invoker(ctx, method, req, reply, cc, opts...)
  36. }
  37. }
  38. return grpc.UnaryClientInterceptor(c.grpcUnaryInterceptor)
  39. }
  40. func (c *Client) grpcUnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
  41. // TODO: also intercept streams.
  42. span := FromContext(ctx).NewChild(method)
  43. if span == nil {
  44. span = c.NewSpan(method)
  45. }
  46. defer span.Finish()
  47. traceContext := make([]byte, tracecontext.Len)
  48. // traceID is a hex-encoded 128-bit value.
  49. // TODO(jbd): Decode trace IDs upon arrival and
  50. // represent trace IDs with 16 bytes internally.
  51. tid, err := hex.DecodeString(span.trace.traceID)
  52. if err != nil {
  53. return invoker(ctx, method, req, reply, cc, opts...)
  54. }
  55. tracecontext.Encode(traceContext, tid, span.span.SpanId, byte(span.trace.globalOptions))
  56. md, ok := metadata.FromOutgoingContext(ctx)
  57. if !ok {
  58. md = metadata.Pairs(grpcMetadataKey, string(traceContext))
  59. } else {
  60. md = md.Copy() // metadata is immutable, copy.
  61. md[grpcMetadataKey] = []string{string(traceContext)}
  62. }
  63. ctx = metadata.NewOutgoingContext(ctx, md)
  64. err = invoker(ctx, method, req, reply, cc, opts...)
  65. if err != nil {
  66. // TODO: standardize gRPC label names?
  67. span.SetLabel("error", err.Error())
  68. }
  69. return err
  70. }
  71. // GRPCServerInterceptor returns a grpc.UnaryServerInterceptor that enables the tracing of the incoming
  72. // gRPC calls. Incoming call's context can be used to extract the span on servers that enabled this option:
  73. //
  74. // span := trace.FromContext(ctx)
  75. //
  76. // If the client is nil, then the interceptor just invokes the handler.
  77. //
  78. // The functionality in gRPC that this feature relies on is currently experimental.
  79. //
  80. // Deprecated: see https://cloud.google.com/trace/docs/setup/go.
  81. func (c *Client) GRPCServerInterceptor() grpc.UnaryServerInterceptor {
  82. if c == nil {
  83. return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
  84. return handler(ctx, req)
  85. }
  86. }
  87. return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
  88. md, _ := metadata.FromIncomingContext(ctx)
  89. var traceHeader string
  90. if header, ok := md[grpcMetadataKey]; ok {
  91. traceID, spanID, opts, ok := tracecontext.Decode([]byte(header[0]))
  92. if ok {
  93. // TODO(jbd): Generate a span directly from string(traceID), spanID and opts.
  94. traceHeader = fmt.Sprintf("%x/%d;o=%d", traceID, spanID, opts)
  95. }
  96. }
  97. span := c.SpanFromHeader(info.FullMethod, traceHeader)
  98. defer span.Finish()
  99. ctx = NewContext(ctx, span)
  100. return handler(ctx, req)
  101. }
  102. }