-
Notifications
You must be signed in to change notification settings - Fork 1
/
trace.go
159 lines (137 loc) · 4.24 KB
/
trace.go
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Package trace defines common-use Dapper-style tracing APIs for the Go programming language.
//
// Package trace provides a backend-agnostic APIs and various tracing providers
// can be used with the package by importing various implementations of the Client interface.
package trace
import (
"context"
"errors"
"net/http"
"time"
)
var client Client
// TODO(jbd): annotate with labels that can propagate, similar to OpenTracing baggage.
// TODO(jbd): should we support a span to have multiple parents?
// TODO(jbd): set error/state on finish.
// TODO(jbd): Avoid things if client = nil.
// TODO(jbd): A big TODO, we probably don't want to set a global client.
func Configure(c Client) {
client = c
}
// Span represents a work.
type Span struct {
ID []byte // represents the global identifier of the span.
Annotations map[string][]byte // annotations set on this span.
}
// Annotate allows you to attach data to a span. Key-value pairs are
// arbitary information you want to collect in the lifetime of a span.
func (s *Span) Annotate(key string, val []byte) {
if s == nil {
return
}
s.Annotations[key] = val
}
// Child creates a child span from s with the given name.
// Created child span needs to be finished by calling
// the finishing function.
func (s *Span) Child(name string, linked ...*Span) (*Span, FinishFunc) {
if s == nil {
return nil, noop
}
child := &Span{
ID: client.NewSpan(s.ID),
Annotations: make(map[string][]byte),
}
start := time.Now()
fn := func() error {
return client.Finish(child.ID, name, spanIDs(linked), child.Annotations, start, time.Now())
}
return child, fn
}
// ToHTTPReq injects the span information in the given request
// and returns the modified request.
//
// If the current client is not supporting HTTP propagation,
// an error is returned.
func (s *Span) ToHTTPReq(req *http.Request) (*http.Request, error) {
if s == nil {
return req, nil
}
hc, ok := client.(HTTPCarrier)
if !ok {
return req, errors.New("not supported")
}
err := hc.ToReq(req, s.ID)
if err != nil {
return nil, err
}
return req, nil
}
// FromHTTPReq creates a *Span from an incoming request.
//
// An error will be returned if the current tracing client is
// not supporting propagation via HTTP.
func FromHTTPReq(req *http.Request) (*Span, error) {
hc, ok := client.(HTTPCarrier)
if !ok {
return nil, errors.New("not supported")
}
id, err := hc.FromReq(req)
if err != nil {
return nil, err
}
return &Span{ID: id}, nil
}
// HTTPCarrier represents a mechanism that can attach the tracing
// information into an HTTP request or extract it from one.
type HTTPCarrier interface {
FromReq(req *http.Request) (id []byte, err error)
ToReq(req *http.Request, id []byte) error
}
// Client represents a client communicates with a tracing backend.
// Tracing backends are supposed to implement the interface in order to
// provide Go support.
//
// A Client is an HTTPCarrier if it can propagate the tracing
// information via an HTTP request.
//
// If you are not a tracing provider, you will never have to interact with
// this interface directly.
type Client interface {
NewSpan(parent []byte) (id []byte)
Finish(id []byte, name string, linked [][]byte, annotations map[string][]byte, start, end time.Time) error
}
// NewSpan creates a new root-level span.
//
// The span must be finished when the job it represents it is finished.
func NewSpan(name string, linked ...*Span) (*Span, FinishFunc) {
span := &Span{
ID: client.NewSpan(nil),
Annotations: make(map[string][]byte),
}
start := time.Now()
fn := func() error {
return client.Finish(span.ID, name, spanIDs(linked), span.Annotations, start, time.Now())
}
return span, fn
}
// NewContext returns a context with the span in.
func NewContext(ctx context.Context, span *Span) context.Context {
return context.WithValue(ctx, spanKey, span)
}
// FromContext returns a span from the given context.
func FromContext(ctx context.Context) *Span {
return ctx.Value(spanKey).(*Span)
}
func spanIDs(spans []*Span) [][]byte {
var links [][]byte
for _, s := range spans {
links = append(links, s.ID)
}
return links
}
// FinishFunc finalizes its span.
type FinishFunc func() error
type contextKey struct{}
var spanKey = contextKey{}
func noop() error { return nil }