Browse code

vendor: add new opentracing deps for session tracing

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2018/06/03 02:18:25
Showing 25 changed files
... ...
@@ -26,8 +26,11 @@ github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
26 26
 github.com/imdario/mergo 0.2.1
27 27
 golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
28 28
 
29
+# buildkit
29 30
 github.com/moby/buildkit b14fd548fe80c0399b105aeec5dbd96ccd2f7720
30 31
 github.com/tonistiigi/fsutil dc68c74458923f357474a9178bd198aa3ed11a5f
32
+github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
33
+github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
31 34
 
32 35
 #get libnetwork packages
33 36
 
34 37
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+Copyright (c) 2016, gRPC Ecosystem
1
+All rights reserved.
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions are met:
5
+
6
+* Redistributions of source code must retain the above copyright notice, this
7
+  list of conditions and the following disclaimer.
8
+
9
+* Redistributions in binary form must reproduce the above copyright notice,
10
+  this list of conditions and the following disclaimer in the documentation
11
+  and/or other materials provided with the distribution.
12
+
13
+* Neither the name of grpc-opentracing nor the names of its
14
+  contributors may be used to endorse or promote products derived from
15
+  this software without specific prior written permission.
16
+
17
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 27
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+Additional IP Rights Grant (Patents)
1
+
2
+"This implementation" means the copyrightable works distributed by
3
+Google as part of the GRPC project.
4
+
5
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
6
+no-charge, royalty-free, irrevocable (except as stated in this section)
7
+patent license to make, have made, use, offer to sell, sell, import,
8
+transfer and otherwise run, modify and propagate the contents of this
9
+implementation of GRPC, where such license applies only to those patent
10
+claims, both currently owned or controlled by Google and acquired in
11
+the future, licensable by Google that are necessarily infringed by this
12
+implementation of GRPC.  This grant does not include claims that would be
13
+infringed only as a consequence of further modification of this
14
+implementation.  If you or your agent or exclusive licensee institute or
15
+order or agree to the institution of patent litigation against any
16
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
17
+that this implementation of GRPC or any code incorporated within this
18
+implementation of GRPC constitutes direct or contributory patent
19
+infringement, or inducement of patent infringement, then any patent
20
+rights granted to you under this License for this implementation of GRPC
21
+shall terminate as of the date such litigation is filed.
22
+Status API Training Shop Blog About
0 23
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+################
1
+GRPC-OpenTracing
2
+################
3
+
4
+This package enables distributed tracing in GRPC clients and servers via `The OpenTracing Project`_: a set of consistent, expressive, vendor-neutral APIs for distributed tracing and context propagation.
5
+
6
+Once a production system contends with real concurrency or splits into many services, crucial (and formerly easy) tasks become difficult: user-facing latency optimization, root-cause analysis of backend errors, communication about distinct pieces of a now-distributed system, etc. Distributed tracing follows a request on its journey from inception to completion from mobile/browser all the way to the microservices. 
7
+
8
+As core services and libraries adopt OpenTracing, the application builder is no longer burdened with the task of adding basic tracing instrumentation to their own code. In this way, developers can build their applications with the tools they prefer and benefit from built-in tracing instrumentation. OpenTracing implementations exist for major distributed tracing systems and can be bound or swapped with a one-line configuration change.
9
+
10
+*******************
11
+Further Information
12
+*******************
13
+
14
+If you’re interested in learning more about the OpenTracing standard, join the conversation on our `mailing list`_ or `Gitter`_.
15
+
16
+If you want to learn more about the underlying API for your platform, visit the `source code`_. 
17
+
18
+If you would like to implement OpenTracing in your project and need help, feel free to send us a note at `community@opentracing.io`_.
19
+
20
+.. _The OpenTracing Project: http://opentracing.io/
21
+.. _source code: https://github.com/opentracing/
22
+.. _mailing list: http://opentracing.us13.list-manage.com/subscribe?u=180afe03860541dae59e84153&id=19117aa6cd
23
+.. _Gitter: https://gitter.im/opentracing/public
24
+.. _community@opentracing.io: community@opentracing.io
0 25
new file mode 100644
... ...
@@ -0,0 +1,57 @@
0
+# OpenTracing support for gRPC in Go
1
+
2
+The `otgrpc` package makes it easy to add OpenTracing support to gRPC-based
3
+systems in Go.
4
+
5
+## Installation
6
+
7
+```
8
+go get github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc
9
+```
10
+
11
+## Documentation
12
+
13
+See the basic usage examples below and the [package documentation on
14
+godoc.org](https://godoc.org/github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc).
15
+
16
+## Client-side usage example
17
+
18
+Wherever you call `grpc.Dial`:
19
+
20
+```go
21
+// You must have some sort of OpenTracing Tracer instance on hand.
22
+var tracer opentracing.Tracer = ...
23
+...
24
+
25
+// Set up a connection to the server peer.
26
+conn, err := grpc.Dial(
27
+    address,
28
+    ... // other options
29
+    grpc.WithUnaryInterceptor(
30
+        otgrpc.OpenTracingClientInterceptor(tracer)),
31
+    grpc.WithStreamInterceptor(
32
+        otgrpc.OpenTracingStreamClientInterceptor(tracer)))
33
+
34
+// All future RPC activity involving `conn` will be automatically traced.
35
+```
36
+
37
+## Server-side usage example
38
+
39
+Wherever you call `grpc.NewServer`:
40
+
41
+```go
42
+// You must have some sort of OpenTracing Tracer instance on hand.
43
+var tracer opentracing.Tracer = ...
44
+...
45
+
46
+// Initialize the gRPC server.
47
+s := grpc.NewServer(
48
+    ... // other options
49
+    grpc.UnaryInterceptor(
50
+        otgrpc.OpenTracingServerInterceptor(tracer)),
51
+    grpc.StreamInterceptor(
52
+        otgrpc.OpenTracingStreamServerInterceptor(tracer)))
53
+
54
+// All future RPC activity involving `s` will be automatically traced.
55
+```
56
+
0 57
new file mode 100644
... ...
@@ -0,0 +1,239 @@
0
+package otgrpc
1
+
2
+import (
3
+	"github.com/opentracing/opentracing-go"
4
+	"github.com/opentracing/opentracing-go/ext"
5
+	"github.com/opentracing/opentracing-go/log"
6
+	"golang.org/x/net/context"
7
+	"google.golang.org/grpc"
8
+	"google.golang.org/grpc/metadata"
9
+	"io"
10
+	"runtime"
11
+	"sync/atomic"
12
+)
13
+
14
+// OpenTracingClientInterceptor returns a grpc.UnaryClientInterceptor suitable
15
+// for use in a grpc.Dial call.
16
+//
17
+// For example:
18
+//
19
+//     conn, err := grpc.Dial(
20
+//         address,
21
+//         ...,  // (existing DialOptions)
22
+//         grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(tracer)))
23
+//
24
+// All gRPC client spans will inject the OpenTracing SpanContext into the gRPC
25
+// metadata; they will also look in the context.Context for an active
26
+// in-process parent Span and establish a ChildOf reference if such a parent
27
+// Span could be found.
28
+func OpenTracingClientInterceptor(tracer opentracing.Tracer, optFuncs ...Option) grpc.UnaryClientInterceptor {
29
+	otgrpcOpts := newOptions()
30
+	otgrpcOpts.apply(optFuncs...)
31
+	return func(
32
+		ctx context.Context,
33
+		method string,
34
+		req, resp interface{},
35
+		cc *grpc.ClientConn,
36
+		invoker grpc.UnaryInvoker,
37
+		opts ...grpc.CallOption,
38
+	) error {
39
+		var err error
40
+		var parentCtx opentracing.SpanContext
41
+		if parent := opentracing.SpanFromContext(ctx); parent != nil {
42
+			parentCtx = parent.Context()
43
+		}
44
+		if otgrpcOpts.inclusionFunc != nil &&
45
+			!otgrpcOpts.inclusionFunc(parentCtx, method, req, resp) {
46
+			return invoker(ctx, method, req, resp, cc, opts...)
47
+		}
48
+		clientSpan := tracer.StartSpan(
49
+			method,
50
+			opentracing.ChildOf(parentCtx),
51
+			ext.SpanKindRPCClient,
52
+			gRPCComponentTag,
53
+		)
54
+		defer clientSpan.Finish()
55
+		ctx = injectSpanContext(ctx, tracer, clientSpan)
56
+		if otgrpcOpts.logPayloads {
57
+			clientSpan.LogFields(log.Object("gRPC request", req))
58
+		}
59
+		err = invoker(ctx, method, req, resp, cc, opts...)
60
+		if err == nil {
61
+			if otgrpcOpts.logPayloads {
62
+				clientSpan.LogFields(log.Object("gRPC response", resp))
63
+			}
64
+		} else {
65
+			SetSpanTags(clientSpan, err, true)
66
+			clientSpan.LogFields(log.String("event", "error"), log.String("message", err.Error()))
67
+		}
68
+		if otgrpcOpts.decorator != nil {
69
+			otgrpcOpts.decorator(clientSpan, method, req, resp, err)
70
+		}
71
+		return err
72
+	}
73
+}
74
+
75
+// OpenTracingStreamClientInterceptor returns a grpc.StreamClientInterceptor suitable
76
+// for use in a grpc.Dial call. The interceptor instruments streaming RPCs by creating
77
+// a single span to correspond to the lifetime of the RPC's stream.
78
+//
79
+// For example:
80
+//
81
+//     conn, err := grpc.Dial(
82
+//         address,
83
+//         ...,  // (existing DialOptions)
84
+//         grpc.WithStreamInterceptor(otgrpc.OpenTracingStreamClientInterceptor(tracer)))
85
+//
86
+// All gRPC client spans will inject the OpenTracing SpanContext into the gRPC
87
+// metadata; they will also look in the context.Context for an active
88
+// in-process parent Span and establish a ChildOf reference if such a parent
89
+// Span could be found.
90
+func OpenTracingStreamClientInterceptor(tracer opentracing.Tracer, optFuncs ...Option) grpc.StreamClientInterceptor {
91
+	otgrpcOpts := newOptions()
92
+	otgrpcOpts.apply(optFuncs...)
93
+	return func(
94
+		ctx context.Context,
95
+		desc *grpc.StreamDesc,
96
+		cc *grpc.ClientConn,
97
+		method string,
98
+		streamer grpc.Streamer,
99
+		opts ...grpc.CallOption,
100
+	) (grpc.ClientStream, error) {
101
+		var err error
102
+		var parentCtx opentracing.SpanContext
103
+		if parent := opentracing.SpanFromContext(ctx); parent != nil {
104
+			parentCtx = parent.Context()
105
+		}
106
+		if otgrpcOpts.inclusionFunc != nil &&
107
+			!otgrpcOpts.inclusionFunc(parentCtx, method, nil, nil) {
108
+			return streamer(ctx, desc, cc, method, opts...)
109
+		}
110
+
111
+		clientSpan := tracer.StartSpan(
112
+			method,
113
+			opentracing.ChildOf(parentCtx),
114
+			ext.SpanKindRPCClient,
115
+			gRPCComponentTag,
116
+		)
117
+		ctx = injectSpanContext(ctx, tracer, clientSpan)
118
+		cs, err := streamer(ctx, desc, cc, method, opts...)
119
+		if err != nil {
120
+			clientSpan.LogFields(log.String("event", "error"), log.String("message", err.Error()))
121
+			SetSpanTags(clientSpan, err, true)
122
+			clientSpan.Finish()
123
+			return cs, err
124
+		}
125
+		return newOpenTracingClientStream(cs, method, desc, clientSpan, otgrpcOpts), nil
126
+	}
127
+}
128
+
129
+func newOpenTracingClientStream(cs grpc.ClientStream, method string, desc *grpc.StreamDesc, clientSpan opentracing.Span, otgrpcOpts *options) grpc.ClientStream {
130
+	finishChan := make(chan struct{})
131
+
132
+	isFinished := new(int32)
133
+	*isFinished = 0
134
+	finishFunc := func(err error) {
135
+		// The current OpenTracing specification forbids finishing a span more than
136
+		// once. Since we have multiple code paths that could concurrently call
137
+		// `finishFunc`, we need to add some sort of synchronization to guard against
138
+		// multiple finishing.
139
+		if !atomic.CompareAndSwapInt32(isFinished, 0, 1) {
140
+			return
141
+		}
142
+		close(finishChan)
143
+		defer clientSpan.Finish()
144
+		if err != nil {
145
+			clientSpan.LogFields(log.String("event", "error"), log.String("message", err.Error()))
146
+			SetSpanTags(clientSpan, err, true)
147
+		}
148
+		if otgrpcOpts.decorator != nil {
149
+			otgrpcOpts.decorator(clientSpan, method, nil, nil, err)
150
+		}
151
+	}
152
+	go func() {
153
+		select {
154
+		case <-finishChan:
155
+			// The client span is being finished by another code path; hence, no
156
+			// action is necessary.
157
+		case <-cs.Context().Done():
158
+			finishFunc(cs.Context().Err())
159
+		}
160
+	}()
161
+	otcs := &openTracingClientStream{
162
+		ClientStream: cs,
163
+		desc:         desc,
164
+		finishFunc:   finishFunc,
165
+	}
166
+
167
+	// The `ClientStream` interface allows one to omit calling `Recv` if it's
168
+	// known that the result will be `io.EOF`. See
169
+	// http://stackoverflow.com/q/42915337
170
+	// In such cases, there's nothing that triggers the span to finish. We,
171
+	// therefore, set a finalizer so that the span and the context goroutine will
172
+	// at least be cleaned up when the garbage collector is run.
173
+	runtime.SetFinalizer(otcs, func(otcs *openTracingClientStream) {
174
+		otcs.finishFunc(nil)
175
+	})
176
+	return otcs
177
+}
178
+
179
+type openTracingClientStream struct {
180
+	grpc.ClientStream
181
+	desc       *grpc.StreamDesc
182
+	finishFunc func(error)
183
+}
184
+
185
+func (cs *openTracingClientStream) Header() (metadata.MD, error) {
186
+	md, err := cs.ClientStream.Header()
187
+	if err != nil {
188
+		cs.finishFunc(err)
189
+	}
190
+	return md, err
191
+}
192
+
193
+func (cs *openTracingClientStream) SendMsg(m interface{}) error {
194
+	err := cs.ClientStream.SendMsg(m)
195
+	if err != nil {
196
+		cs.finishFunc(err)
197
+	}
198
+	return err
199
+}
200
+
201
+func (cs *openTracingClientStream) RecvMsg(m interface{}) error {
202
+	err := cs.ClientStream.RecvMsg(m)
203
+	if err == io.EOF {
204
+		cs.finishFunc(nil)
205
+		return err
206
+	} else if err != nil {
207
+		cs.finishFunc(err)
208
+		return err
209
+	}
210
+	if !cs.desc.ServerStreams {
211
+		cs.finishFunc(nil)
212
+	}
213
+	return err
214
+}
215
+
216
+func (cs *openTracingClientStream) CloseSend() error {
217
+	err := cs.ClientStream.CloseSend()
218
+	if err != nil {
219
+		cs.finishFunc(err)
220
+	}
221
+	return err
222
+}
223
+
224
+func injectSpanContext(ctx context.Context, tracer opentracing.Tracer, clientSpan opentracing.Span) context.Context {
225
+	md, ok := metadata.FromOutgoingContext(ctx)
226
+	if !ok {
227
+		md = metadata.New(nil)
228
+	} else {
229
+		md = md.Copy()
230
+	}
231
+	mdWriter := metadataReaderWriter{md}
232
+	err := tracer.Inject(clientSpan.Context(), opentracing.HTTPHeaders, mdWriter)
233
+	// We have no better place to record an error than the Span itself :-/
234
+	if err != nil {
235
+		clientSpan.LogFields(log.String("event", "Tracer.Inject() failed"), log.Error(err))
236
+	}
237
+	return metadata.NewOutgoingContext(ctx, md)
238
+}
0 239
new file mode 100644
... ...
@@ -0,0 +1,69 @@
0
+package otgrpc
1
+
2
+import (
3
+	"github.com/opentracing/opentracing-go"
4
+	"github.com/opentracing/opentracing-go/ext"
5
+	"google.golang.org/grpc/codes"
6
+	"google.golang.org/grpc/status"
7
+)
8
+
9
+// A Class is a set of types of outcomes (including errors) that will often
10
+// be handled in the same way.
11
+type Class string
12
+
13
+const (
14
+	Unknown Class = "0xx"
15
+	// Success represents outcomes that achieved the desired results.
16
+	Success Class = "2xx"
17
+	// ClientError represents errors that were the client's fault.
18
+	ClientError Class = "4xx"
19
+	// ServerError represents errors that were the server's fault.
20
+	ServerError Class = "5xx"
21
+)
22
+
23
+// ErrorClass returns the class of the given error
24
+func ErrorClass(err error) Class {
25
+	if s, ok := status.FromError(err); ok {
26
+		switch s.Code() {
27
+		// Success or "success"
28
+		case codes.OK, codes.Canceled:
29
+			return Success
30
+
31
+		// Client errors
32
+		case codes.InvalidArgument, codes.NotFound, codes.AlreadyExists,
33
+			codes.PermissionDenied, codes.Unauthenticated, codes.FailedPrecondition,
34
+			codes.OutOfRange:
35
+			return ClientError
36
+
37
+		// Server errors
38
+		case codes.DeadlineExceeded, codes.ResourceExhausted, codes.Aborted,
39
+			codes.Unimplemented, codes.Internal, codes.Unavailable, codes.DataLoss:
40
+			return ServerError
41
+
42
+		// Not sure
43
+		case codes.Unknown:
44
+			fallthrough
45
+		default:
46
+			return Unknown
47
+		}
48
+	}
49
+	return Unknown
50
+}
51
+
52
+// SetSpanTags sets one or more tags on the given span according to the
53
+// error.
54
+func SetSpanTags(span opentracing.Span, err error, client bool) {
55
+	c := ErrorClass(err)
56
+	code := codes.Unknown
57
+	if s, ok := status.FromError(err); ok {
58
+		code = s.Code()
59
+	}
60
+	span.SetTag("response_code", code)
61
+	span.SetTag("response_class", c)
62
+	if err == nil {
63
+		return
64
+	}
65
+	if client || c == ServerError {
66
+		ext.Error.Set(span, true)
67
+	}
68
+}
0 69
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+package otgrpc
1
+
2
+import "github.com/opentracing/opentracing-go"
3
+
4
+// Option instances may be used in OpenTracing(Server|Client)Interceptor
5
+// initialization.
6
+//
7
+// See this post about the "functional options" pattern:
8
+// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
9
+type Option func(o *options)
10
+
11
+// LogPayloads returns an Option that tells the OpenTracing instrumentation to
12
+// try to log application payloads in both directions.
13
+func LogPayloads() Option {
14
+	return func(o *options) {
15
+		o.logPayloads = true
16
+	}
17
+}
18
+
19
+// SpanInclusionFunc provides an optional mechanism to decide whether or not
20
+// to trace a given gRPC call. Return true to create a Span and initiate
21
+// tracing, false to not create a Span and not trace.
22
+//
23
+// parentSpanCtx may be nil if no parent could be extraction from either the Go
24
+// context.Context (on the client) or the RPC (on the server).
25
+type SpanInclusionFunc func(
26
+	parentSpanCtx opentracing.SpanContext,
27
+	method string,
28
+	req, resp interface{}) bool
29
+
30
+// IncludingSpans binds a IncludeSpanFunc to the options
31
+func IncludingSpans(inclusionFunc SpanInclusionFunc) Option {
32
+	return func(o *options) {
33
+		o.inclusionFunc = inclusionFunc
34
+	}
35
+}
36
+
37
+// SpanDecoratorFunc provides an (optional) mechanism for otgrpc users to add
38
+// arbitrary tags/logs/etc to the opentracing.Span associated with client
39
+// and/or server RPCs.
40
+type SpanDecoratorFunc func(
41
+	span opentracing.Span,
42
+	method string,
43
+	req, resp interface{},
44
+	grpcError error)
45
+
46
+// SpanDecorator binds a function that decorates gRPC Spans.
47
+func SpanDecorator(decorator SpanDecoratorFunc) Option {
48
+	return func(o *options) {
49
+		o.decorator = decorator
50
+	}
51
+}
52
+
53
+// The internal-only options struct. Obviously overkill at the moment; but will
54
+// scale well as production use dictates other configuration and tuning
55
+// parameters.
56
+type options struct {
57
+	logPayloads bool
58
+	decorator   SpanDecoratorFunc
59
+	// May be nil.
60
+	inclusionFunc SpanInclusionFunc
61
+}
62
+
63
+// newOptions returns the default options.
64
+func newOptions() *options {
65
+	return &options{
66
+		logPayloads:   false,
67
+		inclusionFunc: nil,
68
+	}
69
+}
70
+
71
+func (o *options) apply(opts ...Option) {
72
+	for _, opt := range opts {
73
+		opt(o)
74
+	}
75
+}
0 76
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+// Package otgrpc provides OpenTracing support for any gRPC client or server.
1
+//
2
+// See the README for simple usage examples:
3
+// https://github.com/grpc-ecosystem/grpc-opentracing/blob/master/go/otgrpc/README.md
4
+package otgrpc
0 5
new file mode 100644
... ...
@@ -0,0 +1,141 @@
0
+package otgrpc
1
+
2
+import (
3
+	"github.com/opentracing/opentracing-go"
4
+	"github.com/opentracing/opentracing-go/ext"
5
+	"github.com/opentracing/opentracing-go/log"
6
+	"golang.org/x/net/context"
7
+	"google.golang.org/grpc"
8
+	"google.golang.org/grpc/metadata"
9
+)
10
+
11
+// OpenTracingServerInterceptor returns a grpc.UnaryServerInterceptor suitable
12
+// for use in a grpc.NewServer call.
13
+//
14
+// For example:
15
+//
16
+//     s := grpc.NewServer(
17
+//         ...,  // (existing ServerOptions)
18
+//         grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer)))
19
+//
20
+// All gRPC server spans will look for an OpenTracing SpanContext in the gRPC
21
+// metadata; if found, the server span will act as the ChildOf that RPC
22
+// SpanContext.
23
+//
24
+// Root or not, the server Span will be embedded in the context.Context for the
25
+// application-specific gRPC handler(s) to access.
26
+func OpenTracingServerInterceptor(tracer opentracing.Tracer, optFuncs ...Option) grpc.UnaryServerInterceptor {
27
+	otgrpcOpts := newOptions()
28
+	otgrpcOpts.apply(optFuncs...)
29
+	return func(
30
+		ctx context.Context,
31
+		req interface{},
32
+		info *grpc.UnaryServerInfo,
33
+		handler grpc.UnaryHandler,
34
+	) (resp interface{}, err error) {
35
+		spanContext, err := extractSpanContext(ctx, tracer)
36
+		if err != nil && err != opentracing.ErrSpanContextNotFound {
37
+			// TODO: establish some sort of error reporting mechanism here. We
38
+			// don't know where to put such an error and must rely on Tracer
39
+			// implementations to do something appropriate for the time being.
40
+		}
41
+		if otgrpcOpts.inclusionFunc != nil &&
42
+			!otgrpcOpts.inclusionFunc(spanContext, info.FullMethod, req, nil) {
43
+			return handler(ctx, req)
44
+		}
45
+		serverSpan := tracer.StartSpan(
46
+			info.FullMethod,
47
+			ext.RPCServerOption(spanContext),
48
+			gRPCComponentTag,
49
+		)
50
+		defer serverSpan.Finish()
51
+
52
+		ctx = opentracing.ContextWithSpan(ctx, serverSpan)
53
+		if otgrpcOpts.logPayloads {
54
+			serverSpan.LogFields(log.Object("gRPC request", req))
55
+		}
56
+		resp, err = handler(ctx, req)
57
+		if err == nil {
58
+			if otgrpcOpts.logPayloads {
59
+				serverSpan.LogFields(log.Object("gRPC response", resp))
60
+			}
61
+		} else {
62
+			SetSpanTags(serverSpan, err, false)
63
+			serverSpan.LogFields(log.String("event", "error"), log.String("message", err.Error()))
64
+		}
65
+		if otgrpcOpts.decorator != nil {
66
+			otgrpcOpts.decorator(serverSpan, info.FullMethod, req, resp, err)
67
+		}
68
+		return resp, err
69
+	}
70
+}
71
+
72
+// OpenTracingStreamServerInterceptor returns a grpc.StreamServerInterceptor suitable
73
+// for use in a grpc.NewServer call. The interceptor instruments streaming RPCs by
74
+// creating a single span to correspond to the lifetime of the RPC's stream.
75
+//
76
+// For example:
77
+//
78
+//     s := grpc.NewServer(
79
+//         ...,  // (existing ServerOptions)
80
+//         grpc.StreamInterceptor(otgrpc.OpenTracingStreamServerInterceptor(tracer)))
81
+//
82
+// All gRPC server spans will look for an OpenTracing SpanContext in the gRPC
83
+// metadata; if found, the server span will act as the ChildOf that RPC
84
+// SpanContext.
85
+//
86
+// Root or not, the server Span will be embedded in the context.Context for the
87
+// application-specific gRPC handler(s) to access.
88
+func OpenTracingStreamServerInterceptor(tracer opentracing.Tracer, optFuncs ...Option) grpc.StreamServerInterceptor {
89
+	otgrpcOpts := newOptions()
90
+	otgrpcOpts.apply(optFuncs...)
91
+	return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
92
+		spanContext, err := extractSpanContext(ss.Context(), tracer)
93
+		if err != nil && err != opentracing.ErrSpanContextNotFound {
94
+			// TODO: establish some sort of error reporting mechanism here. We
95
+			// don't know where to put such an error and must rely on Tracer
96
+			// implementations to do something appropriate for the time being.
97
+		}
98
+		if otgrpcOpts.inclusionFunc != nil &&
99
+			!otgrpcOpts.inclusionFunc(spanContext, info.FullMethod, nil, nil) {
100
+			return handler(srv, ss)
101
+		}
102
+
103
+		serverSpan := tracer.StartSpan(
104
+			info.FullMethod,
105
+			ext.RPCServerOption(spanContext),
106
+			gRPCComponentTag,
107
+		)
108
+		defer serverSpan.Finish()
109
+		ss = &openTracingServerStream{
110
+			ServerStream: ss,
111
+			ctx:          opentracing.ContextWithSpan(ss.Context(), serverSpan),
112
+		}
113
+		err = handler(srv, ss)
114
+		if err != nil {
115
+			SetSpanTags(serverSpan, err, false)
116
+			serverSpan.LogFields(log.String("event", "error"), log.String("message", err.Error()))
117
+		}
118
+		if otgrpcOpts.decorator != nil {
119
+			otgrpcOpts.decorator(serverSpan, info.FullMethod, nil, nil, err)
120
+		}
121
+		return err
122
+	}
123
+}
124
+
125
+type openTracingServerStream struct {
126
+	grpc.ServerStream
127
+	ctx context.Context
128
+}
129
+
130
+func (ss *openTracingServerStream) Context() context.Context {
131
+	return ss.ctx
132
+}
133
+
134
+func extractSpanContext(ctx context.Context, tracer opentracing.Tracer) (opentracing.SpanContext, error) {
135
+	md, ok := metadata.FromIncomingContext(ctx)
136
+	if !ok {
137
+		md = metadata.New(nil)
138
+	}
139
+	return tracer.Extract(opentracing.HTTPHeaders, metadataReaderWriter{md})
140
+}
0 141
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+package otgrpc
1
+
2
+import (
3
+	"strings"
4
+
5
+	opentracing "github.com/opentracing/opentracing-go"
6
+	"github.com/opentracing/opentracing-go/ext"
7
+	"google.golang.org/grpc/metadata"
8
+)
9
+
10
+var (
11
+	// Morally a const:
12
+	gRPCComponentTag = opentracing.Tag{string(ext.Component), "gRPC"}
13
+)
14
+
15
+// metadataReaderWriter satisfies both the opentracing.TextMapReader and
16
+// opentracing.TextMapWriter interfaces.
17
+type metadataReaderWriter struct {
18
+	metadata.MD
19
+}
20
+
21
+func (w metadataReaderWriter) Set(key, val string) {
22
+	// The GRPC HPACK implementation rejects any uppercase keys here.
23
+	//
24
+	// As such, since the HTTP_HEADERS format is case-insensitive anyway, we
25
+	// blindly lowercase the key (which is guaranteed to work in the
26
+	// Inject/Extract sense per the OpenTracing spec).
27
+	key = strings.ToLower(key)
28
+	w.MD[key] = append(w.MD[key], val)
29
+}
30
+
31
+func (w metadataReaderWriter) ForeachKey(handler func(key, val string) error) error {
32
+	for k, vals := range w.MD {
33
+		for _, v := range vals {
34
+			if err := handler(k, v); err != nil {
35
+				return err
36
+			}
37
+		}
38
+	}
39
+
40
+	return nil
41
+}
0 42
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+The repo has moved.
1
+-------------------
2
+
3
+https://github.com/opentracing-contrib/python-grpc
0 4
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+syntax = "proto3";
1
+
2
+package command_line;
3
+
4
+service CommandLine {
5
+  rpc Echo(CommandRequest) returns (CommandResponse) {}
6
+}
7
+
8
+message CommandRequest {
9
+  string text = 1;
10
+}
11
+
12
+message CommandResponse {
13
+  string text = 1;
14
+}
0 15
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+syntax = "proto3";
1
+
2
+package store;
3
+
4
+service Store {
5
+  rpc AddItem(AddItemRequest) returns (Empty) {}
6
+  rpc AddItems(stream AddItemRequest) returns (Empty) {}
7
+  rpc RemoveItem(RemoveItemRequest) returns (RemoveItemResponse) {}
8
+  rpc RemoveItems(stream RemoveItemRequest) returns (RemoveItemResponse) {}
9
+  rpc ListInventory(Empty) returns (stream QuantityResponse) {}
10
+  rpc QueryQuantity(QueryItemRequest) returns (QuantityResponse) {}
11
+  rpc QueryQuantities(stream QueryItemRequest) 
12
+                          returns (stream QuantityResponse) {}
13
+}
14
+
15
+message Empty {}
16
+
17
+message AddItemRequest {
18
+  string name = 1;
19
+}
20
+
21
+message RemoveItemRequest {
22
+  string name = 1;
23
+}
24
+
25
+message RemoveItemResponse {
26
+  bool was_successful = 1;
27
+}
28
+
29
+message QueryItemRequest {
30
+  string name = 1;
31
+}
32
+
33
+message QuantityResponse {
34
+  string name = 1;
35
+  int32 count = 2;
36
+}
0 37
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+The MIT License (MIT)
1
+
2
+Copyright (c) 2016 The OpenTracing Authors
3
+
4
+Permission is hereby granted, free of charge, to any person obtaining a copy
5
+of this software and associated documentation files (the "Software"), to deal
6
+in the Software without restriction, including without limitation the rights
7
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+copies of the Software, and to permit persons to whom the Software is
9
+furnished to do so, subject to the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be included in all
12
+copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+SOFTWARE.
0 21
new file mode 100644
... ...
@@ -0,0 +1,163 @@
0
+[![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/opentracing/public) [![Build Status](https://travis-ci.org/opentracing/opentracing-go.svg?branch=master)](https://travis-ci.org/opentracing/opentracing-go) [![GoDoc](https://godoc.org/github.com/opentracing/opentracing-go?status.svg)](http://godoc.org/github.com/opentracing/opentracing-go)
1
+[![Sourcegraph Badge](https://sourcegraph.com/github.com/opentracing/opentracing-go/-/badge.svg)](https://sourcegraph.com/github.com/opentracing/opentracing-go?badge)
2
+
3
+# OpenTracing API for Go
4
+
5
+This package is a Go platform API for OpenTracing.
6
+
7
+## Required Reading
8
+
9
+In order to understand the Go platform API, one must first be familiar with the
10
+[OpenTracing project](http://opentracing.io) and
11
+[terminology](http://opentracing.io/documentation/pages/spec.html) more specifically.
12
+
13
+## API overview for those adding instrumentation
14
+
15
+Everyday consumers of this `opentracing` package really only need to worry
16
+about a couple of key abstractions: the `StartSpan` function, the `Span`
17
+interface, and binding a `Tracer` at `main()`-time. Here are code snippets
18
+demonstrating some important use cases.
19
+
20
+#### Singleton initialization
21
+
22
+The simplest starting point is `./default_tracer.go`. As early as possible, call
23
+
24
+```go
25
+    import "github.com/opentracing/opentracing-go"
26
+    import ".../some_tracing_impl"
27
+
28
+    func main() {
29
+        opentracing.InitGlobalTracer(
30
+            // tracing impl specific:
31
+            some_tracing_impl.New(...),
32
+        )
33
+        ...
34
+    }
35
+```
36
+
37
+##### Non-Singleton initialization
38
+
39
+If you prefer direct control to singletons, manage ownership of the
40
+`opentracing.Tracer` implementation explicitly.
41
+
42
+#### Creating a Span given an existing Go `context.Context`
43
+
44
+If you use `context.Context` in your application, OpenTracing's Go library will
45
+happily rely on it for `Span` propagation. To start a new (blocking child)
46
+`Span`, you can use `StartSpanFromContext`.
47
+
48
+```go
49
+    func xyz(ctx context.Context, ...) {
50
+        ...
51
+        span, ctx := opentracing.StartSpanFromContext(ctx, "operation_name")
52
+        defer span.Finish()
53
+        span.LogFields(
54
+            log.String("event", "soft error"),
55
+            log.String("type", "cache timeout"),
56
+            log.Int("waited.millis", 1500))
57
+        ...
58
+    }
59
+```
60
+
61
+#### Starting an empty trace by creating a "root span"
62
+
63
+It's always possible to create a "root" `Span` with no parent or other causal
64
+reference.
65
+
66
+```go
67
+    func xyz() {
68
+        ...
69
+        sp := opentracing.StartSpan("operation_name")
70
+        defer sp.Finish()
71
+        ...
72
+    }
73
+```
74
+
75
+#### Creating a (child) Span given an existing (parent) Span
76
+
77
+```go
78
+    func xyz(parentSpan opentracing.Span, ...) {
79
+        ...
80
+        sp := opentracing.StartSpan(
81
+            "operation_name",
82
+            opentracing.ChildOf(parentSpan.Context()))
83
+        defer sp.Finish()
84
+        ...
85
+    }
86
+```
87
+
88
+#### Serializing to the wire
89
+
90
+```go
91
+    func makeSomeRequest(ctx context.Context) ... {
92
+        if span := opentracing.SpanFromContext(ctx); span != nil {
93
+            httpClient := &http.Client{}
94
+            httpReq, _ := http.NewRequest("GET", "http://myservice/", nil)
95
+
96
+            // Transmit the span's TraceContext as HTTP headers on our
97
+            // outbound request.
98
+            opentracing.GlobalTracer().Inject(
99
+                span.Context(),
100
+                opentracing.HTTPHeaders,
101
+                opentracing.HTTPHeadersCarrier(httpReq.Header))
102
+
103
+            resp, err := httpClient.Do(httpReq)
104
+            ...
105
+        }
106
+        ...
107
+    }
108
+```
109
+
110
+#### Deserializing from the wire
111
+
112
+```go
113
+    http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
114
+        var serverSpan opentracing.Span
115
+        appSpecificOperationName := ...
116
+        wireContext, err := opentracing.GlobalTracer().Extract(
117
+            opentracing.HTTPHeaders,
118
+            opentracing.HTTPHeadersCarrier(req.Header))
119
+        if err != nil {
120
+            // Optionally record something about err here
121
+        }
122
+
123
+        // Create the span referring to the RPC client if available.
124
+        // If wireContext == nil, a root span will be created.
125
+        serverSpan = opentracing.StartSpan(
126
+            appSpecificOperationName,
127
+            ext.RPCServerOption(wireContext))
128
+
129
+        defer serverSpan.Finish()
130
+
131
+        ctx := opentracing.ContextWithSpan(context.Background(), serverSpan)
132
+        ...
133
+    }
134
+```
135
+
136
+#### Conditionally capture a field using `log.Noop`
137
+
138
+In some situations, you may want to dynamically decide whether or not
139
+to log a field.  For example, you may want to capture additional data,
140
+such as a customer ID, in non-production environments:
141
+
142
+```go
143
+    func Customer(order *Order) log.Field {
144
+        if os.Getenv("ENVIRONMENT") == "dev" {
145
+            return log.String("customer", order.Customer.ID)
146
+        }
147
+        return log.Noop()
148
+    }
149
+```
150
+
151
+#### Goroutine-safety
152
+
153
+The entire public API is goroutine-safe and does not require external
154
+synchronization.
155
+
156
+## API pointers for those implementing a tracing system
157
+
158
+Tracing system implementors may be able to reuse or copy-paste-modify the `basictracer` package, found [here](https://github.com/opentracing/basictracer-go). In particular, see `basictracer.New(...)`.
159
+
160
+## API compatibility
161
+
162
+For the time being, "mild" backwards-incompatible changes may be made without changing the major version number. As OpenTracing and `opentracing-go` mature, backwards compatibility will become more of a priority.
0 163
new file mode 100644
... ...
@@ -0,0 +1,210 @@
0
+package ext
1
+
2
+import opentracing "github.com/opentracing/opentracing-go"
3
+
4
+// These constants define common tag names recommended for better portability across
5
+// tracing systems and languages/platforms.
6
+//
7
+// The tag names are defined as typed strings, so that in addition to the usual use
8
+//
9
+//     span.setTag(TagName, value)
10
+//
11
+// they also support value type validation via this additional syntax:
12
+//
13
+//    TagName.Set(span, value)
14
+//
15
+var (
16
+	//////////////////////////////////////////////////////////////////////
17
+	// SpanKind (client/server or producer/consumer)
18
+	//////////////////////////////////////////////////////////////////////
19
+
20
+	// SpanKind hints at relationship between spans, e.g. client/server
21
+	SpanKind = spanKindTagName("span.kind")
22
+
23
+	// SpanKindRPCClient marks a span representing the client-side of an RPC
24
+	// or other remote call
25
+	SpanKindRPCClientEnum = SpanKindEnum("client")
26
+	SpanKindRPCClient     = opentracing.Tag{Key: string(SpanKind), Value: SpanKindRPCClientEnum}
27
+
28
+	// SpanKindRPCServer marks a span representing the server-side of an RPC
29
+	// or other remote call
30
+	SpanKindRPCServerEnum = SpanKindEnum("server")
31
+	SpanKindRPCServer     = opentracing.Tag{Key: string(SpanKind), Value: SpanKindRPCServerEnum}
32
+
33
+	// SpanKindProducer marks a span representing the producer-side of a
34
+	// message bus
35
+	SpanKindProducerEnum = SpanKindEnum("producer")
36
+	SpanKindProducer     = opentracing.Tag{Key: string(SpanKind), Value: SpanKindProducerEnum}
37
+
38
+	// SpanKindConsumer marks a span representing the consumer-side of a
39
+	// message bus
40
+	SpanKindConsumerEnum = SpanKindEnum("consumer")
41
+	SpanKindConsumer     = opentracing.Tag{Key: string(SpanKind), Value: SpanKindConsumerEnum}
42
+
43
+	//////////////////////////////////////////////////////////////////////
44
+	// Component name
45
+	//////////////////////////////////////////////////////////////////////
46
+
47
+	// Component is a low-cardinality identifier of the module, library,
48
+	// or package that is generating a span.
49
+	Component = stringTagName("component")
50
+
51
+	//////////////////////////////////////////////////////////////////////
52
+	// Sampling hint
53
+	//////////////////////////////////////////////////////////////////////
54
+
55
+	// SamplingPriority determines the priority of sampling this Span.
56
+	SamplingPriority = uint16TagName("sampling.priority")
57
+
58
+	//////////////////////////////////////////////////////////////////////
59
+	// Peer tags. These tags can be emitted by either client-side of
60
+	// server-side to describe the other side/service in a peer-to-peer
61
+	// communications, like an RPC call.
62
+	//////////////////////////////////////////////////////////////////////
63
+
64
+	// PeerService records the service name of the peer.
65
+	PeerService = stringTagName("peer.service")
66
+
67
+	// PeerAddress records the address name of the peer. This may be a "ip:port",
68
+	// a bare "hostname", a FQDN or even a database DSN substring
69
+	// like "mysql://username@127.0.0.1:3306/dbname"
70
+	PeerAddress = stringTagName("peer.address")
71
+
72
+	// PeerHostname records the host name of the peer
73
+	PeerHostname = stringTagName("peer.hostname")
74
+
75
+	// PeerHostIPv4 records IP v4 host address of the peer
76
+	PeerHostIPv4 = ipv4Tag("peer.ipv4")
77
+
78
+	// PeerHostIPv6 records IP v6 host address of the peer
79
+	PeerHostIPv6 = stringTagName("peer.ipv6")
80
+
81
+	// PeerPort records port number of the peer
82
+	PeerPort = uint16TagName("peer.port")
83
+
84
+	//////////////////////////////////////////////////////////////////////
85
+	// HTTP Tags
86
+	//////////////////////////////////////////////////////////////////////
87
+
88
+	// HTTPUrl should be the URL of the request being handled in this segment
89
+	// of the trace, in standard URI format. The protocol is optional.
90
+	HTTPUrl = stringTagName("http.url")
91
+
92
+	// HTTPMethod is the HTTP method of the request, and is case-insensitive.
93
+	HTTPMethod = stringTagName("http.method")
94
+
95
+	// HTTPStatusCode is the numeric HTTP status code (200, 404, etc) of the
96
+	// HTTP response.
97
+	HTTPStatusCode = uint16TagName("http.status_code")
98
+
99
+	//////////////////////////////////////////////////////////////////////
100
+	// DB Tags
101
+	//////////////////////////////////////////////////////////////////////
102
+
103
+	// DBInstance is database instance name.
104
+	DBInstance = stringTagName("db.instance")
105
+
106
+	// DBStatement is a database statement for the given database type.
107
+	// It can be a query or a prepared statement (i.e., before substitution).
108
+	DBStatement = stringTagName("db.statement")
109
+
110
+	// DBType is a database type. For any SQL database, "sql".
111
+	// For others, the lower-case database category, e.g. "redis"
112
+	DBType = stringTagName("db.type")
113
+
114
+	// DBUser is a username for accessing database.
115
+	DBUser = stringTagName("db.user")
116
+
117
+	//////////////////////////////////////////////////////////////////////
118
+	// Message Bus Tag
119
+	//////////////////////////////////////////////////////////////////////
120
+
121
+	// MessageBusDestination is an address at which messages can be exchanged
122
+	MessageBusDestination = stringTagName("message_bus.destination")
123
+
124
+	//////////////////////////////////////////////////////////////////////
125
+	// Error Tag
126
+	//////////////////////////////////////////////////////////////////////
127
+
128
+	// Error indicates that operation represented by the span resulted in an error.
129
+	Error = boolTagName("error")
130
+)
131
+
132
+// ---
133
+
134
+// SpanKindEnum represents common span types
135
+type SpanKindEnum string
136
+
137
+type spanKindTagName string
138
+
139
+// Set adds a string tag to the `span`
140
+func (tag spanKindTagName) Set(span opentracing.Span, value SpanKindEnum) {
141
+	span.SetTag(string(tag), value)
142
+}
143
+
144
+type rpcServerOption struct {
145
+	clientContext opentracing.SpanContext
146
+}
147
+
148
+func (r rpcServerOption) Apply(o *opentracing.StartSpanOptions) {
149
+	if r.clientContext != nil {
150
+		opentracing.ChildOf(r.clientContext).Apply(o)
151
+	}
152
+	SpanKindRPCServer.Apply(o)
153
+}
154
+
155
+// RPCServerOption returns a StartSpanOption appropriate for an RPC server span
156
+// with `client` representing the metadata for the remote peer Span if available.
157
+// In case client == nil, due to the client not being instrumented, this RPC
158
+// server span will be a root span.
159
+func RPCServerOption(client opentracing.SpanContext) opentracing.StartSpanOption {
160
+	return rpcServerOption{client}
161
+}
162
+
163
+// ---
164
+
165
+type stringTagName string
166
+
167
+// Set adds a string tag to the `span`
168
+func (tag stringTagName) Set(span opentracing.Span, value string) {
169
+	span.SetTag(string(tag), value)
170
+}
171
+
172
+// ---
173
+
174
+type uint32TagName string
175
+
176
+// Set adds a uint32 tag to the `span`
177
+func (tag uint32TagName) Set(span opentracing.Span, value uint32) {
178
+	span.SetTag(string(tag), value)
179
+}
180
+
181
+// ---
182
+
183
+type uint16TagName string
184
+
185
+// Set adds a uint16 tag to the `span`
186
+func (tag uint16TagName) Set(span opentracing.Span, value uint16) {
187
+	span.SetTag(string(tag), value)
188
+}
189
+
190
+// ---
191
+
192
+type boolTagName string
193
+
194
+// Add adds a bool tag to the `span`
195
+func (tag boolTagName) Set(span opentracing.Span, value bool) {
196
+	span.SetTag(string(tag), value)
197
+}
198
+
199
+type ipv4Tag string
200
+
201
+// Set adds IP v4 host address of the peer as an uint32 value to the `span`, keep this for backward and zipkin compatibility
202
+func (tag ipv4Tag) Set(span opentracing.Span, value uint32) {
203
+	span.SetTag(string(tag), value)
204
+}
205
+
206
+// SetString records IP v4 host address of the peer as a .-separated tuple to the `span`. E.g., "127.0.0.1"
207
+func (tag ipv4Tag) SetString(span opentracing.Span, value string) {
208
+	span.SetTag(string(tag), value)
209
+}
0 210
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package opentracing
1
+
2
+var (
3
+	globalTracer Tracer = NoopTracer{}
4
+)
5
+
6
+// SetGlobalTracer sets the [singleton] opentracing.Tracer returned by
7
+// GlobalTracer(). Those who use GlobalTracer (rather than directly manage an
8
+// opentracing.Tracer instance) should call SetGlobalTracer as early as
9
+// possible in main(), prior to calling the `StartSpan` global func below.
10
+// Prior to calling `SetGlobalTracer`, any Spans started via the `StartSpan`
11
+// (etc) globals are noops.
12
+func SetGlobalTracer(tracer Tracer) {
13
+	globalTracer = tracer
14
+}
15
+
16
+// GlobalTracer returns the global singleton `Tracer` implementation.
17
+// Before `SetGlobalTracer()` is called, the `GlobalTracer()` is a noop
18
+// implementation that drops all data handed to it.
19
+func GlobalTracer() Tracer {
20
+	return globalTracer
21
+}
22
+
23
+// StartSpan defers to `Tracer.StartSpan`. See `GlobalTracer()`.
24
+func StartSpan(operationName string, opts ...StartSpanOption) Span {
25
+	return globalTracer.StartSpan(operationName, opts...)
26
+}
27
+
28
+// InitGlobalTracer is deprecated. Please use SetGlobalTracer.
29
+func InitGlobalTracer(tracer Tracer) {
30
+	SetGlobalTracer(tracer)
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,57 @@
0
+package opentracing
1
+
2
+import "golang.org/x/net/context"
3
+
4
+type contextKey struct{}
5
+
6
+var activeSpanKey = contextKey{}
7
+
8
+// ContextWithSpan returns a new `context.Context` that holds a reference to
9
+// `span`'s SpanContext.
10
+func ContextWithSpan(ctx context.Context, span Span) context.Context {
11
+	return context.WithValue(ctx, activeSpanKey, span)
12
+}
13
+
14
+// SpanFromContext returns the `Span` previously associated with `ctx`, or
15
+// `nil` if no such `Span` could be found.
16
+//
17
+// NOTE: context.Context != SpanContext: the former is Go's intra-process
18
+// context propagation mechanism, and the latter houses OpenTracing's per-Span
19
+// identity and baggage information.
20
+func SpanFromContext(ctx context.Context) Span {
21
+	val := ctx.Value(activeSpanKey)
22
+	if sp, ok := val.(Span); ok {
23
+		return sp
24
+	}
25
+	return nil
26
+}
27
+
28
+// StartSpanFromContext starts and returns a Span with `operationName`, using
29
+// any Span found within `ctx` as a ChildOfRef. If no such parent could be
30
+// found, StartSpanFromContext creates a root (parentless) Span.
31
+//
32
+// The second return value is a context.Context object built around the
33
+// returned Span.
34
+//
35
+// Example usage:
36
+//
37
+//    SomeFunction(ctx context.Context, ...) {
38
+//        sp, ctx := opentracing.StartSpanFromContext(ctx, "SomeFunction")
39
+//        defer sp.Finish()
40
+//        ...
41
+//    }
42
+func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context) {
43
+	return startSpanFromContextWithTracer(ctx, GlobalTracer(), operationName, opts...)
44
+}
45
+
46
+// startSpanFromContextWithTracer is factored out for testing purposes.
47
+func startSpanFromContextWithTracer(ctx context.Context, tracer Tracer, operationName string, opts ...StartSpanOption) (Span, context.Context) {
48
+	var span Span
49
+	if parentSpan := SpanFromContext(ctx); parentSpan != nil {
50
+		opts = append(opts, ChildOf(parentSpan.Context()))
51
+		span = tracer.StartSpan(operationName, opts...)
52
+	} else {
53
+		span = tracer.StartSpan(operationName, opts...)
54
+	}
55
+	return span, ContextWithSpan(ctx, span)
56
+}
0 57
new file mode 100644
... ...
@@ -0,0 +1,269 @@
0
+package log
1
+
2
+import (
3
+	"fmt"
4
+	"math"
5
+)
6
+
7
+type fieldType int
8
+
9
+const (
10
+	stringType fieldType = iota
11
+	boolType
12
+	intType
13
+	int32Type
14
+	uint32Type
15
+	int64Type
16
+	uint64Type
17
+	float32Type
18
+	float64Type
19
+	errorType
20
+	objectType
21
+	lazyLoggerType
22
+	noopType
23
+)
24
+
25
+// Field instances are constructed via LogBool, LogString, and so on.
26
+// Tracing implementations may then handle them via the Field.Marshal
27
+// method.
28
+//
29
+// "heavily influenced by" (i.e., partially stolen from)
30
+// https://github.com/uber-go/zap
31
+type Field struct {
32
+	key          string
33
+	fieldType    fieldType
34
+	numericVal   int64
35
+	stringVal    string
36
+	interfaceVal interface{}
37
+}
38
+
39
+// String adds a string-valued key:value pair to a Span.LogFields() record
40
+func String(key, val string) Field {
41
+	return Field{
42
+		key:       key,
43
+		fieldType: stringType,
44
+		stringVal: val,
45
+	}
46
+}
47
+
48
+// Bool adds a bool-valued key:value pair to a Span.LogFields() record
49
+func Bool(key string, val bool) Field {
50
+	var numericVal int64
51
+	if val {
52
+		numericVal = 1
53
+	}
54
+	return Field{
55
+		key:        key,
56
+		fieldType:  boolType,
57
+		numericVal: numericVal,
58
+	}
59
+}
60
+
61
+// Int adds an int-valued key:value pair to a Span.LogFields() record
62
+func Int(key string, val int) Field {
63
+	return Field{
64
+		key:        key,
65
+		fieldType:  intType,
66
+		numericVal: int64(val),
67
+	}
68
+}
69
+
70
+// Int32 adds an int32-valued key:value pair to a Span.LogFields() record
71
+func Int32(key string, val int32) Field {
72
+	return Field{
73
+		key:        key,
74
+		fieldType:  int32Type,
75
+		numericVal: int64(val),
76
+	}
77
+}
78
+
79
+// Int64 adds an int64-valued key:value pair to a Span.LogFields() record
80
+func Int64(key string, val int64) Field {
81
+	return Field{
82
+		key:        key,
83
+		fieldType:  int64Type,
84
+		numericVal: val,
85
+	}
86
+}
87
+
88
+// Uint32 adds a uint32-valued key:value pair to a Span.LogFields() record
89
+func Uint32(key string, val uint32) Field {
90
+	return Field{
91
+		key:        key,
92
+		fieldType:  uint32Type,
93
+		numericVal: int64(val),
94
+	}
95
+}
96
+
97
+// Uint64 adds a uint64-valued key:value pair to a Span.LogFields() record
98
+func Uint64(key string, val uint64) Field {
99
+	return Field{
100
+		key:        key,
101
+		fieldType:  uint64Type,
102
+		numericVal: int64(val),
103
+	}
104
+}
105
+
106
+// Float32 adds a float32-valued key:value pair to a Span.LogFields() record
107
+func Float32(key string, val float32) Field {
108
+	return Field{
109
+		key:        key,
110
+		fieldType:  float32Type,
111
+		numericVal: int64(math.Float32bits(val)),
112
+	}
113
+}
114
+
115
+// Float64 adds a float64-valued key:value pair to a Span.LogFields() record
116
+func Float64(key string, val float64) Field {
117
+	return Field{
118
+		key:        key,
119
+		fieldType:  float64Type,
120
+		numericVal: int64(math.Float64bits(val)),
121
+	}
122
+}
123
+
124
+// Error adds an error with the key "error" to a Span.LogFields() record
125
+func Error(err error) Field {
126
+	return Field{
127
+		key:          "error",
128
+		fieldType:    errorType,
129
+		interfaceVal: err,
130
+	}
131
+}
132
+
133
+// Object adds an object-valued key:value pair to a Span.LogFields() record
134
+func Object(key string, obj interface{}) Field {
135
+	return Field{
136
+		key:          key,
137
+		fieldType:    objectType,
138
+		interfaceVal: obj,
139
+	}
140
+}
141
+
142
+// LazyLogger allows for user-defined, late-bound logging of arbitrary data
143
+type LazyLogger func(fv Encoder)
144
+
145
+// Lazy adds a LazyLogger to a Span.LogFields() record; the tracing
146
+// implementation will call the LazyLogger function at an indefinite time in
147
+// the future (after Lazy() returns).
148
+func Lazy(ll LazyLogger) Field {
149
+	return Field{
150
+		fieldType:    lazyLoggerType,
151
+		interfaceVal: ll,
152
+	}
153
+}
154
+
155
+// Noop creates a no-op log field that should be ignored by the tracer.
156
+// It can be used to capture optional fields, for example those that should
157
+// only be logged in non-production environment:
158
+//
159
+//     func customerField(order *Order) log.Field {
160
+//          if os.Getenv("ENVIRONMENT") == "dev" {
161
+//              return log.String("customer", order.Customer.ID)
162
+//          }
163
+//          return log.Noop()
164
+//     }
165
+//
166
+//     span.LogFields(log.String("event", "purchase"), customerField(order))
167
+//
168
+func Noop() Field {
169
+	return Field{
170
+		fieldType: noopType,
171
+	}
172
+}
173
+
174
+// Encoder allows access to the contents of a Field (via a call to
175
+// Field.Marshal).
176
+//
177
+// Tracer implementations typically provide an implementation of Encoder;
178
+// OpenTracing callers typically do not need to concern themselves with it.
179
+type Encoder interface {
180
+	EmitString(key, value string)
181
+	EmitBool(key string, value bool)
182
+	EmitInt(key string, value int)
183
+	EmitInt32(key string, value int32)
184
+	EmitInt64(key string, value int64)
185
+	EmitUint32(key string, value uint32)
186
+	EmitUint64(key string, value uint64)
187
+	EmitFloat32(key string, value float32)
188
+	EmitFloat64(key string, value float64)
189
+	EmitObject(key string, value interface{})
190
+	EmitLazyLogger(value LazyLogger)
191
+}
192
+
193
+// Marshal passes a Field instance through to the appropriate
194
+// field-type-specific method of an Encoder.
195
+func (lf Field) Marshal(visitor Encoder) {
196
+	switch lf.fieldType {
197
+	case stringType:
198
+		visitor.EmitString(lf.key, lf.stringVal)
199
+	case boolType:
200
+		visitor.EmitBool(lf.key, lf.numericVal != 0)
201
+	case intType:
202
+		visitor.EmitInt(lf.key, int(lf.numericVal))
203
+	case int32Type:
204
+		visitor.EmitInt32(lf.key, int32(lf.numericVal))
205
+	case int64Type:
206
+		visitor.EmitInt64(lf.key, int64(lf.numericVal))
207
+	case uint32Type:
208
+		visitor.EmitUint32(lf.key, uint32(lf.numericVal))
209
+	case uint64Type:
210
+		visitor.EmitUint64(lf.key, uint64(lf.numericVal))
211
+	case float32Type:
212
+		visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal)))
213
+	case float64Type:
214
+		visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal)))
215
+	case errorType:
216
+		if err, ok := lf.interfaceVal.(error); ok {
217
+			visitor.EmitString(lf.key, err.Error())
218
+		} else {
219
+			visitor.EmitString(lf.key, "<nil>")
220
+		}
221
+	case objectType:
222
+		visitor.EmitObject(lf.key, lf.interfaceVal)
223
+	case lazyLoggerType:
224
+		visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger))
225
+	case noopType:
226
+		// intentionally left blank
227
+	}
228
+}
229
+
230
+// Key returns the field's key.
231
+func (lf Field) Key() string {
232
+	return lf.key
233
+}
234
+
235
+// Value returns the field's value as interface{}.
236
+func (lf Field) Value() interface{} {
237
+	switch lf.fieldType {
238
+	case stringType:
239
+		return lf.stringVal
240
+	case boolType:
241
+		return lf.numericVal != 0
242
+	case intType:
243
+		return int(lf.numericVal)
244
+	case int32Type:
245
+		return int32(lf.numericVal)
246
+	case int64Type:
247
+		return int64(lf.numericVal)
248
+	case uint32Type:
249
+		return uint32(lf.numericVal)
250
+	case uint64Type:
251
+		return uint64(lf.numericVal)
252
+	case float32Type:
253
+		return math.Float32frombits(uint32(lf.numericVal))
254
+	case float64Type:
255
+		return math.Float64frombits(uint64(lf.numericVal))
256
+	case errorType, objectType, lazyLoggerType:
257
+		return lf.interfaceVal
258
+	case noopType:
259
+		return nil
260
+	default:
261
+		return nil
262
+	}
263
+}
264
+
265
+// String returns a string representation of the key and value.
266
+func (lf Field) String() string {
267
+	return fmt.Sprint(lf.key, ":", lf.Value())
268
+}
0 269
new file mode 100644
... ...
@@ -0,0 +1,54 @@
0
+package log
1
+
2
+import "fmt"
3
+
4
+// InterleavedKVToFields converts keyValues a la Span.LogKV() to a Field slice
5
+// a la Span.LogFields().
6
+func InterleavedKVToFields(keyValues ...interface{}) ([]Field, error) {
7
+	if len(keyValues)%2 != 0 {
8
+		return nil, fmt.Errorf("non-even keyValues len: %d", len(keyValues))
9
+	}
10
+	fields := make([]Field, len(keyValues)/2)
11
+	for i := 0; i*2 < len(keyValues); i++ {
12
+		key, ok := keyValues[i*2].(string)
13
+		if !ok {
14
+			return nil, fmt.Errorf(
15
+				"non-string key (pair #%d): %T",
16
+				i, keyValues[i*2])
17
+		}
18
+		switch typedVal := keyValues[i*2+1].(type) {
19
+		case bool:
20
+			fields[i] = Bool(key, typedVal)
21
+		case string:
22
+			fields[i] = String(key, typedVal)
23
+		case int:
24
+			fields[i] = Int(key, typedVal)
25
+		case int8:
26
+			fields[i] = Int32(key, int32(typedVal))
27
+		case int16:
28
+			fields[i] = Int32(key, int32(typedVal))
29
+		case int32:
30
+			fields[i] = Int32(key, typedVal)
31
+		case int64:
32
+			fields[i] = Int64(key, typedVal)
33
+		case uint:
34
+			fields[i] = Uint64(key, uint64(typedVal))
35
+		case uint64:
36
+			fields[i] = Uint64(key, typedVal)
37
+		case uint8:
38
+			fields[i] = Uint32(key, uint32(typedVal))
39
+		case uint16:
40
+			fields[i] = Uint32(key, uint32(typedVal))
41
+		case uint32:
42
+			fields[i] = Uint32(key, typedVal)
43
+		case float32:
44
+			fields[i] = Float32(key, typedVal)
45
+		case float64:
46
+			fields[i] = Float64(key, typedVal)
47
+		default:
48
+			// When in doubt, coerce to a string
49
+			fields[i] = String(key, fmt.Sprint(typedVal))
50
+		}
51
+	}
52
+	return fields, nil
53
+}
0 54
new file mode 100644
... ...
@@ -0,0 +1,64 @@
0
+package opentracing
1
+
2
+import "github.com/opentracing/opentracing-go/log"
3
+
4
+// A NoopTracer is a trivial, minimum overhead implementation of Tracer
5
+// for which all operations are no-ops.
6
+//
7
+// The primary use of this implementation is in libraries, such as RPC
8
+// frameworks, that make tracing an optional feature controlled by the
9
+// end user. A no-op implementation allows said libraries to use it
10
+// as the default Tracer and to write instrumentation that does
11
+// not need to keep checking if the tracer instance is nil.
12
+//
13
+// For the same reason, the NoopTracer is the default "global" tracer
14
+// (see GlobalTracer and SetGlobalTracer functions).
15
+//
16
+// WARNING: NoopTracer does not support baggage propagation.
17
+type NoopTracer struct{}
18
+
19
+type noopSpan struct{}
20
+type noopSpanContext struct{}
21
+
22
+var (
23
+	defaultNoopSpanContext = noopSpanContext{}
24
+	defaultNoopSpan        = noopSpan{}
25
+	defaultNoopTracer      = NoopTracer{}
26
+)
27
+
28
+const (
29
+	emptyString = ""
30
+)
31
+
32
+// noopSpanContext:
33
+func (n noopSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
34
+
35
+// noopSpan:
36
+func (n noopSpan) Context() SpanContext                                  { return defaultNoopSpanContext }
37
+func (n noopSpan) SetBaggageItem(key, val string) Span                   { return defaultNoopSpan }
38
+func (n noopSpan) BaggageItem(key string) string                         { return emptyString }
39
+func (n noopSpan) SetTag(key string, value interface{}) Span             { return n }
40
+func (n noopSpan) LogFields(fields ...log.Field)                         {}
41
+func (n noopSpan) LogKV(keyVals ...interface{})                          {}
42
+func (n noopSpan) Finish()                                               {}
43
+func (n noopSpan) FinishWithOptions(opts FinishOptions)                  {}
44
+func (n noopSpan) SetOperationName(operationName string) Span            { return n }
45
+func (n noopSpan) Tracer() Tracer                                        { return defaultNoopTracer }
46
+func (n noopSpan) LogEvent(event string)                                 {}
47
+func (n noopSpan) LogEventWithPayload(event string, payload interface{}) {}
48
+func (n noopSpan) Log(data LogData)                                      {}
49
+
50
+// StartSpan belongs to the Tracer interface.
51
+func (n NoopTracer) StartSpan(operationName string, opts ...StartSpanOption) Span {
52
+	return defaultNoopSpan
53
+}
54
+
55
+// Inject belongs to the Tracer interface.
56
+func (n NoopTracer) Inject(sp SpanContext, format interface{}, carrier interface{}) error {
57
+	return nil
58
+}
59
+
60
+// Extract belongs to the Tracer interface.
61
+func (n NoopTracer) Extract(format interface{}, carrier interface{}) (SpanContext, error) {
62
+	return nil, ErrSpanContextNotFound
63
+}
0 64
new file mode 100644
... ...
@@ -0,0 +1,176 @@
0
+package opentracing
1
+
2
+import (
3
+	"errors"
4
+	"net/http"
5
+)
6
+
7
+///////////////////////////////////////////////////////////////////////////////
8
+// CORE PROPAGATION INTERFACES:
9
+///////////////////////////////////////////////////////////////////////////////
10
+
11
+var (
12
+	// ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or
13
+	// Tracer.Extract() is not recognized by the Tracer implementation.
14
+	ErrUnsupportedFormat = errors.New("opentracing: Unknown or unsupported Inject/Extract format")
15
+
16
+	// ErrSpanContextNotFound occurs when the `carrier` passed to
17
+	// Tracer.Extract() is valid and uncorrupted but has insufficient
18
+	// information to extract a SpanContext.
19
+	ErrSpanContextNotFound = errors.New("opentracing: SpanContext not found in Extract carrier")
20
+
21
+	// ErrInvalidSpanContext errors occur when Tracer.Inject() is asked to
22
+	// operate on a SpanContext which it is not prepared to handle (for
23
+	// example, since it was created by a different tracer implementation).
24
+	ErrInvalidSpanContext = errors.New("opentracing: SpanContext type incompatible with tracer")
25
+
26
+	// ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Extract()
27
+	// implementations expect a different type of `carrier` than they are
28
+	// given.
29
+	ErrInvalidCarrier = errors.New("opentracing: Invalid Inject/Extract carrier")
30
+
31
+	// ErrSpanContextCorrupted occurs when the `carrier` passed to
32
+	// Tracer.Extract() is of the expected type but is corrupted.
33
+	ErrSpanContextCorrupted = errors.New("opentracing: SpanContext data corrupted in Extract carrier")
34
+)
35
+
36
+///////////////////////////////////////////////////////////////////////////////
37
+// BUILTIN PROPAGATION FORMATS:
38
+///////////////////////////////////////////////////////////////////////////////
39
+
40
+// BuiltinFormat is used to demarcate the values within package `opentracing`
41
+// that are intended for use with the Tracer.Inject() and Tracer.Extract()
42
+// methods.
43
+type BuiltinFormat byte
44
+
45
+const (
46
+	// Binary represents SpanContexts as opaque binary data.
47
+	//
48
+	// For Tracer.Inject(): the carrier must be an `io.Writer`.
49
+	//
50
+	// For Tracer.Extract(): the carrier must be an `io.Reader`.
51
+	Binary BuiltinFormat = iota
52
+
53
+	// TextMap represents SpanContexts as key:value string pairs.
54
+	//
55
+	// Unlike HTTPHeaders, the TextMap format does not restrict the key or
56
+	// value character sets in any way.
57
+	//
58
+	// For Tracer.Inject(): the carrier must be a `TextMapWriter`.
59
+	//
60
+	// For Tracer.Extract(): the carrier must be a `TextMapReader`.
61
+	TextMap
62
+
63
+	// HTTPHeaders represents SpanContexts as HTTP header string pairs.
64
+	//
65
+	// Unlike TextMap, the HTTPHeaders format requires that the keys and values
66
+	// be valid as HTTP headers as-is (i.e., character casing may be unstable
67
+	// and special characters are disallowed in keys, values should be
68
+	// URL-escaped, etc).
69
+	//
70
+	// For Tracer.Inject(): the carrier must be a `TextMapWriter`.
71
+	//
72
+	// For Tracer.Extract(): the carrier must be a `TextMapReader`.
73
+	//
74
+	// See HTTPHeadersCarrier for an implementation of both TextMapWriter
75
+	// and TextMapReader that defers to an http.Header instance for storage.
76
+	// For example, Inject():
77
+	//
78
+	//    carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
79
+	//    err := span.Tracer().Inject(
80
+	//        span.Context(), opentracing.HTTPHeaders, carrier)
81
+	//
82
+	// Or Extract():
83
+	//
84
+	//    carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
85
+	//    clientContext, err := tracer.Extract(
86
+	//        opentracing.HTTPHeaders, carrier)
87
+	//
88
+	HTTPHeaders
89
+)
90
+
91
+// TextMapWriter is the Inject() carrier for the TextMap builtin format. With
92
+// it, the caller can encode a SpanContext for propagation as entries in a map
93
+// of unicode strings.
94
+type TextMapWriter interface {
95
+	// Set a key:value pair to the carrier. Multiple calls to Set() for the
96
+	// same key leads to undefined behavior.
97
+	//
98
+	// NOTE: The backing store for the TextMapWriter may contain data unrelated
99
+	// to SpanContext. As such, Inject() and Extract() implementations that
100
+	// call the TextMapWriter and TextMapReader interfaces must agree on a
101
+	// prefix or other convention to distinguish their own key:value pairs.
102
+	Set(key, val string)
103
+}
104
+
105
+// TextMapReader is the Extract() carrier for the TextMap builtin format. With it,
106
+// the caller can decode a propagated SpanContext as entries in a map of
107
+// unicode strings.
108
+type TextMapReader interface {
109
+	// ForeachKey returns TextMap contents via repeated calls to the `handler`
110
+	// function. If any call to `handler` returns a non-nil error, ForeachKey
111
+	// terminates and returns that error.
112
+	//
113
+	// NOTE: The backing store for the TextMapReader may contain data unrelated
114
+	// to SpanContext. As such, Inject() and Extract() implementations that
115
+	// call the TextMapWriter and TextMapReader interfaces must agree on a
116
+	// prefix or other convention to distinguish their own key:value pairs.
117
+	//
118
+	// The "foreach" callback pattern reduces unnecessary copying in some cases
119
+	// and also allows implementations to hold locks while the map is read.
120
+	ForeachKey(handler func(key, val string) error) error
121
+}
122
+
123
+// TextMapCarrier allows the use of regular map[string]string
124
+// as both TextMapWriter and TextMapReader.
125
+type TextMapCarrier map[string]string
126
+
127
+// ForeachKey conforms to the TextMapReader interface.
128
+func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error {
129
+	for k, v := range c {
130
+		if err := handler(k, v); err != nil {
131
+			return err
132
+		}
133
+	}
134
+	return nil
135
+}
136
+
137
+// Set implements Set() of opentracing.TextMapWriter
138
+func (c TextMapCarrier) Set(key, val string) {
139
+	c[key] = val
140
+}
141
+
142
+// HTTPHeadersCarrier satisfies both TextMapWriter and TextMapReader.
143
+//
144
+// Example usage for server side:
145
+//
146
+//     carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
147
+//     clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier)
148
+//
149
+// Example usage for client side:
150
+//
151
+//     carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
152
+//     err := tracer.Inject(
153
+//         span.Context(),
154
+//         opentracing.HTTPHeaders,
155
+//         carrier)
156
+//
157
+type HTTPHeadersCarrier http.Header
158
+
159
+// Set conforms to the TextMapWriter interface.
160
+func (c HTTPHeadersCarrier) Set(key, val string) {
161
+	h := http.Header(c)
162
+	h.Add(key, val)
163
+}
164
+
165
+// ForeachKey conforms to the TextMapReader interface.
166
+func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
167
+	for k, vals := range c {
168
+		for _, v := range vals {
169
+			if err := handler(k, v); err != nil {
170
+				return err
171
+			}
172
+		}
173
+	}
174
+	return nil
175
+}
0 176
new file mode 100644
... ...
@@ -0,0 +1,185 @@
0
+package opentracing
1
+
2
+import (
3
+	"time"
4
+
5
+	"github.com/opentracing/opentracing-go/log"
6
+)
7
+
8
+// SpanContext represents Span state that must propagate to descendant Spans and across process
9
+// boundaries (e.g., a <trace_id, span_id, sampled> tuple).
10
+type SpanContext interface {
11
+	// ForeachBaggageItem grants access to all baggage items stored in the
12
+	// SpanContext.
13
+	// The handler function will be called for each baggage key/value pair.
14
+	// The ordering of items is not guaranteed.
15
+	//
16
+	// The bool return value indicates if the handler wants to continue iterating
17
+	// through the rest of the baggage items; for example if the handler is trying to
18
+	// find some baggage item by pattern matching the name, it can return false
19
+	// as soon as the item is found to stop further iterations.
20
+	ForeachBaggageItem(handler func(k, v string) bool)
21
+}
22
+
23
+// Span represents an active, un-finished span in the OpenTracing system.
24
+//
25
+// Spans are created by the Tracer interface.
26
+type Span interface {
27
+	// Sets the end timestamp and finalizes Span state.
28
+	//
29
+	// With the exception of calls to Context() (which are always allowed),
30
+	// Finish() must be the last call made to any span instance, and to do
31
+	// otherwise leads to undefined behavior.
32
+	Finish()
33
+	// FinishWithOptions is like Finish() but with explicit control over
34
+	// timestamps and log data.
35
+	FinishWithOptions(opts FinishOptions)
36
+
37
+	// Context() yields the SpanContext for this Span. Note that the return
38
+	// value of Context() is still valid after a call to Span.Finish(), as is
39
+	// a call to Span.Context() after a call to Span.Finish().
40
+	Context() SpanContext
41
+
42
+	// Sets or changes the operation name.
43
+	SetOperationName(operationName string) Span
44
+
45
+	// Adds a tag to the span.
46
+	//
47
+	// If there is a pre-existing tag set for `key`, it is overwritten.
48
+	//
49
+	// Tag values can be numeric types, strings, or bools. The behavior of
50
+	// other tag value types is undefined at the OpenTracing level. If a
51
+	// tracing system does not know how to handle a particular value type, it
52
+	// may ignore the tag, but shall not panic.
53
+	SetTag(key string, value interface{}) Span
54
+
55
+	// LogFields is an efficient and type-checked way to record key:value
56
+	// logging data about a Span, though the programming interface is a little
57
+	// more verbose than LogKV(). Here's an example:
58
+	//
59
+	//    span.LogFields(
60
+	//        log.String("event", "soft error"),
61
+	//        log.String("type", "cache timeout"),
62
+	//        log.Int("waited.millis", 1500))
63
+	//
64
+	// Also see Span.FinishWithOptions() and FinishOptions.BulkLogData.
65
+	LogFields(fields ...log.Field)
66
+
67
+	// LogKV is a concise, readable way to record key:value logging data about
68
+	// a Span, though unfortunately this also makes it less efficient and less
69
+	// type-safe than LogFields(). Here's an example:
70
+	//
71
+	//    span.LogKV(
72
+	//        "event", "soft error",
73
+	//        "type", "cache timeout",
74
+	//        "waited.millis", 1500)
75
+	//
76
+	// For LogKV (as opposed to LogFields()), the parameters must appear as
77
+	// key-value pairs, like
78
+	//
79
+	//    span.LogKV(key1, val1, key2, val2, key3, val3, ...)
80
+	//
81
+	// The keys must all be strings. The values may be strings, numeric types,
82
+	// bools, Go error instances, or arbitrary structs.
83
+	//
84
+	// (Note to implementors: consider the log.InterleavedKVToFields() helper)
85
+	LogKV(alternatingKeyValues ...interface{})
86
+
87
+	// SetBaggageItem sets a key:value pair on this Span and its SpanContext
88
+	// that also propagates to descendants of this Span.
89
+	//
90
+	// SetBaggageItem() enables powerful functionality given a full-stack
91
+	// opentracing integration (e.g., arbitrary application data from a mobile
92
+	// app can make it, transparently, all the way into the depths of a storage
93
+	// system), and with it some powerful costs: use this feature with care.
94
+	//
95
+	// IMPORTANT NOTE #1: SetBaggageItem() will only propagate baggage items to
96
+	// *future* causal descendants of the associated Span.
97
+	//
98
+	// IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and
99
+	// value is copied into every local *and remote* child of the associated
100
+	// Span, and that can add up to a lot of network and cpu overhead.
101
+	//
102
+	// Returns a reference to this Span for chaining.
103
+	SetBaggageItem(restrictedKey, value string) Span
104
+
105
+	// Gets the value for a baggage item given its key. Returns the empty string
106
+	// if the value isn't found in this Span.
107
+	BaggageItem(restrictedKey string) string
108
+
109
+	// Provides access to the Tracer that created this Span.
110
+	Tracer() Tracer
111
+
112
+	// Deprecated: use LogFields or LogKV
113
+	LogEvent(event string)
114
+	// Deprecated: use LogFields or LogKV
115
+	LogEventWithPayload(event string, payload interface{})
116
+	// Deprecated: use LogFields or LogKV
117
+	Log(data LogData)
118
+}
119
+
120
+// LogRecord is data associated with a single Span log. Every LogRecord
121
+// instance must specify at least one Field.
122
+type LogRecord struct {
123
+	Timestamp time.Time
124
+	Fields    []log.Field
125
+}
126
+
127
+// FinishOptions allows Span.FinishWithOptions callers to override the finish
128
+// timestamp and provide log data via a bulk interface.
129
+type FinishOptions struct {
130
+	// FinishTime overrides the Span's finish time, or implicitly becomes
131
+	// time.Now() if FinishTime.IsZero().
132
+	//
133
+	// FinishTime must resolve to a timestamp that's >= the Span's StartTime
134
+	// (per StartSpanOptions).
135
+	FinishTime time.Time
136
+
137
+	// LogRecords allows the caller to specify the contents of many LogFields()
138
+	// calls with a single slice. May be nil.
139
+	//
140
+	// None of the LogRecord.Timestamp values may be .IsZero() (i.e., they must
141
+	// be set explicitly). Also, they must be >= the Span's start timestamp and
142
+	// <= the FinishTime (or time.Now() if FinishTime.IsZero()). Otherwise the
143
+	// behavior of FinishWithOptions() is undefined.
144
+	//
145
+	// If specified, the caller hands off ownership of LogRecords at
146
+	// FinishWithOptions() invocation time.
147
+	//
148
+	// If specified, the (deprecated) BulkLogData must be nil or empty.
149
+	LogRecords []LogRecord
150
+
151
+	// BulkLogData is DEPRECATED.
152
+	BulkLogData []LogData
153
+}
154
+
155
+// LogData is DEPRECATED
156
+type LogData struct {
157
+	Timestamp time.Time
158
+	Event     string
159
+	Payload   interface{}
160
+}
161
+
162
+// ToLogRecord converts a deprecated LogData to a non-deprecated LogRecord
163
+func (ld *LogData) ToLogRecord() LogRecord {
164
+	var literalTimestamp time.Time
165
+	if ld.Timestamp.IsZero() {
166
+		literalTimestamp = time.Now()
167
+	} else {
168
+		literalTimestamp = ld.Timestamp
169
+	}
170
+	rval := LogRecord{
171
+		Timestamp: literalTimestamp,
172
+	}
173
+	if ld.Payload == nil {
174
+		rval.Fields = []log.Field{
175
+			log.String("event", ld.Event),
176
+		}
177
+	} else {
178
+		rval.Fields = []log.Field{
179
+			log.String("event", ld.Event),
180
+			log.Object("payload", ld.Payload),
181
+		}
182
+	}
183
+	return rval
184
+}
0 185
new file mode 100644
... ...
@@ -0,0 +1,305 @@
0
+package opentracing
1
+
2
+import "time"
3
+
4
+// Tracer is a simple, thin interface for Span creation and SpanContext
5
+// propagation.
6
+type Tracer interface {
7
+
8
+	// Create, start, and return a new Span with the given `operationName` and
9
+	// incorporate the given StartSpanOption `opts`. (Note that `opts` borrows
10
+	// from the "functional options" pattern, per
11
+	// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
12
+	//
13
+	// A Span with no SpanReference options (e.g., opentracing.ChildOf() or
14
+	// opentracing.FollowsFrom()) becomes the root of its own trace.
15
+	//
16
+	// Examples:
17
+	//
18
+	//     var tracer opentracing.Tracer = ...
19
+	//
20
+	//     // The root-span case:
21
+	//     sp := tracer.StartSpan("GetFeed")
22
+	//
23
+	//     // The vanilla child span case:
24
+	//     sp := tracer.StartSpan(
25
+	//         "GetFeed",
26
+	//         opentracing.ChildOf(parentSpan.Context()))
27
+	//
28
+	//     // All the bells and whistles:
29
+	//     sp := tracer.StartSpan(
30
+	//         "GetFeed",
31
+	//         opentracing.ChildOf(parentSpan.Context()),
32
+	//         opentracing.Tag{"user_agent", loggedReq.UserAgent},
33
+	//         opentracing.StartTime(loggedReq.Timestamp),
34
+	//     )
35
+	//
36
+	StartSpan(operationName string, opts ...StartSpanOption) Span
37
+
38
+	// Inject() takes the `sm` SpanContext instance and injects it for
39
+	// propagation within `carrier`. The actual type of `carrier` depends on
40
+	// the value of `format`.
41
+	//
42
+	// OpenTracing defines a common set of `format` values (see BuiltinFormat),
43
+	// and each has an expected carrier type.
44
+	//
45
+	// Other packages may declare their own `format` values, much like the keys
46
+	// used by `context.Context` (see
47
+	// https://godoc.org/golang.org/x/net/context#WithValue).
48
+	//
49
+	// Example usage (sans error handling):
50
+	//
51
+	//     carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
52
+	//     err := tracer.Inject(
53
+	//         span.Context(),
54
+	//         opentracing.HTTPHeaders,
55
+	//         carrier)
56
+	//
57
+	// NOTE: All opentracing.Tracer implementations MUST support all
58
+	// BuiltinFormats.
59
+	//
60
+	// Implementations may return opentracing.ErrUnsupportedFormat if `format`
61
+	// is not supported by (or not known by) the implementation.
62
+	//
63
+	// Implementations may return opentracing.ErrInvalidCarrier or any other
64
+	// implementation-specific error if the format is supported but injection
65
+	// fails anyway.
66
+	//
67
+	// See Tracer.Extract().
68
+	Inject(sm SpanContext, format interface{}, carrier interface{}) error
69
+
70
+	// Extract() returns a SpanContext instance given `format` and `carrier`.
71
+	//
72
+	// OpenTracing defines a common set of `format` values (see BuiltinFormat),
73
+	// and each has an expected carrier type.
74
+	//
75
+	// Other packages may declare their own `format` values, much like the keys
76
+	// used by `context.Context` (see
77
+	// https://godoc.org/golang.org/x/net/context#WithValue).
78
+	//
79
+	// Example usage (with StartSpan):
80
+	//
81
+	//
82
+	//     carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
83
+	//     clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier)
84
+	//
85
+	//     // ... assuming the ultimate goal here is to resume the trace with a
86
+	//     // server-side Span:
87
+	//     var serverSpan opentracing.Span
88
+	//     if err == nil {
89
+	//         span = tracer.StartSpan(
90
+	//             rpcMethodName, ext.RPCServerOption(clientContext))
91
+	//     } else {
92
+	//         span = tracer.StartSpan(rpcMethodName)
93
+	//     }
94
+	//
95
+	//
96
+	// NOTE: All opentracing.Tracer implementations MUST support all
97
+	// BuiltinFormats.
98
+	//
99
+	// Return values:
100
+	//  - A successful Extract returns a SpanContext instance and a nil error
101
+	//  - If there was simply no SpanContext to extract in `carrier`, Extract()
102
+	//    returns (nil, opentracing.ErrSpanContextNotFound)
103
+	//  - If `format` is unsupported or unrecognized, Extract() returns (nil,
104
+	//    opentracing.ErrUnsupportedFormat)
105
+	//  - If there are more fundamental problems with the `carrier` object,
106
+	//    Extract() may return opentracing.ErrInvalidCarrier,
107
+	//    opentracing.ErrSpanContextCorrupted, or implementation-specific
108
+	//    errors.
109
+	//
110
+	// See Tracer.Inject().
111
+	Extract(format interface{}, carrier interface{}) (SpanContext, error)
112
+}
113
+
114
+// StartSpanOptions allows Tracer.StartSpan() callers and implementors a
115
+// mechanism to override the start timestamp, specify Span References, and make
116
+// a single Tag or multiple Tags available at Span start time.
117
+//
118
+// StartSpan() callers should look at the StartSpanOption interface and
119
+// implementations available in this package.
120
+//
121
+// Tracer implementations can convert a slice of `StartSpanOption` instances
122
+// into a `StartSpanOptions` struct like so:
123
+//
124
+//     func StartSpan(opName string, opts ...opentracing.StartSpanOption) {
125
+//         sso := opentracing.StartSpanOptions{}
126
+//         for _, o := range opts {
127
+//             o.Apply(&sso)
128
+//         }
129
+//         ...
130
+//     }
131
+//
132
+type StartSpanOptions struct {
133
+	// Zero or more causal references to other Spans (via their SpanContext).
134
+	// If empty, start a "root" Span (i.e., start a new trace).
135
+	References []SpanReference
136
+
137
+	// StartTime overrides the Span's start time, or implicitly becomes
138
+	// time.Now() if StartTime.IsZero().
139
+	StartTime time.Time
140
+
141
+	// Tags may have zero or more entries; the restrictions on map values are
142
+	// identical to those for Span.SetTag(). May be nil.
143
+	//
144
+	// If specified, the caller hands off ownership of Tags at
145
+	// StartSpan() invocation time.
146
+	Tags map[string]interface{}
147
+}
148
+
149
+// StartSpanOption instances (zero or more) may be passed to Tracer.StartSpan.
150
+//
151
+// StartSpanOption borrows from the "functional options" pattern, per
152
+// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
153
+type StartSpanOption interface {
154
+	Apply(*StartSpanOptions)
155
+}
156
+
157
+// SpanReferenceType is an enum type describing different categories of
158
+// relationships between two Spans. If Span-2 refers to Span-1, the
159
+// SpanReferenceType describes Span-1 from Span-2's perspective. For example,
160
+// ChildOfRef means that Span-1 created Span-2.
161
+//
162
+// NOTE: Span-1 and Span-2 do *not* necessarily depend on each other for
163
+// completion; e.g., Span-2 may be part of a background job enqueued by Span-1,
164
+// or Span-2 may be sitting in a distributed queue behind Span-1.
165
+type SpanReferenceType int
166
+
167
+const (
168
+	// ChildOfRef refers to a parent Span that caused *and* somehow depends
169
+	// upon the new child Span. Often (but not always), the parent Span cannot
170
+	// finish until the child Span does.
171
+	//
172
+	// An timing diagram for a ChildOfRef that's blocked on the new Span:
173
+	//
174
+	//     [-Parent Span---------]
175
+	//          [-Child Span----]
176
+	//
177
+	// See http://opentracing.io/spec/
178
+	//
179
+	// See opentracing.ChildOf()
180
+	ChildOfRef SpanReferenceType = iota
181
+
182
+	// FollowsFromRef refers to a parent Span that does not depend in any way
183
+	// on the result of the new child Span. For instance, one might use
184
+	// FollowsFromRefs to describe pipeline stages separated by queues,
185
+	// or a fire-and-forget cache insert at the tail end of a web request.
186
+	//
187
+	// A FollowsFromRef Span is part of the same logical trace as the new Span:
188
+	// i.e., the new Span is somehow caused by the work of its FollowsFromRef.
189
+	//
190
+	// All of the following could be valid timing diagrams for children that
191
+	// "FollowFrom" a parent.
192
+	//
193
+	//     [-Parent Span-]  [-Child Span-]
194
+	//
195
+	//
196
+	//     [-Parent Span--]
197
+	//      [-Child Span-]
198
+	//
199
+	//
200
+	//     [-Parent Span-]
201
+	//                 [-Child Span-]
202
+	//
203
+	// See http://opentracing.io/spec/
204
+	//
205
+	// See opentracing.FollowsFrom()
206
+	FollowsFromRef
207
+)
208
+
209
+// SpanReference is a StartSpanOption that pairs a SpanReferenceType and a
210
+// referenced SpanContext. See the SpanReferenceType documentation for
211
+// supported relationships.  If SpanReference is created with
212
+// ReferencedContext==nil, it has no effect. Thus it allows for a more concise
213
+// syntax for starting spans:
214
+//
215
+//     sc, _ := tracer.Extract(someFormat, someCarrier)
216
+//     span := tracer.StartSpan("operation", opentracing.ChildOf(sc))
217
+//
218
+// The `ChildOf(sc)` option above will not panic if sc == nil, it will just
219
+// not add the parent span reference to the options.
220
+type SpanReference struct {
221
+	Type              SpanReferenceType
222
+	ReferencedContext SpanContext
223
+}
224
+
225
+// Apply satisfies the StartSpanOption interface.
226
+func (r SpanReference) Apply(o *StartSpanOptions) {
227
+	if r.ReferencedContext != nil {
228
+		o.References = append(o.References, r)
229
+	}
230
+}
231
+
232
+// ChildOf returns a StartSpanOption pointing to a dependent parent span.
233
+// If sc == nil, the option has no effect.
234
+//
235
+// See ChildOfRef, SpanReference
236
+func ChildOf(sc SpanContext) SpanReference {
237
+	return SpanReference{
238
+		Type:              ChildOfRef,
239
+		ReferencedContext: sc,
240
+	}
241
+}
242
+
243
+// FollowsFrom returns a StartSpanOption pointing to a parent Span that caused
244
+// the child Span but does not directly depend on its result in any way.
245
+// If sc == nil, the option has no effect.
246
+//
247
+// See FollowsFromRef, SpanReference
248
+func FollowsFrom(sc SpanContext) SpanReference {
249
+	return SpanReference{
250
+		Type:              FollowsFromRef,
251
+		ReferencedContext: sc,
252
+	}
253
+}
254
+
255
+// StartTime is a StartSpanOption that sets an explicit start timestamp for the
256
+// new Span.
257
+type StartTime time.Time
258
+
259
+// Apply satisfies the StartSpanOption interface.
260
+func (t StartTime) Apply(o *StartSpanOptions) {
261
+	o.StartTime = time.Time(t)
262
+}
263
+
264
+// Tags are a generic map from an arbitrary string key to an opaque value type.
265
+// The underlying tracing system is responsible for interpreting and
266
+// serializing the values.
267
+type Tags map[string]interface{}
268
+
269
+// Apply satisfies the StartSpanOption interface.
270
+func (t Tags) Apply(o *StartSpanOptions) {
271
+	if o.Tags == nil {
272
+		o.Tags = make(map[string]interface{})
273
+	}
274
+	for k, v := range t {
275
+		o.Tags[k] = v
276
+	}
277
+}
278
+
279
+// Tag may be passed as a StartSpanOption to add a tag to new spans,
280
+// or its Set method may be used to apply the tag to an existing Span,
281
+// for example:
282
+//
283
+// tracer.StartSpan("opName", Tag{"Key", value})
284
+//
285
+//   or
286
+//
287
+// Tag{"key", value}.Set(span)
288
+type Tag struct {
289
+	Key   string
290
+	Value interface{}
291
+}
292
+
293
+// Apply satisfies the StartSpanOption interface.
294
+func (t Tag) Apply(o *StartSpanOptions) {
295
+	if o.Tags == nil {
296
+		o.Tags = make(map[string]interface{})
297
+	}
298
+	o.Tags[t.Key] = t.Value
299
+}
300
+
301
+// Set applies the tag to an existing Span.
302
+func (t Tag) Set(s Span) {
303
+	s.SetTag(t.Key, t.Value)
304
+}