// 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) }