File: trace.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.25.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 22,724 kB
  • sloc: javascript: 2,027; asm: 1,645; sh: 166; yacc: 155; makefile: 49; ansic: 8
file content (117 lines) | stat: -rw-r--r-- 2,675 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// 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 don't 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)
}