vitess.io/vitess@v0.16.2/go/trace/trace.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package trace contains a helper interface that allows various tracing 18 // tools to be plugged in to components using this interface. If no plugin is 19 // registered, the default one makes all trace calls into no-ops. 20 package trace 21 22 import ( 23 "context" 24 "fmt" 25 "io" 26 "strings" 27 28 "github.com/spf13/pflag" 29 "google.golang.org/grpc" 30 31 "vitess.io/vitess/go/vt/log" 32 "vitess.io/vitess/go/vt/vterrors" 33 ) 34 35 // Span represents a unit of work within a trace. After creating a Span with 36 // NewSpan(), call one of the Start methods to mark the beginning of the work 37 // represented by this Span. Call Finish() when that work is done to record the 38 // Span. A Span may be reused by calling Start again. 39 type Span interface { 40 Finish() 41 // Annotate records a key/value pair associated with a Span. It should be 42 // called between Start and Finish. 43 Annotate(key string, value any) 44 } 45 46 // NewSpan creates a new Span with the currently installed tracing plugin. 47 // If no tracing plugin is installed, it returns a fake Span that does nothing. 48 func NewSpan(inCtx context.Context, label string) (Span, context.Context) { 49 parent, _ := currentTracer.FromContext(inCtx) 50 span := currentTracer.New(parent, label) 51 outCtx := currentTracer.NewContext(inCtx, span) 52 53 return span, outCtx 54 } 55 56 // NewFromString creates a new Span with the currently installed tracing plugin, extracting the span context from 57 // the provided string. 58 func NewFromString(inCtx context.Context, parent, label string) (Span, context.Context, error) { 59 span, err := currentTracer.NewFromString(parent, label) 60 if err != nil { 61 return nil, nil, err 62 } 63 outCtx := currentTracer.NewContext(inCtx, span) 64 return span, outCtx, nil 65 } 66 67 // AnnotateSQL annotates information about a sql query in the span. This is done in a way 68 // so as to not leak personally identifying information (PII), or sensitive personal information (SPI) 69 func AnnotateSQL(span Span, strippedSQL fmt.Stringer) { 70 span.Annotate("sql-statement-type", strippedSQL.String()) 71 } 72 73 // FromContext returns the Span from a Context if present. The bool return 74 // value indicates whether a Span was present in the Context. 75 func FromContext(ctx context.Context) (Span, bool) { 76 return currentTracer.FromContext(ctx) 77 } 78 79 // NewContext returns a context based on parent with a new Span value. 80 func NewContext(parent context.Context, span Span) context.Context { 81 return currentTracer.NewContext(parent, span) 82 } 83 84 // CopySpan creates a new context from parentCtx, with only the trace span 85 // copied over from spanCtx, if it has any. If not, parentCtx is returned. 86 func CopySpan(parentCtx, spanCtx context.Context) context.Context { 87 if span, ok := FromContext(spanCtx); ok { 88 return NewContext(parentCtx, span) 89 } 90 return parentCtx 91 } 92 93 // AddGrpcServerOptions adds GRPC interceptors that read the parent span from the grpc packets 94 func AddGrpcServerOptions(addInterceptors func(s grpc.StreamServerInterceptor, u grpc.UnaryServerInterceptor)) { 95 currentTracer.AddGrpcServerOptions(addInterceptors) 96 } 97 98 // AddGrpcClientOptions adds GRPC interceptors that add parent information to outgoing grpc packets 99 func AddGrpcClientOptions(addInterceptors func(s grpc.StreamClientInterceptor, u grpc.UnaryClientInterceptor)) { 100 currentTracer.AddGrpcClientOptions(addInterceptors) 101 } 102 103 // tracingService is an interface for creating spans or extracting them from Contexts. 104 type tracingService interface { 105 // New creates a new span from an existing one, if provided. The parent can also be nil 106 New(parent Span, label string) Span 107 108 // NewFromString creates a new span and uses the provided string to reconstitute the parent span 109 NewFromString(parent, label string) (Span, error) 110 111 // FromContext extracts a span from a context, making it possible to annotate the span with additional information 112 FromContext(ctx context.Context) (Span, bool) 113 114 // NewContext creates a new context containing the provided span 115 NewContext(parent context.Context, span Span) context.Context 116 117 // AddGrpcServerOptions allows a tracing system to add interceptors to grpc server traffic 118 AddGrpcServerOptions(addInterceptors func(s grpc.StreamServerInterceptor, u grpc.UnaryServerInterceptor)) 119 120 // AddGrpcClientOptions allows a tracing system to add interceptors to grpc server traffic 121 AddGrpcClientOptions(addInterceptors func(s grpc.StreamClientInterceptor, u grpc.UnaryClientInterceptor)) 122 } 123 124 // TracerFactory creates a tracing service for the service provided. It's important to close the provided io.Closer 125 // object to make sure that all spans are sent to the backend before the process exits. 126 type TracerFactory func(serviceName string) (tracingService, io.Closer, error) 127 128 var ( 129 // tracingBackendFactories should be added to by a plugin during init() to install itself 130 tracingBackendFactories = make(map[string]TracerFactory) 131 132 currentTracer tracingService = noopTracingServer{} 133 134 /* flags */ 135 136 tracingServer = "noop" 137 enableLogging bool 138 139 pluginFlags []func(fs *pflag.FlagSet) 140 ) 141 142 func RegisterFlags(fs *pflag.FlagSet) { 143 fs.StringVar(&tracingServer, "tracer", "noop", "tracing service to use") 144 fs.BoolVar(&enableLogging, "tracing-enable-logging", false, "whether to enable logging in the tracing service") 145 146 for _, fn := range pluginFlags { 147 fn(fs) 148 } 149 } 150 151 // StartTracing enables tracing for a named service 152 func StartTracing(serviceName string) io.Closer { 153 factory, ok := tracingBackendFactories[tracingServer] 154 if !ok { 155 return fail(serviceName) 156 } 157 158 tracer, closer, err := factory(serviceName) 159 if err != nil { 160 log.Error(vterrors.Wrapf(err, "failed to create a %s tracer", tracingServer)) 161 return &nilCloser{} 162 } 163 164 currentTracer = tracer 165 if tracingServer != "noop" { 166 log.Infof("successfully started tracing with [%s]", tracingServer) 167 } 168 169 return closer 170 } 171 172 func fail(serviceName string) io.Closer { 173 options := make([]string, len(tracingBackendFactories)) 174 for k := range tracingBackendFactories { 175 options = append(options, k) 176 } 177 altStr := strings.Join(options, ", ") 178 log.Errorf("no such [%s] tracing service found. alternatives are: %v", serviceName, altStr) 179 return &nilCloser{} 180 } 181 182 type nilCloser struct { 183 } 184 185 func (c *nilCloser) Close() error { return nil }