|  | // Copyright 2019 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package export | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "fmt" | 
|  | "sync" | 
|  |  | 
|  | "golang.org/x/tools/internal/event" | 
|  | "golang.org/x/tools/internal/event/core" | 
|  | "golang.org/x/tools/internal/event/keys" | 
|  | "golang.org/x/tools/internal/event/label" | 
|  | ) | 
|  |  | 
|  | type SpanContext struct { | 
|  | TraceID TraceID | 
|  | SpanID  SpanID | 
|  | } | 
|  |  | 
|  | type Span struct { | 
|  | Name     string | 
|  | ID       SpanContext | 
|  | ParentID SpanID | 
|  | mu       sync.Mutex | 
|  | start    core.Event | 
|  | finish   core.Event | 
|  | events   []core.Event | 
|  | } | 
|  |  | 
|  | type contextKeyType int | 
|  |  | 
|  | const ( | 
|  | spanContextKey = contextKeyType(iota) | 
|  | labelContextKey | 
|  | ) | 
|  |  | 
|  | func GetSpan(ctx context.Context) *Span { | 
|  | v := ctx.Value(spanContextKey) | 
|  | if v == nil { | 
|  | return nil | 
|  | } | 
|  | return v.(*Span) | 
|  | } | 
|  |  | 
|  | // Spans creates an exporter that maintains hierarchical span structure in the | 
|  | // context. | 
|  | // It creates new spans on start events, adds events to the current span on | 
|  | // log or label, and closes the span on end events. | 
|  | // The span structure can then be used by other exporters. | 
|  | func Spans(output event.Exporter) event.Exporter { | 
|  | return func(ctx context.Context, ev core.Event, lm label.Map) context.Context { | 
|  | switch { | 
|  | case event.IsLog(ev), event.IsLabel(ev): | 
|  | if span := GetSpan(ctx); span != nil { | 
|  | span.mu.Lock() | 
|  | span.events = append(span.events, ev) | 
|  | span.mu.Unlock() | 
|  | } | 
|  | case event.IsStart(ev): | 
|  | span := &Span{ | 
|  | Name:  keys.Start.Get(lm), | 
|  | start: ev, | 
|  | } | 
|  | if parent := GetSpan(ctx); parent != nil { | 
|  | span.ID.TraceID = parent.ID.TraceID | 
|  | span.ParentID = parent.ID.SpanID | 
|  | } else { | 
|  | span.ID.TraceID = newTraceID() | 
|  | } | 
|  | span.ID.SpanID = newSpanID() | 
|  | ctx = context.WithValue(ctx, spanContextKey, span) | 
|  | case event.IsEnd(ev): | 
|  | if span := GetSpan(ctx); span != nil { | 
|  | span.mu.Lock() | 
|  | span.finish = ev | 
|  | span.mu.Unlock() | 
|  | } | 
|  | case event.IsDetach(ev): | 
|  | ctx = context.WithValue(ctx, spanContextKey, nil) | 
|  | } | 
|  | return output(ctx, ev, lm) | 
|  | } | 
|  | } | 
|  |  | 
|  | func (s *SpanContext) Format(f fmt.State, r rune) { | 
|  | fmt.Fprintf(f, "%v:%v", s.TraceID, s.SpanID) | 
|  | } | 
|  |  | 
|  | func (s *Span) Start() core.Event { | 
|  | // start never changes after construction, so we dont need to hold the mutex | 
|  | return s.start | 
|  | } | 
|  |  | 
|  | func (s *Span) Finish() core.Event { | 
|  | s.mu.Lock() | 
|  | defer s.mu.Unlock() | 
|  | return s.finish | 
|  | } | 
|  |  | 
|  | func (s *Span) Events() []core.Event { | 
|  | s.mu.Lock() | 
|  | defer s.mu.Unlock() | 
|  | return s.events | 
|  | } | 
|  |  | 
|  | func (s *Span) Format(f fmt.State, r rune) { | 
|  | s.mu.Lock() | 
|  | defer s.mu.Unlock() | 
|  | fmt.Fprintf(f, "%v %v", s.Name, s.ID) | 
|  | if s.ParentID.IsValid() { | 
|  | fmt.Fprintf(f, "[%v]", s.ParentID) | 
|  | } | 
|  | fmt.Fprintf(f, " %v->%v", s.start, s.finish) | 
|  | } |