github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/tracing/tracer.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package tracing
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"math/rand"
    17  	"regexp"
    18  	"sort"
    19  	"strconv"
    20  	"strings"
    21  	"sync/atomic"
    22  	"time"
    23  	"unsafe"
    24  
    25  	"github.com/cockroachdb/cockroach/pkg/settings"
    26  	"github.com/cockroachdb/cockroach/pkg/util/caller"
    27  	"github.com/cockroachdb/cockroach/pkg/util/envutil"
    28  	"github.com/cockroachdb/errors"
    29  	"github.com/cockroachdb/logtags"
    30  	opentracing "github.com/opentracing/opentracing-go"
    31  	"golang.org/x/net/trace"
    32  )
    33  
    34  // Snowball is set as Baggage on traces which are used for snowball tracing.
    35  const Snowball = "sb"
    36  
    37  // maxLogsPerSpan limits the number of logs in a Span; use a comfortable limit.
    38  const maxLogsPerSpan = 1000
    39  
    40  // These constants are used to form keys to represent tracing context
    41  // information in carriers supporting opentracing.HTTPHeaders format.
    42  const (
    43  	prefixTracerState = "crdb-tracer-"
    44  	prefixBaggage     = "crdb-baggage-"
    45  	// prefixShadow is prepended to the keys for the context of the shadow tracer
    46  	// (e.g. LightStep).
    47  	prefixShadow = "crdb-shadow-"
    48  
    49  	fieldNameTraceID = prefixTracerState + "traceid"
    50  	fieldNameSpanID  = prefixTracerState + "spanid"
    51  	// fieldNameShadow is the name of the shadow tracer.
    52  	fieldNameShadowType = prefixTracerState + "shadowtype"
    53  )
    54  
    55  var enableNetTrace = settings.RegisterPublicBoolSetting(
    56  	"trace.debug.enable",
    57  	"if set, traces for recent requests can be seen in the /debug page",
    58  	false,
    59  )
    60  
    61  var lightstepToken = settings.RegisterPublicStringSetting(
    62  	"trace.lightstep.token",
    63  	"if set, traces go to Lightstep using this token",
    64  	envutil.EnvOrDefaultString("COCKROACH_TEST_LIGHTSTEP_TOKEN", ""),
    65  )
    66  
    67  var zipkinCollector = settings.RegisterPublicStringSetting(
    68  	"trace.zipkin.collector",
    69  	"if set, traces go to the given Zipkin instance (example: '127.0.0.1:9411'); ignored if trace.lightstep.token is set",
    70  	envutil.EnvOrDefaultString("COCKROACH_TEST_ZIPKIN_COLLECTOR", ""),
    71  )
    72  
    73  // Tracer is our own custom implementation of opentracing.Tracer. It supports:
    74  //
    75  //  - forwarding events to x/net/trace instances
    76  //
    77  //  - recording traces. Recording is started automatically for spans that have
    78  //    the Snowball baggage and can be started explicitly as well. Recorded
    79  //    events can be retrieved at any time.
    80  //
    81  //  - lightstep traces. This is implemented by maintaining a "shadow" lightstep
    82  //    span inside each of our spans.
    83  //
    84  // Even when tracing is disabled, we still use this Tracer (with x/net/trace and
    85  // lightstep disabled) because of its recording capability (snowball
    86  // tracing needs to work in all cases).
    87  //
    88  // Tracer is currently stateless so we could have a single instance; however,
    89  // this won't be the case if the cluster settings move away from using global
    90  // state.
    91  type Tracer struct {
    92  	// Preallocated noopSpan, used to avoid creating spans when we are not using
    93  	// x/net/trace or lightstep and we are not recording.
    94  	noopSpan noopSpan
    95  
    96  	// If forceRealSpans is set, this Tracer will always create real spans (never
    97  	// noopSpans), regardless of the recording or lightstep configuration. Used
    98  	// by tests for situations when they need to indirectly create spans and don't
    99  	// have the option of passing the Recordable option to their constructor.
   100  	forceRealSpans bool
   101  
   102  	// True if tracing to the debug/requests endpoint. Accessed via t.useNetTrace().
   103  	_useNetTrace int32 // updated atomically
   104  
   105  	// Pointer to shadowTracer, if using one.
   106  	shadowTracer unsafe.Pointer
   107  }
   108  
   109  var _ opentracing.Tracer = &Tracer{}
   110  
   111  // NewTracer creates a Tracer. It initially tries to run with minimal overhead
   112  // and collects essentially nothing; use Configure() to enable various tracing
   113  // backends.
   114  func NewTracer() *Tracer {
   115  	t := &Tracer{}
   116  	t.noopSpan.tracer = t
   117  	return t
   118  }
   119  
   120  // Configure sets up the Tracer according to the cluster settings (and keeps
   121  // it updated if they change).
   122  func (t *Tracer) Configure(sv *settings.Values) {
   123  	reconfigure := func() {
   124  		if lsToken := lightstepToken.Get(sv); lsToken != "" {
   125  			t.setShadowTracer(createLightStepTracer(lsToken))
   126  		} else if zipkinAddr := zipkinCollector.Get(sv); zipkinAddr != "" {
   127  			t.setShadowTracer(createZipkinTracer(zipkinAddr))
   128  		} else {
   129  			t.setShadowTracer(nil, nil)
   130  		}
   131  		var nt int32
   132  		if enableNetTrace.Get(sv) {
   133  			nt = 1
   134  		}
   135  		atomic.StoreInt32(&t._useNetTrace, nt)
   136  	}
   137  
   138  	reconfigure()
   139  
   140  	enableNetTrace.SetOnChange(sv, reconfigure)
   141  	lightstepToken.SetOnChange(sv, reconfigure)
   142  	zipkinCollector.SetOnChange(sv, reconfigure)
   143  }
   144  
   145  func (t *Tracer) useNetTrace() bool {
   146  	return atomic.LoadInt32(&t._useNetTrace) != 0
   147  }
   148  
   149  // Close cleans up any resources associated with a Tracer.
   150  func (t *Tracer) Close() {
   151  	// Clean up any shadow tracer.
   152  	t.setShadowTracer(nil, nil)
   153  }
   154  
   155  // SetForceRealSpans sets forceRealSpans option to v and returns the previous
   156  // value.
   157  func (t *Tracer) SetForceRealSpans(v bool) bool {
   158  	prevVal := t.forceRealSpans
   159  	t.forceRealSpans = v
   160  	return prevVal
   161  }
   162  
   163  func (t *Tracer) setShadowTracer(manager shadowTracerManager, tr opentracing.Tracer) {
   164  	var shadow *shadowTracer
   165  	if manager != nil {
   166  		shadow = &shadowTracer{
   167  			Tracer:  tr,
   168  			manager: manager,
   169  		}
   170  	}
   171  	if old := atomic.SwapPointer(&t.shadowTracer, unsafe.Pointer(shadow)); old != nil {
   172  		(*shadowTracer)(old).Close()
   173  	}
   174  }
   175  
   176  func (t *Tracer) getShadowTracer() *shadowTracer {
   177  	return (*shadowTracer)(atomic.LoadPointer(&t.shadowTracer))
   178  }
   179  
   180  type recordableOption struct{}
   181  
   182  // Apply is part of the opentracing.StartSpanOption interface.
   183  func (recordableOption) Apply(*opentracing.StartSpanOptions) {}
   184  
   185  // Recordable is a StartSpanOption that forces creation of a real span.
   186  //
   187  // When tracing is disabled all spans are noopSpans; these spans aren't
   188  // capable of recording, so this option should be passed to StartSpan if the
   189  // caller wants to be able to call StartRecording on the resulting span.
   190  var Recordable opentracing.StartSpanOption = recordableOption{}
   191  
   192  // StartSpan is part of the opentracing.Tracer interface.
   193  //
   194  // Avoid using this method in favor of Tracer.StartRootSpan() or
   195  // tracing.StartChildSpan() (or higher-level methods like EnsureContext() or
   196  // AmbientContext.AnnotateCtxWithSpan()) when possible. Those are more efficient
   197  // because they don't have to use the StartSpanOption interface and so a bunch
   198  // of allocations are avoided. However, we need to implement this standard
   199  // StartSpan() too because grpc integrates with a generic opentracing.Tracer.
   200  func (t *Tracer) StartSpan(
   201  	operationName string, opts ...opentracing.StartSpanOption,
   202  ) opentracing.Span {
   203  	// Fast paths to avoid the allocation of StartSpanOptions below when tracing
   204  	// is disabled: if we have no options or a single SpanReference (the common
   205  	// case) with a noop context, return a noop span now.
   206  	if len(opts) == 1 {
   207  		if o, ok := opts[0].(opentracing.SpanReference); ok {
   208  			if IsNoopContext(o.ReferencedContext) {
   209  				return &t.noopSpan
   210  			}
   211  		}
   212  	}
   213  
   214  	shadowTr := t.getShadowTracer()
   215  
   216  	if len(opts) == 0 && !t.useNetTrace() && shadowTr == nil && !t.forceRealSpans {
   217  		return &t.noopSpan
   218  	}
   219  
   220  	var sso opentracing.StartSpanOptions
   221  	var recordable bool
   222  	var logTags *logtags.Buffer
   223  	for _, o := range opts {
   224  		switch to := o.(type) {
   225  		case recordableOption:
   226  			recordable = true
   227  		case *logTagsOption:
   228  			// NOTE: calling logTagsOption.Apply() would be functionally equivalent,
   229  			// but less efficient.
   230  			logTags = (*logtags.Buffer)(to)
   231  		default:
   232  			o.Apply(&sso)
   233  		}
   234  	}
   235  
   236  	var hasParent bool
   237  	var parentType opentracing.SpanReferenceType
   238  	var parentCtx *spanContext
   239  	var recordingGroup *spanGroup
   240  	var recordingType RecordingType
   241  
   242  	for _, r := range sso.References {
   243  		if r.Type != opentracing.ChildOfRef && r.Type != opentracing.FollowsFromRef {
   244  			continue
   245  		}
   246  		if r.ReferencedContext == nil {
   247  			continue
   248  		}
   249  		if IsNoopContext(r.ReferencedContext) {
   250  			continue
   251  		}
   252  		hasParent = true
   253  		parentType = r.Type
   254  		parentCtx = r.ReferencedContext.(*spanContext)
   255  		if parentCtx.recordingGroup != nil {
   256  			recordingGroup = parentCtx.recordingGroup
   257  			recordingType = parentCtx.recordingType
   258  		} else if parentCtx.Baggage[Snowball] != "" {
   259  			// Automatically enable recording if we have the Snowball baggage item.
   260  			recordingGroup = new(spanGroup)
   261  			recordingType = SnowballRecording
   262  		}
   263  		// TODO(radu): can we do something for multiple references?
   264  		break
   265  	}
   266  	if hasParent {
   267  		// We use the parent's shadow tracer, to avoid inconsistency inside a
   268  		// trace when the shadow tracer changes.
   269  		shadowTr = parentCtx.shadowTr
   270  	}
   271  
   272  	// If tracing is disabled, the Recordable option wasn't passed, and we're not
   273  	// part of a recording or snowball trace, avoid overhead and return a noop
   274  	// span.
   275  	if !recordable && recordingGroup == nil && shadowTr == nil && !t.useNetTrace() && !t.forceRealSpans {
   276  		return &t.noopSpan
   277  	}
   278  
   279  	s := &span{
   280  		tracer:    t,
   281  		operation: operationName,
   282  		startTime: sso.StartTime,
   283  		logTags:   logTags,
   284  	}
   285  	if s.startTime.IsZero() {
   286  		s.startTime = time.Now()
   287  	}
   288  	s.mu.duration = -1
   289  
   290  	if !hasParent {
   291  		// No parent Span; allocate new trace id.
   292  		s.TraceID = uint64(rand.Int63())
   293  	} else {
   294  		s.TraceID = parentCtx.TraceID
   295  	}
   296  	s.SpanID = uint64(rand.Int63())
   297  
   298  	// Start recording if necessary.
   299  	if recordingGroup != nil {
   300  		s.enableRecording(recordingGroup, recordingType)
   301  	}
   302  
   303  	if t.useNetTrace() {
   304  		s.netTr = trace.New("tracing", operationName)
   305  		s.netTr.SetMaxEvents(maxLogsPerSpan)
   306  	}
   307  
   308  	if hasParent {
   309  		s.parentSpanID = parentCtx.SpanID
   310  		// Copy baggage from parent.
   311  		if l := len(parentCtx.Baggage); l > 0 {
   312  			s.mu.Baggage = make(map[string]string, l)
   313  			for k, v := range parentCtx.Baggage {
   314  				s.mu.Baggage[k] = v
   315  			}
   316  		}
   317  	}
   318  
   319  	for k, v := range sso.Tags {
   320  		s.SetTag(k, v)
   321  	}
   322  
   323  	// Copy baggage items to tags so they show up in the shadow tracer UI,
   324  	// x/net/trace, or recordings.
   325  	for k, v := range s.mu.Baggage {
   326  		s.SetTag(k, v)
   327  	}
   328  
   329  	if shadowTr != nil {
   330  		var parentShadowCtx opentracing.SpanContext
   331  		if hasParent {
   332  			parentShadowCtx = parentCtx.shadowCtx
   333  		}
   334  		linkShadowSpan(s, shadowTr, parentShadowCtx, parentType)
   335  	}
   336  
   337  	return s
   338  }
   339  
   340  // RecordableOpt specifies whether a root span should be recordable.
   341  type RecordableOpt bool
   342  
   343  const (
   344  	// RecordableSpan means that the root span will be recordable. This means that
   345  	// a real span will be created (and so it carries a cost).
   346  	RecordableSpan RecordableOpt = true
   347  	// NonRecordableSpan means that the root span will not be recordable. This
   348  	// means that the static noop span might be returned.
   349  	NonRecordableSpan RecordableOpt = false
   350  )
   351  
   352  // AlwaysTrace returns true if operations should be traced regardless of the
   353  // context.
   354  func (t *Tracer) AlwaysTrace() bool {
   355  	shadowTracer := t.getShadowTracer()
   356  	return t.useNetTrace() || shadowTracer != nil || t.forceRealSpans
   357  }
   358  
   359  // StartRootSpan creates a root span. This is functionally equivalent to:
   360  // parentSpan.Tracer().(*Tracer).StartSpan(opName, LogTags(...), [Recordable])
   361  // Compared to that, it's more efficient, particularly in terms of memory
   362  // allocations because the opentracing.StartSpanOption interface is not used.
   363  //
   364  // logTags can be nil.
   365  func (t *Tracer) StartRootSpan(
   366  	opName string, logTags *logtags.Buffer, recordable RecordableOpt,
   367  ) opentracing.Span {
   368  	// In the usual case, we return noopSpan.
   369  	if !t.AlwaysTrace() && recordable == NonRecordableSpan {
   370  		return &t.noopSpan
   371  	}
   372  
   373  	s := &span{
   374  		spanMeta: spanMeta{
   375  			TraceID: uint64(rand.Int63()),
   376  			SpanID:  uint64(rand.Int63()),
   377  		},
   378  		tracer:    t,
   379  		operation: opName,
   380  		startTime: time.Now(),
   381  		logTags:   logTags,
   382  	}
   383  	s.mu.duration = -1
   384  
   385  	shadowTracer := t.getShadowTracer()
   386  	if shadowTracer != nil {
   387  		linkShadowSpan(
   388  			s, shadowTracer, nil, /* parentShadowCtx */
   389  			opentracing.SpanReferenceType(0) /* parentType - ignored*/)
   390  	}
   391  
   392  	if t.useNetTrace() {
   393  		var tags []logtags.Tag
   394  		if logTags != nil {
   395  			tags = logTags.Get()
   396  		}
   397  
   398  		s.netTr = trace.New("tracing", opName)
   399  		s.netTr.SetMaxEvents(maxLogsPerSpan)
   400  		for i := range tags {
   401  			tag := &tags[i]
   402  			s.netTr.LazyPrintf("%s:%v", tag.Key(), tag.Value())
   403  		}
   404  	}
   405  
   406  	return s
   407  }
   408  
   409  // StartChildSpan creates a child span of the given parent span. This is
   410  // functionally equivalent to:
   411  // parentSpan.Tracer().(*Tracer).StartSpan(opName, opentracing.ChildOf(parentSpan.Context()))
   412  // Compared to that, it's more efficient, particularly in terms of memory
   413  // allocations; among others, it saves the call to parentSpan.Context.
   414  //
   415  // This only works for creating children of local parents (i.e. the caller needs
   416  // to have a reference to the parent span).
   417  //
   418  // If separateRecording is true and the parent span is recording, we start a
   419  // new recording for the child span. If separateRecording is false (the
   420  // default), then the child span will be part of the same recording.
   421  func StartChildSpan(
   422  	opName string, parentSpan opentracing.Span, logTags *logtags.Buffer, separateRecording bool,
   423  ) opentracing.Span {
   424  	tr := parentSpan.Tracer().(*Tracer)
   425  	// If tracing is disabled, avoid overhead and return a noop span.
   426  	if IsBlackHoleSpan(parentSpan) {
   427  		return &tr.noopSpan
   428  	}
   429  
   430  	pSpan := parentSpan.(*span)
   431  
   432  	s := &span{
   433  		tracer:       tr,
   434  		operation:    opName,
   435  		startTime:    time.Now(),
   436  		parentSpanID: pSpan.SpanID,
   437  		logTags:      logTags,
   438  	}
   439  
   440  	// Copy baggage from parent.
   441  	pSpan.mu.Lock()
   442  	if l := len(pSpan.mu.Baggage); l > 0 {
   443  		s.mu.Baggage = make(map[string]string, l)
   444  		for k, v := range pSpan.mu.Baggage {
   445  			s.mu.Baggage[k] = v
   446  		}
   447  	}
   448  
   449  	s.TraceID = pSpan.TraceID
   450  	s.SpanID = uint64(rand.Int63())
   451  
   452  	if pSpan.shadowTr != nil {
   453  		linkShadowSpan(s, pSpan.shadowTr, pSpan.shadowSpan.Context(), opentracing.ChildOfRef)
   454  	}
   455  
   456  	// Start recording if necessary.
   457  	if pSpan.isRecording() {
   458  		recordingGroup := pSpan.mu.recordingGroup
   459  		if separateRecording {
   460  			recordingGroup = new(spanGroup)
   461  		}
   462  		s.enableRecording(recordingGroup, pSpan.mu.recordingType)
   463  	}
   464  
   465  	if pSpan.netTr != nil {
   466  		s.netTr = trace.New("tracing", opName)
   467  		s.netTr.SetMaxEvents(maxLogsPerSpan)
   468  		if startTags := s.logTags; startTags != nil {
   469  			tags := startTags.Get()
   470  			for i := range tags {
   471  				tag := &tags[i]
   472  				s.netTr.LazyPrintf("%s:%v", tagName(tag.Key()), tag.Value())
   473  			}
   474  		}
   475  	}
   476  
   477  	if pSpan.netTr != nil || pSpan.shadowTr != nil {
   478  		// Copy baggage items to tags so they show up in the shadow tracer UI or x/net/trace.
   479  		for k, v := range s.mu.Baggage {
   480  			s.SetTag(k, v)
   481  		}
   482  	}
   483  
   484  	pSpan.mu.Unlock()
   485  	return s
   486  }
   487  
   488  type textMapWriterFn func(key, val string)
   489  
   490  var _ opentracing.TextMapWriter = textMapWriterFn(nil)
   491  
   492  // Set is part of the opentracing.TextMapWriter interface.
   493  func (fn textMapWriterFn) Set(key, val string) {
   494  	fn(key, val)
   495  }
   496  
   497  // Inject is part of the opentracing.Tracer interface.
   498  func (t *Tracer) Inject(
   499  	osc opentracing.SpanContext, format interface{}, carrier interface{},
   500  ) error {
   501  	if IsNoopContext(osc) {
   502  		// Fast path when tracing is disabled. Extract will accept an empty map as a
   503  		// noop context.
   504  		return nil
   505  	}
   506  
   507  	// We only support the HTTPHeaders/TextMap format.
   508  	if format != opentracing.HTTPHeaders && format != opentracing.TextMap {
   509  		return opentracing.ErrUnsupportedFormat
   510  	}
   511  
   512  	mapWriter, ok := carrier.(opentracing.TextMapWriter)
   513  	if !ok {
   514  		return opentracing.ErrInvalidCarrier
   515  	}
   516  
   517  	sc, ok := osc.(*spanContext)
   518  	if !ok {
   519  		return opentracing.ErrInvalidSpanContext
   520  	}
   521  
   522  	mapWriter.Set(fieldNameTraceID, strconv.FormatUint(sc.TraceID, 16))
   523  	mapWriter.Set(fieldNameSpanID, strconv.FormatUint(sc.SpanID, 16))
   524  
   525  	for k, v := range sc.Baggage {
   526  		mapWriter.Set(prefixBaggage+k, v)
   527  	}
   528  
   529  	if sc.shadowTr != nil {
   530  		mapWriter.Set(fieldNameShadowType, sc.shadowTr.Typ())
   531  		// Encapsulate the shadow text map, prepending a prefix to the keys.
   532  		if err := sc.shadowTr.Inject(sc.shadowCtx, format, textMapWriterFn(func(key, val string) {
   533  			mapWriter.Set(prefixShadow+key, val)
   534  		})); err != nil {
   535  			return err
   536  		}
   537  	}
   538  
   539  	return nil
   540  }
   541  
   542  type textMapReaderFn func(handler func(key, val string) error) error
   543  
   544  var _ opentracing.TextMapReader = textMapReaderFn(nil)
   545  
   546  // ForeachKey is part of the opentracing.TextMapReader interface.
   547  func (fn textMapReaderFn) ForeachKey(handler func(key, val string) error) error {
   548  	return fn(handler)
   549  }
   550  
   551  // Extract is part of the opentracing.Tracer interface.
   552  // It always returns a valid context, even in error cases (this is assumed by the
   553  // grpc-opentracing interceptor).
   554  func (t *Tracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
   555  	// We only support the HTTPHeaders/TextMap format.
   556  	if format != opentracing.HTTPHeaders && format != opentracing.TextMap {
   557  		return noopSpanContext{}, opentracing.ErrUnsupportedFormat
   558  	}
   559  
   560  	mapReader, ok := carrier.(opentracing.TextMapReader)
   561  	if !ok {
   562  		return noopSpanContext{}, opentracing.ErrInvalidCarrier
   563  	}
   564  
   565  	var sc spanContext
   566  	var shadowType string
   567  	var shadowCarrier opentracing.TextMapCarrier
   568  
   569  	err := mapReader.ForeachKey(func(k, v string) error {
   570  		switch k = strings.ToLower(k); k {
   571  		case fieldNameTraceID:
   572  			var err error
   573  			sc.TraceID, err = strconv.ParseUint(v, 16, 64)
   574  			if err != nil {
   575  				return opentracing.ErrSpanContextCorrupted
   576  			}
   577  		case fieldNameSpanID:
   578  			var err error
   579  			sc.SpanID, err = strconv.ParseUint(v, 16, 64)
   580  			if err != nil {
   581  				return opentracing.ErrSpanContextCorrupted
   582  			}
   583  		case fieldNameShadowType:
   584  			shadowType = v
   585  		default:
   586  			if strings.HasPrefix(k, prefixBaggage) {
   587  				if sc.Baggage == nil {
   588  					sc.Baggage = make(map[string]string)
   589  				}
   590  				sc.Baggage[strings.TrimPrefix(k, prefixBaggage)] = v
   591  			} else if strings.HasPrefix(k, prefixShadow) {
   592  				if shadowCarrier == nil {
   593  					shadowCarrier = make(opentracing.TextMapCarrier)
   594  				}
   595  				// We build a shadow textmap with the original shadow keys.
   596  				shadowCarrier.Set(strings.TrimPrefix(k, prefixShadow), v)
   597  			}
   598  		}
   599  		return nil
   600  	})
   601  	if err != nil {
   602  		return noopSpanContext{}, err
   603  	}
   604  	if sc.TraceID == 0 && sc.SpanID == 0 {
   605  		return noopSpanContext{}, nil
   606  	}
   607  
   608  	if shadowType != "" {
   609  		// Using a shadow tracer only works if all hosts use the same shadow tracer.
   610  		// If that's not the case, ignore the shadow context.
   611  		if shadowTr := t.getShadowTracer(); shadowTr != nil &&
   612  			strings.EqualFold(shadowType, shadowTr.Typ()) {
   613  			sc.shadowTr = shadowTr
   614  			// Extract the shadow context using the un-encapsulated textmap.
   615  			sc.shadowCtx, err = shadowTr.Extract(format, shadowCarrier)
   616  			if err != nil {
   617  				return noopSpanContext{}, err
   618  			}
   619  		}
   620  	}
   621  
   622  	return &sc, nil
   623  }
   624  
   625  // FinishSpan closes the given span (if not nil). It is a convenience wrapper
   626  // for span.Finish() which tolerates nil spans.
   627  func FinishSpan(span opentracing.Span) {
   628  	if span != nil {
   629  		span.Finish()
   630  	}
   631  }
   632  
   633  // ForkCtxSpan checks if ctx has a Span open; if it does, it creates a new Span
   634  // that "follows from" the original Span. This allows the resulting context to be
   635  // used in an async task that might outlive the original operation.
   636  //
   637  // Returns the new context and the new span (if any). The span should be
   638  // closed via FinishSpan.
   639  //
   640  // See also ChildSpan() for a "parent-child relationship".
   641  func ForkCtxSpan(ctx context.Context, opName string) (context.Context, opentracing.Span) {
   642  	if span := opentracing.SpanFromContext(ctx); span != nil {
   643  		if _, noop := span.(*noopSpan); noop {
   644  			// Optimization: avoid ContextWithSpan call if tracing is disabled.
   645  			return ctx, span
   646  		}
   647  		tr := span.Tracer()
   648  		if IsBlackHoleSpan(span) {
   649  			ns := &tr.(*Tracer).noopSpan
   650  			return opentracing.ContextWithSpan(ctx, ns), ns
   651  		}
   652  		newSpan := tr.StartSpan(opName, opentracing.FollowsFrom(span.Context()), LogTagsFromCtx(ctx))
   653  		return opentracing.ContextWithSpan(ctx, newSpan), newSpan
   654  	}
   655  	return ctx, nil
   656  }
   657  
   658  // ChildSpan opens a span as a child of the current span in the context (if
   659  // there is one).
   660  // The span's tags are inherited from the ctx's log tags automatically.
   661  //
   662  // Returns the new context and the new span (if any). The span should be
   663  // closed via FinishSpan.
   664  func ChildSpan(ctx context.Context, opName string) (context.Context, opentracing.Span) {
   665  	return childSpan(ctx, opName, false /* separateRecording */)
   666  }
   667  
   668  // ChildSpanSeparateRecording is like ChildSpan but the new span has separate
   669  // recording (see StartChildSpan).
   670  func ChildSpanSeparateRecording(
   671  	ctx context.Context, opName string,
   672  ) (context.Context, opentracing.Span) {
   673  	return childSpan(ctx, opName, true /* separateRecording */)
   674  }
   675  
   676  func childSpan(
   677  	ctx context.Context, opName string, separateRecording bool,
   678  ) (context.Context, opentracing.Span) {
   679  	span := opentracing.SpanFromContext(ctx)
   680  	if span == nil {
   681  		return ctx, nil
   682  	}
   683  	if _, noop := span.(*noopSpan); noop {
   684  		// Optimization: avoid ContextWithSpan call if tracing is disabled.
   685  		return ctx, span
   686  	}
   687  	tr := span.Tracer()
   688  	if IsBlackHoleSpan(span) {
   689  		ns := &tr.(*Tracer).noopSpan
   690  		return opentracing.ContextWithSpan(ctx, ns), ns
   691  	}
   692  	newSpan := StartChildSpan(opName, span, logtags.FromContext(ctx), separateRecording)
   693  	return opentracing.ContextWithSpan(ctx, newSpan), newSpan
   694  }
   695  
   696  // EnsureContext checks whether the given context.Context contains a Span. If
   697  // not, it creates one using the provided Tracer and wraps it in the returned
   698  // Span. The returned closure must be called after the request has been fully
   699  // processed.
   700  //
   701  // Note that, if there's already a span in the context, this method does nothing
   702  // even if the current context's log tags are different from that span's tags.
   703  func EnsureContext(
   704  	ctx context.Context, tracer opentracing.Tracer, opName string,
   705  ) (context.Context, func()) {
   706  	if opentracing.SpanFromContext(ctx) == nil {
   707  		sp := tracer.(*Tracer).StartRootSpan(opName, logtags.FromContext(ctx), NonRecordableSpan)
   708  		return opentracing.ContextWithSpan(ctx, sp), sp.Finish
   709  	}
   710  	return ctx, func() {}
   711  }
   712  
   713  // EnsureChildSpan is the same as EnsureContext, except it creates a child
   714  // span for the input context if the input context already has an active
   715  // trace.
   716  //
   717  // The caller is responsible for closing the span (via Span.Finish).
   718  func EnsureChildSpan(
   719  	ctx context.Context, tracer opentracing.Tracer, name string,
   720  ) (context.Context, opentracing.Span) {
   721  	if opentracing.SpanFromContext(ctx) == nil {
   722  		sp := tracer.(*Tracer).StartRootSpan(name, logtags.FromContext(ctx), NonRecordableSpan)
   723  		return opentracing.ContextWithSpan(ctx, sp), sp
   724  	}
   725  	return ChildSpan(ctx, name)
   726  }
   727  
   728  // StartSnowballTrace takes in a context and returns a derived one with a
   729  // "snowball span" in it. The caller takes ownership of this span from the
   730  // returned context and is in charge of Finish()ing it. The span has recording
   731  // enabled.
   732  //
   733  // TODO(andrei): remove this method once EXPLAIN(TRACE) is gone.
   734  func StartSnowballTrace(
   735  	ctx context.Context, tracer opentracing.Tracer, opName string,
   736  ) (context.Context, opentracing.Span) {
   737  	var span opentracing.Span
   738  	if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {
   739  		span = parentSpan.Tracer().StartSpan(
   740  			opName, opentracing.ChildOf(parentSpan.Context()), Recordable, LogTagsFromCtx(ctx),
   741  		)
   742  	} else {
   743  		span = tracer.StartSpan(opName, Recordable, LogTagsFromCtx(ctx))
   744  	}
   745  	StartRecording(span, SnowballRecording)
   746  	return opentracing.ContextWithSpan(ctx, span), span
   747  }
   748  
   749  // TestingCheckRecordedSpans checks whether a recording looks like an expected
   750  // one represented by a string with one line per expected span and one line per
   751  // expected event (i.e. log message).
   752  //
   753  // Use with something like:
   754  // 	 if err := TestingCheckRecordedSpans(tracing.GetRecording(span), `
   755  //     span root:
   756  //       event: a
   757  //       event: c
   758  //     span child:
   759  //       event: [ambient] b
   760  //   `); err != nil {
   761  //   	t.Fatal(err)
   762  //   }
   763  //
   764  // The event lines can (and generally should) omit the file:line part that they
   765  // might contain (depending on the level at which they were logged).
   766  //
   767  // Note: this test function is in this file because it needs to be used by
   768  // both tests in the tracing package and tests outside of it, and the function
   769  // itself depends on tracing.
   770  func TestingCheckRecordedSpans(recSpans []RecordedSpan, expected string) error {
   771  	expected = strings.TrimSpace(expected)
   772  	var rows []string
   773  	row := func(format string, args ...interface{}) {
   774  		rows = append(rows, fmt.Sprintf(format, args...))
   775  	}
   776  
   777  	for _, rs := range recSpans {
   778  		row("span %s:", rs.Operation)
   779  		if len(rs.Tags) > 0 {
   780  			var tags []string
   781  			for k, v := range rs.Tags {
   782  				tags = append(tags, fmt.Sprintf("%s=%v", k, v))
   783  			}
   784  			sort.Strings(tags)
   785  			row("  tags: %s", strings.Join(tags, " "))
   786  		}
   787  		for _, l := range rs.Logs {
   788  			msg := ""
   789  			for _, f := range l.Fields {
   790  				msg = msg + fmt.Sprintf("  %s: %v", f.Key, f.Value)
   791  			}
   792  			row("%s", msg)
   793  		}
   794  	}
   795  	var expRows []string
   796  	if expected != "" {
   797  		expRows = strings.Split(expected, "\n")
   798  	}
   799  	match := false
   800  	if len(expRows) == len(rows) {
   801  		match = true
   802  		for i := range expRows {
   803  			e := strings.Trim(expRows[i], " \t")
   804  			r := strings.Trim(rows[i], " \t")
   805  			if e != r && !matchesWithoutFileLine(r, e) {
   806  				match = false
   807  				break
   808  			}
   809  		}
   810  	}
   811  	if !match {
   812  		file, line, _ := caller.Lookup(1)
   813  		return errors.Errorf(
   814  			"%s:%d expected:\n%s\ngot:\n%s",
   815  			file, line, expected, strings.Join(rows, "\n"))
   816  	}
   817  	return nil
   818  }
   819  
   820  // matchesWithoutFileLine tries to match an event by stripping a file:line from
   821  // it. For example:
   822  // "event: util/log/trace_test.go:111 log" will match "event: log".
   823  //
   824  // Returns true if it matches.
   825  func matchesWithoutFileLine(msg string, expected string) bool {
   826  	groups := regexp.MustCompile(`^(event: ).*:[0-9]* (.*)$`).FindStringSubmatch(msg)
   827  	return len(groups) == 3 && fmt.Sprintf("event: %s", groups[2]) == expected
   828  }
   829  
   830  // ContextWithRecordingSpan returns a context with an embedded trace span which
   831  // returns its contents when getRecording is called and must be stopped by
   832  // calling the cancel method when done with the context (getRecording() needs to
   833  // be called before cancel()).
   834  //
   835  // Note that to convert the recorded spans into text, you can use
   836  // Recording.String(). Tests can also use FindMsgInRecording().
   837  func ContextWithRecordingSpan(
   838  	ctx context.Context, opName string,
   839  ) (retCtx context.Context, getRecording func() Recording, cancel func()) {
   840  	tr := NewTracer()
   841  	sp := tr.StartSpan(opName, Recordable, LogTagsFromCtx(ctx))
   842  	StartRecording(sp, SnowballRecording)
   843  	ctx, cancelCtx := context.WithCancel(ctx)
   844  	ctx = opentracing.ContextWithSpan(ctx, sp)
   845  
   846  	getRecording = func() Recording {
   847  		return GetRecording(sp)
   848  	}
   849  	cancel = func() {
   850  		cancelCtx()
   851  		StopRecording(sp)
   852  		sp.Finish()
   853  		tr.Close()
   854  	}
   855  	return ctx, getRecording, cancel
   856  }