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

     1  // Copyright 2017 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  	"bytes"
    15  	"encoding/json"
    16  	"fmt"
    17  	"regexp"
    18  	"sort"
    19  	"strconv"
    20  	"strings"
    21  	"sync/atomic"
    22  	"time"
    23  
    24  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    25  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    26  	"github.com/cockroachdb/errors"
    27  	"github.com/cockroachdb/logtags"
    28  	proto "github.com/gogo/protobuf/proto"
    29  	"github.com/gogo/protobuf/types"
    30  	jaegerjson "github.com/jaegertracing/jaeger/model/json"
    31  	opentracing "github.com/opentracing/opentracing-go"
    32  	otlog "github.com/opentracing/opentracing-go/log"
    33  	"golang.org/x/net/trace"
    34  )
    35  
    36  // spanMeta stores span information that is common to span and spanContext.
    37  type spanMeta struct {
    38  	// A probabilistically unique identifier for a [multi-span] trace.
    39  	TraceID uint64
    40  
    41  	// A probabilistically unique identifier for a span.
    42  	SpanID uint64
    43  }
    44  
    45  type spanContext struct {
    46  	spanMeta
    47  
    48  	// Underlying shadow tracer info and context (optional).
    49  	shadowTr  *shadowTracer
    50  	shadowCtx opentracing.SpanContext
    51  
    52  	// If set, all spans derived from this context are being recorded as a group.
    53  	recordingGroup *spanGroup
    54  	recordingType  RecordingType
    55  
    56  	// The span's associated baggage.
    57  	Baggage map[string]string
    58  }
    59  
    60  const (
    61  	// TagPrefix is prefixed to all tags that should be output in SHOW TRACE.
    62  	TagPrefix = "cockroach."
    63  	// StatTagPrefix is prefixed to all stats output in span tags.
    64  	StatTagPrefix = TagPrefix + "stat."
    65  )
    66  
    67  // SpanStats are stats that can be added to a span.
    68  type SpanStats interface {
    69  	proto.Message
    70  	// Stats returns the stats that the object represents as a map from stat name
    71  	// to value to be added to span tags. The keys will be prefixed with
    72  	// StatTagPrefix.
    73  	Stats() map[string]string
    74  }
    75  
    76  var _ opentracing.SpanContext = &spanContext{}
    77  
    78  // ForeachBaggageItem is part of the opentracing.SpanContext interface.
    79  func (sc *spanContext) ForeachBaggageItem(handler func(k, v string) bool) {
    80  	for k, v := range sc.Baggage {
    81  		if !handler(k, v) {
    82  			break
    83  		}
    84  	}
    85  }
    86  
    87  // RecordingType is the type of recording that a span might be performing.
    88  type RecordingType int
    89  
    90  const (
    91  	// NoRecording means that the span isn't recording.
    92  	NoRecording RecordingType = iota
    93  	// SnowballRecording means that remote child spans (generally opened through
    94  	// RPCs) are also recorded.
    95  	SnowballRecording
    96  	// SingleNodeRecording means that only spans on the current node are recorded.
    97  	SingleNodeRecording
    98  )
    99  
   100  type span struct {
   101  	spanMeta
   102  
   103  	parentSpanID uint64
   104  
   105  	tracer *Tracer
   106  
   107  	// x/net/trace.Trace instance; nil if not tracing to x/net/trace.
   108  	netTr trace.Trace
   109  	// Shadow tracer and span; nil if not using a shadow tracer.
   110  	shadowTr   *shadowTracer
   111  	shadowSpan opentracing.Span
   112  
   113  	operation string
   114  	startTime time.Time
   115  
   116  	// logTags are set to the log tags that were available when this span was
   117  	// created, so that there's no need to eagerly copy all of those log tags into
   118  	// this span's tags. If the span's tags are actually requested, these logTags
   119  	// will be copied out at that point.
   120  	// Note that these tags have not gone through the log tag -> span tag
   121  	// remapping procedure; tagName() needs to be called before exposing each
   122  	// tag's key to a user.
   123  	logTags *logtags.Buffer
   124  
   125  	// Atomic flag used to avoid taking the mutex in the hot path.
   126  	recording int32
   127  
   128  	mu struct {
   129  		syncutil.Mutex
   130  		// duration is initialized to -1 and set on Finish().
   131  		duration time.Duration
   132  
   133  		recordingGroup *spanGroup
   134  		recordingType  RecordingType
   135  		recordedLogs   []opentracing.LogRecord
   136  		// tags are only set when recording. These are tags that have been added to
   137  		// this span, and will be appended to the tags in logTags when someone
   138  		// needs to actually observe the total set of tags that is a part of this
   139  		// span.
   140  		// TODO(radu): perhaps we want a recording to capture all the tags (even
   141  		// those that were set before recording started)?
   142  		tags opentracing.Tags
   143  
   144  		stats SpanStats
   145  
   146  		// The span's associated baggage.
   147  		Baggage map[string]string
   148  	}
   149  }
   150  
   151  var _ opentracing.Span = &span{}
   152  
   153  func (s *span) isRecording() bool {
   154  	return atomic.LoadInt32(&s.recording) != 0
   155  }
   156  
   157  // IsRecording returns true if the span is recording its events.
   158  func IsRecording(s opentracing.Span) bool {
   159  	if _, noop := s.(*noopSpan); noop {
   160  		return false
   161  	}
   162  	return s.(*span).isRecording()
   163  }
   164  
   165  func (s *span) enableRecording(group *spanGroup, recType RecordingType) {
   166  	if group == nil {
   167  		panic("no spanGroup")
   168  	}
   169  	s.mu.Lock()
   170  	atomic.StoreInt32(&s.recording, 1)
   171  	s.mu.recordingGroup = group
   172  	s.mu.recordingType = recType
   173  	if recType == SnowballRecording {
   174  		s.setBaggageItemLocked(Snowball, "1")
   175  	}
   176  	// Clear any previously recorded logs.
   177  	s.mu.recordedLogs = nil
   178  	s.mu.Unlock()
   179  
   180  	group.addSpan(s)
   181  }
   182  
   183  // StartRecording enables recording on the span. Events from this point forward
   184  // are recorded; also, all direct and indirect child spans started from now on
   185  // will be part of the same recording.
   186  //
   187  // Recording is not supported by noop spans; to ensure a real span is always
   188  // created, use the Recordable option to StartSpan.
   189  //
   190  // If recording was already started on this span (either directly or because a
   191  // parent span is recording), the old recording is lost.
   192  func StartRecording(os opentracing.Span, recType RecordingType) {
   193  	if recType == NoRecording {
   194  		panic("StartRecording called with NoRecording")
   195  	}
   196  	if _, noop := os.(*noopSpan); noop {
   197  		panic("StartRecording called on NoopSpan; use the Recordable option for StartSpan")
   198  	}
   199  	os.(*span).enableRecording(new(spanGroup), recType)
   200  }
   201  
   202  // StopRecording disables recording on this span. Child spans that were created
   203  // since recording was started will continue to record until they finish.
   204  //
   205  // Calling this after StartRecording is not required; the recording will go away
   206  // when all the spans finish.
   207  //
   208  // StopRecording() can be called on a Finish()ed span.
   209  func StopRecording(os opentracing.Span) {
   210  	os.(*span).disableRecording()
   211  }
   212  
   213  func (s *span) disableRecording() {
   214  	s.mu.Lock()
   215  	atomic.StoreInt32(&s.recording, 0)
   216  	s.mu.recordingGroup = nil
   217  	// We test the duration as a way to check if the span has been finished. If it
   218  	// has, we don't want to do the call below as it might crash (at least if
   219  	// there's a netTr).
   220  	if (s.mu.duration == -1) && (s.mu.recordingType == SnowballRecording) {
   221  		// Clear the Snowball baggage item, assuming that it was set by
   222  		// enableRecording().
   223  		s.setBaggageItemLocked(Snowball, "")
   224  	}
   225  	s.mu.Unlock()
   226  }
   227  
   228  // IsRecordable returns true if {Start,Stop}Recording() can be called on this
   229  // span.
   230  //
   231  // In other words, this tests if the span is our custom type, and not a noopSpan
   232  // or anything else.
   233  func IsRecordable(os opentracing.Span) bool {
   234  	_, isCockroachSpan := os.(*span)
   235  	return isCockroachSpan
   236  }
   237  
   238  // Recording represents a group of RecordedSpans, as returned by GetRecording.
   239  type Recording []RecordedSpan
   240  
   241  // GetRecording retrieves the current recording, if the span has recording
   242  // enabled. This can be called while spans that are part of the record are
   243  // still open; it can run concurrently with operations on those spans.
   244  func GetRecording(os opentracing.Span) Recording {
   245  	if _, noop := os.(*noopSpan); noop {
   246  		return nil
   247  	}
   248  	s := os.(*span)
   249  	if !s.isRecording() {
   250  		return nil
   251  	}
   252  	s.mu.Lock()
   253  	group := s.mu.recordingGroup
   254  	s.mu.Unlock()
   255  	if group == nil {
   256  		return nil
   257  	}
   258  	return group.getSpans()
   259  }
   260  
   261  type traceLogData struct {
   262  	opentracing.LogRecord
   263  	depth int
   264  	// timeSincePrev represents the duration since the previous log line (previous in the
   265  	// set of log lines that this is part of). This is always computed relative to a log line
   266  	// from the same span, except for start of span in which case the duration is computed relative
   267  	// to the last log in the parent occurring before this start. For example:
   268  	// start span A
   269  	// log 1           // duration relative to "start span A"
   270  	//   start span B  // duration relative to "log 1"
   271  	//   log 2  			 // duration relative to "start span B"
   272  	// log 3  				 // duration relative to "log 1"
   273  	timeSincePrev time.Duration
   274  }
   275  
   276  // String formats the given spans for human consumption, showing the
   277  // relationship using nesting and times as both relative to the previous event
   278  // and cumulative. Logs from the same span are kept together, before logs from
   279  // children spans. Each log line show the time since the beginning of the trace
   280  // and since the previous log line. Span starts are shown with special "===
   281  // <operation>" lines. For a span start, the time since the relative log line
   282  // can be negative when the span start follows a message from the parent that
   283  // was generated after the child span started (or even after the child
   284  // finished).
   285  //
   286  // TODO(andrei): this should be unified with
   287  // SessionTracing.GenerateSessionTraceVTable.
   288  func (r Recording) String() string {
   289  	var logs []traceLogData
   290  	var start time.Time
   291  	for _, sp := range r {
   292  		if sp.ParentSpanID == 0 {
   293  			if start == (time.Time{}) {
   294  				start = sp.StartTime
   295  			}
   296  			logs = append(logs, r.visitSpan(sp, 0 /* depth */)...)
   297  		}
   298  	}
   299  
   300  	var buf strings.Builder
   301  	for _, entry := range logs {
   302  		fmt.Fprintf(&buf, "% 10.3fms % 10.3fms%s",
   303  			1000*entry.Timestamp.Sub(start).Seconds(),
   304  			1000*entry.timeSincePrev.Seconds(),
   305  			strings.Repeat("    ", entry.depth+1))
   306  		for i, f := range entry.Fields {
   307  			if i != 0 {
   308  				buf.WriteByte(' ')
   309  			}
   310  			fmt.Fprintf(&buf, "%s:%v", f.Key(), f.Value())
   311  		}
   312  		buf.WriteByte('\n')
   313  	}
   314  	return buf.String()
   315  }
   316  
   317  // FindLogMessage returns the first log message in the recording that matches
   318  // the given regexp. The bool return value is true if such a message is found.
   319  func (r Recording) FindLogMessage(pattern string) (string, bool) {
   320  	re := regexp.MustCompile(pattern)
   321  	for _, sp := range r {
   322  		for _, l := range sp.Logs {
   323  			msg := l.Msg()
   324  			if re.MatchString(msg) {
   325  				return msg, true
   326  			}
   327  		}
   328  	}
   329  	return "", false
   330  }
   331  
   332  // visitSpan returns the log messages for sp, and all of sp's children.
   333  //
   334  // All messages from a span are kept together. Sibling spans are ordered within
   335  // the parent in their start order.
   336  func (r Recording) visitSpan(sp RecordedSpan, depth int) []traceLogData {
   337  	ownLogs := make([]traceLogData, 0, len(sp.Logs)+1)
   338  
   339  	conv := func(l opentracing.LogRecord, ref time.Time) traceLogData {
   340  		var timeSincePrev time.Duration
   341  		if ref != (time.Time{}) {
   342  			timeSincePrev = l.Timestamp.Sub(ref)
   343  		}
   344  		return traceLogData{
   345  			LogRecord:     l,
   346  			depth:         depth,
   347  			timeSincePrev: timeSincePrev,
   348  		}
   349  	}
   350  
   351  	// Add a log line representing the start of the span.
   352  	lr := opentracing.LogRecord{
   353  		Timestamp: sp.StartTime,
   354  		Fields:    []otlog.Field{otlog.String("=== operation", sp.Operation)},
   355  	}
   356  	if len(sp.Tags) > 0 {
   357  		tags := make([]string, 0, len(sp.Tags))
   358  		for k := range sp.Tags {
   359  			tags = append(tags, k)
   360  		}
   361  		sort.Strings(tags)
   362  		for _, k := range tags {
   363  			lr.Fields = append(lr.Fields, otlog.String(k, sp.Tags[k]))
   364  		}
   365  	}
   366  	ownLogs = append(ownLogs, conv(
   367  		lr,
   368  		// ref - this entries timeSincePrev will be computed when we merge it into the parent
   369  		time.Time{}))
   370  
   371  	for _, l := range sp.Logs {
   372  		lr := opentracing.LogRecord{
   373  			Timestamp: l.Time,
   374  			Fields:    make([]otlog.Field, len(l.Fields)),
   375  		}
   376  		for i, f := range l.Fields {
   377  			lr.Fields[i] = otlog.String(f.Key, f.Value)
   378  		}
   379  		lastLog := ownLogs[len(ownLogs)-1]
   380  		ownLogs = append(ownLogs, conv(lr, lastLog.Timestamp))
   381  	}
   382  
   383  	childSpans := make([][]traceLogData, 0)
   384  	for _, osp := range r {
   385  		if osp.ParentSpanID != sp.SpanID {
   386  			continue
   387  		}
   388  		childSpans = append(childSpans, r.visitSpan(osp, depth+1))
   389  	}
   390  
   391  	// Merge ownLogs with childSpans.
   392  	mergedLogs := make([]traceLogData, 0, len(ownLogs))
   393  	timeMax := time.Date(2200, 0, 0, 0, 0, 0, 0, time.UTC)
   394  	i, j := 0, 0
   395  	var lastTimestamp time.Time
   396  	for i < len(ownLogs) || j < len(childSpans) {
   397  		if len(mergedLogs) > 0 {
   398  			lastTimestamp = mergedLogs[len(mergedLogs)-1].Timestamp
   399  		}
   400  		nextLog, nextChild := timeMax, timeMax
   401  		if i < len(ownLogs) {
   402  			nextLog = ownLogs[i].Timestamp
   403  		}
   404  		if j < len(childSpans) {
   405  			nextChild = childSpans[j][0].Timestamp
   406  		}
   407  		if nextLog.After(nextChild) {
   408  			// Fill in timeSincePrev for the first one of the child's entries.
   409  			if lastTimestamp != (time.Time{}) {
   410  				childSpans[j][0].timeSincePrev = childSpans[j][0].Timestamp.Sub(lastTimestamp)
   411  			}
   412  			mergedLogs = append(mergedLogs, childSpans[j]...)
   413  			lastTimestamp = childSpans[j][0].Timestamp
   414  			j++
   415  		} else {
   416  			mergedLogs = append(mergedLogs, ownLogs[i])
   417  			lastTimestamp = ownLogs[i].Timestamp
   418  			i++
   419  		}
   420  	}
   421  
   422  	return mergedLogs
   423  }
   424  
   425  // ToJaegerJSON returns the trace as a JSON that can be imported into Jaeger for
   426  // visualization.
   427  //
   428  // The format is described here: https://github.com/jaegertracing/jaeger-ui/issues/381#issuecomment-494150826
   429  //
   430  // The statement is passed in so it can be included in the trace.
   431  func (r Recording) ToJaegerJSON(stmt string) (string, error) {
   432  	if len(r) == 0 {
   433  		return "", nil
   434  	}
   435  
   436  	cpy := make(Recording, len(r))
   437  	copy(cpy, r)
   438  	r = cpy
   439  	tagsCopy := make(map[string]string)
   440  	for k, v := range r[0].Tags {
   441  		tagsCopy[k] = v
   442  	}
   443  	tagsCopy["statement"] = stmt
   444  	r[0].Tags = tagsCopy
   445  
   446  	toJaegerSpanID := func(spanID uint64) jaegerjson.SpanID {
   447  		return jaegerjson.SpanID(strconv.FormatUint(spanID, 10))
   448  	}
   449  
   450  	// Each span in Jaeger belongs to a "process" that generated it. Spans
   451  	// belonging to different colors are colored differently in Jaeger. We're
   452  	// going to map our different nodes to different processes.
   453  	processes := make(map[jaegerjson.ProcessID]jaegerjson.Process)
   454  	// getProcessID figures out what "process" a span belongs to. It looks for an
   455  	// "node: <node id>" tag. The processes map is populated with an entry for every
   456  	// node present in the trace.
   457  	getProcessID := func(sp RecordedSpan) jaegerjson.ProcessID {
   458  		node := "unknown node"
   459  		for k, v := range sp.Tags {
   460  			if k == "node" {
   461  				node = fmt.Sprintf("node %s", v)
   462  				break
   463  			}
   464  		}
   465  		pid := jaegerjson.ProcessID(node)
   466  		if _, ok := processes[pid]; !ok {
   467  			processes[pid] = jaegerjson.Process{
   468  				ServiceName: node,
   469  				Tags:        nil,
   470  			}
   471  		}
   472  		return pid
   473  	}
   474  
   475  	var t jaegerjson.Trace
   476  	t.TraceID = jaegerjson.TraceID(strconv.FormatUint(r[0].TraceID, 10))
   477  	t.Processes = processes
   478  
   479  	for _, sp := range r {
   480  		var s jaegerjson.Span
   481  
   482  		s.TraceID = t.TraceID
   483  		s.Duration = uint64(sp.Duration.Microseconds())
   484  		s.StartTime = uint64(sp.StartTime.UnixNano() / 1000)
   485  		s.SpanID = toJaegerSpanID(sp.SpanID)
   486  		s.OperationName = sp.Operation
   487  		s.ProcessID = getProcessID(sp)
   488  
   489  		if sp.ParentSpanID != 0 {
   490  			s.References = []jaegerjson.Reference{{
   491  				RefType: jaegerjson.ChildOf,
   492  				TraceID: s.TraceID,
   493  				SpanID:  toJaegerSpanID(sp.ParentSpanID),
   494  			}}
   495  		}
   496  
   497  		for k, v := range sp.Tags {
   498  			s.Tags = append(s.Tags, jaegerjson.KeyValue{
   499  				Key:   k,
   500  				Value: v,
   501  				Type:  "STRING",
   502  			})
   503  		}
   504  		for _, l := range sp.Logs {
   505  			jl := jaegerjson.Log{Timestamp: uint64(l.Time.UnixNano() / 1000)}
   506  			for _, field := range l.Fields {
   507  				jl.Fields = append(jl.Fields, jaegerjson.KeyValue{
   508  					Key:   field.Key,
   509  					Value: field.Value,
   510  					Type:  "STRING",
   511  				})
   512  			}
   513  			s.Logs = append(s.Logs, jl)
   514  		}
   515  		t.Spans = append(t.Spans, s)
   516  	}
   517  
   518  	data := TraceCollection{
   519  		Data: []jaegerjson.Trace{t},
   520  		// Add a comment that will show-up at the top of the JSON file, is someone opens the file.
   521  		// NOTE: This comment is scarce on newlines because they appear as \n in the
   522  		// generated file doing more harm than good.
   523  		Comment: fmt.Sprintf(`This is a trace for SQL statement: %s
   524  This trace can be imported into Jaeger for visualization. From the Jaeger Search screen, select JSON File.
   525  Jaeger can be started using docker with: docker run -d --name jaeger -p 16686:16686 jaegertracing/all-in-one:1.17
   526  The UI can then be accessed at http://localhost:16686/search`,
   527  			stmt),
   528  	}
   529  	json, err := json.MarshalIndent(data, "" /* prefix */, "\t" /* indent */)
   530  	if err != nil {
   531  		return "", err
   532  	}
   533  	return string(json), nil
   534  }
   535  
   536  // TraceCollection is the format accepted by the Jaegar upload feature, as per
   537  // https://github.com/jaegertracing/jaeger-ui/issues/381#issuecomment-494150826
   538  type TraceCollection struct {
   539  	// Comment is a dummy field we use to put instructions on how to load the trace.
   540  	Comment string             `json:"_comment"`
   541  	Data    []jaegerjson.Trace `json:"data"`
   542  }
   543  
   544  // ImportRemoteSpans adds RecordedSpan data to the recording of the given span;
   545  // these spans will be part of the result of GetRecording. Used to import
   546  // recorded traces from other nodes.
   547  func ImportRemoteSpans(os opentracing.Span, remoteSpans []RecordedSpan) error {
   548  	s := os.(*span)
   549  	s.mu.Lock()
   550  	group := s.mu.recordingGroup
   551  	s.mu.Unlock()
   552  	if group == nil {
   553  		return errors.New("adding Raw Spans to a span that isn't recording")
   554  	}
   555  	group.Lock()
   556  	group.remoteSpans = append(group.remoteSpans, remoteSpans...)
   557  	group.Unlock()
   558  	return nil
   559  }
   560  
   561  // IsBlackHoleSpan returns true if events for this span are just dropped. This
   562  // is the case when tracing is disabled and we're not recording. Tracing clients
   563  // can use this method to figure out if they can short-circuit some
   564  // tracing-related work that would be discarded anyway.
   565  func IsBlackHoleSpan(s opentracing.Span) bool {
   566  	// There are two types of black holes: instances of noopSpan and, when tracing
   567  	// is disabled, real spans that are not recording.
   568  	if _, noop := s.(*noopSpan); noop {
   569  		return true
   570  	}
   571  	sp := s.(*span)
   572  	return !sp.isRecording() && sp.netTr == nil && sp.shadowTr == nil
   573  }
   574  
   575  // IsNoopContext returns true if the span context is from a "no-op" span. If
   576  // this is true, any span derived from this context will be a "black hole span".
   577  func IsNoopContext(spanCtx opentracing.SpanContext) bool {
   578  	_, noop := spanCtx.(noopSpanContext)
   579  	return noop
   580  }
   581  
   582  // SetSpanStats sets the stats on a span. stats.Stats() will also be added to
   583  // the span tags.
   584  func SetSpanStats(os opentracing.Span, stats SpanStats) {
   585  	s := os.(*span)
   586  	s.mu.Lock()
   587  	s.mu.stats = stats
   588  	for name, value := range stats.Stats() {
   589  		s.setTagInner(StatTagPrefix+name, value, true /* locked */)
   590  	}
   591  	s.mu.Unlock()
   592  }
   593  
   594  // Finish is part of the opentracing.Span interface.
   595  func (s *span) Finish() {
   596  	s.FinishWithOptions(opentracing.FinishOptions{})
   597  }
   598  
   599  // FinishWithOptions is part of the opentracing.Span interface.
   600  func (s *span) FinishWithOptions(opts opentracing.FinishOptions) {
   601  	finishTime := opts.FinishTime
   602  	if finishTime.IsZero() {
   603  		finishTime = time.Now()
   604  	}
   605  	s.mu.Lock()
   606  	s.mu.duration = finishTime.Sub(s.startTime)
   607  	s.mu.Unlock()
   608  	if s.shadowTr != nil {
   609  		s.shadowSpan.Finish()
   610  	}
   611  	if s.netTr != nil {
   612  		s.netTr.Finish()
   613  	}
   614  }
   615  
   616  // Context is part of the opentracing.Span interface.
   617  //
   618  // TODO(andrei, radu): Should this return noopSpanContext for a Recordable span
   619  // that's not currently recording? That might save work and allocations when
   620  // creating child spans.
   621  func (s *span) Context() opentracing.SpanContext {
   622  	s.mu.Lock()
   623  	defer s.mu.Unlock()
   624  	baggageCopy := make(map[string]string, len(s.mu.Baggage))
   625  	for k, v := range s.mu.Baggage {
   626  		baggageCopy[k] = v
   627  	}
   628  	sc := &spanContext{
   629  		spanMeta: s.spanMeta,
   630  		Baggage:  baggageCopy,
   631  	}
   632  	if s.shadowTr != nil {
   633  		sc.shadowTr = s.shadowTr
   634  		sc.shadowCtx = s.shadowSpan.Context()
   635  	}
   636  
   637  	if s.isRecording() {
   638  		sc.recordingGroup = s.mu.recordingGroup
   639  		sc.recordingType = s.mu.recordingType
   640  	}
   641  	return sc
   642  }
   643  
   644  // SetOperationName is part of the opentracing.Span interface.
   645  func (s *span) SetOperationName(operationName string) opentracing.Span {
   646  	if s.shadowTr != nil {
   647  		s.shadowSpan.SetOperationName(operationName)
   648  	}
   649  	s.operation = operationName
   650  	return s
   651  }
   652  
   653  // SetTag is part of the opentracing.Span interface.
   654  func (s *span) SetTag(key string, value interface{}) opentracing.Span {
   655  	return s.setTagInner(key, value, false /* locked */)
   656  }
   657  
   658  func (s *span) setTagInner(key string, value interface{}, locked bool) opentracing.Span {
   659  	if s.shadowTr != nil {
   660  		s.shadowSpan.SetTag(key, value)
   661  	}
   662  	if s.netTr != nil {
   663  		s.netTr.LazyPrintf("%s:%v", key, value)
   664  	}
   665  	// The internal tags will be used if we start a recording on this span.
   666  	if !locked {
   667  		s.mu.Lock()
   668  	}
   669  	if s.mu.tags == nil {
   670  		s.mu.tags = make(opentracing.Tags)
   671  	}
   672  	s.mu.tags[key] = value
   673  	if !locked {
   674  		s.mu.Unlock()
   675  	}
   676  	return s
   677  }
   678  
   679  // LogFields is part of the opentracing.Span interface.
   680  func (s *span) LogFields(fields ...otlog.Field) {
   681  	if s.shadowTr != nil {
   682  		s.shadowSpan.LogFields(fields...)
   683  	}
   684  	if s.netTr != nil {
   685  		// TODO(radu): when LightStep supports arbitrary fields, we should make
   686  		// the formatting of the message consistent with that. Until then we treat
   687  		// legacy events that just have an "event" key specially.
   688  		if len(fields) == 1 && fields[0].Key() == LogMessageField {
   689  			s.netTr.LazyPrintf("%s", fields[0].Value())
   690  		} else {
   691  			var buf bytes.Buffer
   692  			for i, f := range fields {
   693  				if i > 0 {
   694  					buf.WriteByte(' ')
   695  				}
   696  				fmt.Fprintf(&buf, "%s:%v", f.Key(), f.Value())
   697  			}
   698  
   699  			s.netTr.LazyPrintf("%s", buf.String())
   700  		}
   701  	}
   702  	if s.isRecording() {
   703  		s.mu.Lock()
   704  		if len(s.mu.recordedLogs) < maxLogsPerSpan {
   705  			s.mu.recordedLogs = append(s.mu.recordedLogs, opentracing.LogRecord{
   706  				Timestamp: time.Now(),
   707  				Fields:    fields,
   708  			})
   709  		}
   710  		s.mu.Unlock()
   711  	}
   712  }
   713  
   714  // LogKV is part of the opentracing.Span interface.
   715  func (s *span) LogKV(alternatingKeyValues ...interface{}) {
   716  	fields, err := otlog.InterleavedKVToFields(alternatingKeyValues...)
   717  	if err != nil {
   718  		s.LogFields(otlog.Error(err), otlog.String("function", "LogKV"))
   719  		return
   720  	}
   721  	s.LogFields(fields...)
   722  }
   723  
   724  // SetBaggageItem is part of the opentracing.Span interface.
   725  func (s *span) SetBaggageItem(restrictedKey, value string) opentracing.Span {
   726  	s.mu.Lock()
   727  	defer s.mu.Unlock()
   728  	return s.setBaggageItemLocked(restrictedKey, value)
   729  }
   730  
   731  func (s *span) setBaggageItemLocked(restrictedKey, value string) opentracing.Span {
   732  	if oldVal, ok := s.mu.Baggage[restrictedKey]; ok && oldVal == value {
   733  		// No-op.
   734  		return s
   735  	}
   736  	if s.mu.Baggage == nil {
   737  		s.mu.Baggage = make(map[string]string)
   738  	}
   739  	s.mu.Baggage[restrictedKey] = value
   740  
   741  	if s.shadowTr != nil {
   742  		s.shadowSpan.SetBaggageItem(restrictedKey, value)
   743  	}
   744  	// Also set a tag so it shows up in the Lightstep UI or x/net/trace.
   745  	s.setTagInner(restrictedKey, value, true /* locked */)
   746  	return s
   747  }
   748  
   749  // BaggageItem is part of the opentracing.Span interface.
   750  func (s *span) BaggageItem(restrictedKey string) string {
   751  	s.mu.Lock()
   752  	defer s.mu.Unlock()
   753  	return s.mu.Baggage[restrictedKey]
   754  }
   755  
   756  // Tracer is part of the opentracing.Span interface.
   757  func (s *span) Tracer() opentracing.Tracer {
   758  	return s.tracer
   759  }
   760  
   761  // LogEvent is part of the opentracing.Span interface. Deprecated.
   762  func (s *span) LogEvent(event string) {
   763  	s.LogFields(otlog.String(LogMessageField, event))
   764  }
   765  
   766  // LogEventWithPayload is part of the opentracing.Span interface. Deprecated.
   767  func (s *span) LogEventWithPayload(event string, payload interface{}) {
   768  	s.LogFields(otlog.String(LogMessageField, event), otlog.Object("payload", payload))
   769  }
   770  
   771  // Log is part of the opentracing.Span interface. Deprecated.
   772  func (s *span) Log(data opentracing.LogData) {
   773  	panic("unimplemented")
   774  }
   775  
   776  // getRecording returns the span's recording.
   777  func (s *span) getRecording() RecordedSpan {
   778  	s.mu.Lock()
   779  	defer s.mu.Unlock()
   780  
   781  	rs := RecordedSpan{
   782  		TraceID:      s.TraceID,
   783  		SpanID:       s.SpanID,
   784  		ParentSpanID: s.parentSpanID,
   785  		Operation:    s.operation,
   786  		StartTime:    s.startTime,
   787  		Duration:     s.mu.duration,
   788  	}
   789  
   790  	addTag := func(k, v string) {
   791  		if rs.Tags == nil {
   792  			rs.Tags = make(map[string]string)
   793  		}
   794  		rs.Tags[k] = v
   795  	}
   796  
   797  	switch rs.Duration {
   798  	case -1:
   799  		// -1 indicates an unfinished span. For a recording it's better to put some
   800  		// duration in it, otherwise tools get confused. For example, we export
   801  		// recordings to Jaeger, and spans with a zero duration don't look nice.
   802  		rs.Duration = timeutil.Now().Sub(rs.StartTime)
   803  		addTag("unfinished", "")
   804  	}
   805  
   806  	if s.mu.stats != nil {
   807  		stats, err := types.MarshalAny(s.mu.stats)
   808  		if err != nil {
   809  			panic(err)
   810  		}
   811  		rs.Stats = stats
   812  	}
   813  
   814  	if len(s.mu.Baggage) > 0 {
   815  		rs.Baggage = make(map[string]string)
   816  		for k, v := range s.mu.Baggage {
   817  			rs.Baggage[k] = v
   818  		}
   819  	}
   820  	if s.logTags != nil {
   821  		tags := s.logTags.Get()
   822  		for i := range tags {
   823  			tag := &tags[i]
   824  			addTag(tagName(tag.Key()), tag.ValueStr())
   825  		}
   826  	}
   827  	if len(s.mu.tags) > 0 {
   828  		for k, v := range s.mu.tags {
   829  			// We encode the tag values as strings.
   830  			addTag(k, fmt.Sprint(v))
   831  		}
   832  	}
   833  	rs.Logs = make([]LogRecord, len(s.mu.recordedLogs))
   834  	for i, r := range s.mu.recordedLogs {
   835  		rs.Logs[i].Time = r.Timestamp
   836  		rs.Logs[i].Fields = make([]LogRecord_Field, len(r.Fields))
   837  		for j, f := range r.Fields {
   838  			rs.Logs[i].Fields[j] = LogRecord_Field{
   839  				Key:   f.Key(),
   840  				Value: fmt.Sprint(f.Value()),
   841  			}
   842  		}
   843  	}
   844  
   845  	return rs
   846  }
   847  
   848  // spanGroup keeps track of all the spans that are being recorded as a group (i.e.
   849  // the span for which recording was enabled and all direct or indirect child
   850  // spans since then).
   851  type spanGroup struct {
   852  	syncutil.Mutex
   853  	// spans keeps track of all the local spans. A span is inserted in this slice
   854  	// as soon as it is opened; the first element is the span passed to
   855  	// StartRecording().
   856  	spans []*span
   857  	// remoteSpans stores spans obtained from another host that we want to associate
   858  	// with the record for this group.
   859  	remoteSpans Recording
   860  }
   861  
   862  func (ss *spanGroup) addSpan(s *span) {
   863  	ss.Lock()
   864  	ss.spans = append(ss.spans, s)
   865  	ss.Unlock()
   866  }
   867  
   868  // getSpans returns all the local and remote spans accumulated in this group.
   869  // The first result is the first local span - i.e. the span originally passed to
   870  // StartRecording().
   871  func (ss *spanGroup) getSpans() Recording {
   872  	ss.Lock()
   873  	spans := ss.spans
   874  	remoteSpans := ss.remoteSpans
   875  	ss.Unlock()
   876  
   877  	result := make(Recording, 0, len(spans)+len(remoteSpans))
   878  	for _, s := range spans {
   879  		rs := s.getRecording()
   880  		result = append(result, rs)
   881  	}
   882  	return append(result, remoteSpans...)
   883  }
   884  
   885  type noopSpanContext struct{}
   886  
   887  var _ opentracing.SpanContext = noopSpanContext{}
   888  
   889  func (n noopSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
   890  
   891  type noopSpan struct {
   892  	tracer *Tracer
   893  }
   894  
   895  var _ opentracing.Span = &noopSpan{}
   896  
   897  func (n *noopSpan) Context() opentracing.SpanContext                       { return noopSpanContext{} }
   898  func (n *noopSpan) BaggageItem(key string) string                          { return "" }
   899  func (n *noopSpan) SetTag(key string, value interface{}) opentracing.Span  { return n }
   900  func (n *noopSpan) Finish()                                                {}
   901  func (n *noopSpan) FinishWithOptions(opts opentracing.FinishOptions)       {}
   902  func (n *noopSpan) SetOperationName(operationName string) opentracing.Span { return n }
   903  func (n *noopSpan) Tracer() opentracing.Tracer                             { return n.tracer }
   904  func (n *noopSpan) LogFields(fields ...otlog.Field)                        {}
   905  func (n *noopSpan) LogKV(keyVals ...interface{})                           {}
   906  func (n *noopSpan) LogEvent(event string)                                  {}
   907  func (n *noopSpan) LogEventWithPayload(event string, payload interface{})  {}
   908  func (n *noopSpan) Log(data opentracing.LogData)                           {}
   909  
   910  func (n *noopSpan) SetBaggageItem(key, val string) opentracing.Span {
   911  	if key == Snowball {
   912  		panic("attempting to set Snowball on a noop span; use the Recordable option to StartSpan")
   913  	}
   914  	return n
   915  }