From 14793a82b74ac366dce49204fe1a7c70ab311611 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Wed, 14 Oct 2020 10:17:13 -0700 Subject: [PATCH] Allow replacing trace SDK --- go.sum | 2 + plugin/ochttp/client.go | 4 +- plugin/ochttp/server.go | 2 +- plugin/ochttp/server_test.go | 2 +- plugin/ochttp/span_annotating_client_trace.go | 6 +- plugin/ochttp/trace.go | 4 +- plugin/ochttp/trace_test.go | 2 +- trace/basetypes.go | 10 ++ trace/examples_test.go | 2 +- trace/spanstore.go | 14 +- trace/trace.go | 85 ++++++----- trace/trace_api.go | 137 ++++++++++++++++++ trace/trace_test.go | 53 +++---- 13 files changed, 243 insertions(+), 80 deletions(-) create mode 100644 trace/trace_api.go diff --git a/go.sum b/go.sum index 01c02972c..42941590d 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/plugin/ochttp/client.go b/plugin/ochttp/client.go index da815b2a7..791de857b 100644 --- a/plugin/ochttp/client.go +++ b/plugin/ochttp/client.go @@ -56,10 +56,10 @@ type Transport struct { // name equals the URL Path. FormatSpanName func(*http.Request) string - // NewClientTrace may be set to a function allowing the current *trace.Span + // NewClientTrace may be set to a function allowing the current trace.Span // to be annotated with HTTP request event information emitted by the // httptrace package. - NewClientTrace func(*http.Request, *trace.Span) *httptrace.ClientTrace + NewClientTrace func(*http.Request, trace.Span) *httptrace.ClientTrace // TODO: Implement tag propagation for HTTP. } diff --git a/plugin/ochttp/server.go b/plugin/ochttp/server.go index c7ea64235..69b357c2e 100644 --- a/plugin/ochttp/server.go +++ b/plugin/ochttp/server.go @@ -109,7 +109,7 @@ func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Requ startOpts = h.GetStartOptions(r) } - var span *trace.Span + var span trace.Span sc, ok := h.extractSpanContext(r) if ok && !h.IsPublicEndpoint { ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc, diff --git a/plugin/ochttp/server_test.go b/plugin/ochttp/server_test.go index 6e4ed5fbb..69c6a706b 100644 --- a/plugin/ochttp/server_test.go +++ b/plugin/ochttp/server_test.go @@ -584,7 +584,7 @@ func TestIgnoreHealthEndpoints(t *testing.T) { ts := httptest.NewServer(&Handler{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { span := trace.FromContext(r.Context()) - if span != nil { + if span.IsRecordingEvents() { spans++ } fmt.Fprint(w, "ok") diff --git a/plugin/ochttp/span_annotating_client_trace.go b/plugin/ochttp/span_annotating_client_trace.go index 05c6c56cc..be29c64ca 100644 --- a/plugin/ochttp/span_annotating_client_trace.go +++ b/plugin/ochttp/span_annotating_client_trace.go @@ -24,7 +24,7 @@ import ( ) type spanAnnotator struct { - sp *trace.Span + sp trace.Span } // TODO: Remove NewSpanAnnotator at the next release. @@ -32,13 +32,13 @@ type spanAnnotator struct { // NewSpanAnnotator returns a httptrace.ClientTrace which annotates // all emitted httptrace events on the provided Span. // Deprecated: Use NewSpanAnnotatingClientTrace instead -func NewSpanAnnotator(r *http.Request, s *trace.Span) *httptrace.ClientTrace { +func NewSpanAnnotator(r *http.Request, s trace.Span) *httptrace.ClientTrace { return NewSpanAnnotatingClientTrace(r, s) } // NewSpanAnnotatingClientTrace returns a httptrace.ClientTrace which annotates // all emitted httptrace events on the provided Span. -func NewSpanAnnotatingClientTrace(_ *http.Request, s *trace.Span) *httptrace.ClientTrace { +func NewSpanAnnotatingClientTrace(_ *http.Request, s trace.Span) *httptrace.ClientTrace { sa := spanAnnotator{sp: s} return &httptrace.ClientTrace{ diff --git a/plugin/ochttp/trace.go b/plugin/ochttp/trace.go index ed3a5db56..43f979994 100644 --- a/plugin/ochttp/trace.go +++ b/plugin/ochttp/trace.go @@ -44,7 +44,7 @@ type traceTransport struct { startOptions trace.StartOptions format propagation.HTTPFormat formatSpanName func(*http.Request) string - newClientTrace func(*http.Request, *trace.Span) *httptrace.ClientTrace + newClientTrace func(*http.Request, trace.Span) *httptrace.ClientTrace } // TODO(jbd): Add message events for request and response size. @@ -104,7 +104,7 @@ func (t *traceTransport) RoundTrip(req *http.Request) (*http.Response, error) { // the body of the original response. type bodyTracker struct { rc io.ReadCloser - span *trace.Span + span trace.Span } var _ io.ReadCloser = (*bodyTracker)(nil) diff --git a/plugin/ochttp/trace_test.go b/plugin/ochttp/trace_test.go index 615c271eb..afb93e659 100644 --- a/plugin/ochttp/trace_test.go +++ b/plugin/ochttp/trace_test.go @@ -106,7 +106,7 @@ func TestTransport_RoundTrip(t *testing.T) { _, parent := trace.StartSpan(context.Background(), "parent") tests := []struct { name string - parent *trace.Span + parent trace.Span }{ { name: "no parent", diff --git a/trace/basetypes.go b/trace/basetypes.go index 0c54492a2..c8e26ed63 100644 --- a/trace/basetypes.go +++ b/trace/basetypes.go @@ -49,6 +49,16 @@ type Attribute struct { value interface{} } +// Key returns the attribute's key +func (a *Attribute) Key() string { + return a.key +} + +// Value returns the attribute's value +func (a *Attribute) Value() interface{} { + return a.value +} + // BoolAttribute returns a bool-valued attribute. func BoolAttribute(key string, value bool) Attribute { return Attribute{key: key, value: value} diff --git a/trace/examples_test.go b/trace/examples_test.go index 286990cca..6981ebf30 100644 --- a/trace/examples_test.go +++ b/trace/examples_test.go @@ -21,7 +21,7 @@ import ( "go.opencensus.io/trace" ) -// This example shows how to use StartSpan and (*Span).End to capture +// This example shows how to use StartSpan and (Span).End to capture // a function execution in a Span. It assumes that the function // has a context.Context argument. func ExampleStartSpan() { diff --git a/trace/spanstore.go b/trace/spanstore.go index c442d9902..b61c1b7c8 100644 --- a/trace/spanstore.go +++ b/trace/spanstore.go @@ -48,8 +48,10 @@ func (i internalOnly) ReportActiveSpans(name string) []*SpanData { var out []*SpanData s.mu.Lock() defer s.mu.Unlock() - for span := range s.active { - out = append(out, span.makeSpanData()) + for activeSpan := range s.active { + if s, ok := activeSpan.(*span); ok { + out = append(out, s.makeSpanData()) + } } return out } @@ -185,7 +187,7 @@ func (i internalOnly) ReportSpansByLatency(name string, minLatency, maxLatency t // bucketed by latency. type spanStore struct { mu sync.Mutex // protects everything below. - active map[*Span]struct{} + active map[Span]struct{} errors map[int32]*bucket latency []bucket maxSpansPerErrorBucket int @@ -194,7 +196,7 @@ type spanStore struct { // newSpanStore creates a span store. func newSpanStore(name string, latencyBucketSize int, errorBucketSize int) *spanStore { s := &spanStore{ - active: make(map[*Span]struct{}), + active: make(map[Span]struct{}), latency: make([]bucket, len(defaultLatencies)+1), maxSpansPerErrorBucket: errorBucketSize, } @@ -271,7 +273,7 @@ func (s *spanStore) resize(latencyBucketSize int, errorBucketSize int) { } // add adds a span to the active bucket of the spanStore. -func (s *spanStore) add(span *Span) { +func (s *spanStore) add(span Span) { s.mu.Lock() s.active[span] = struct{}{} s.mu.Unlock() @@ -279,7 +281,7 @@ func (s *spanStore) add(span *Span) { // finished removes a span from the active set, and adds a corresponding // SpanData to a latency or error bucket. -func (s *spanStore) finished(span *Span, sd *SpanData) { +func (s *spanStore) finished(span Span, sd *SpanData) { latency := sd.EndTime.Sub(sd.StartTime) if latency < 0 { latency = 0 diff --git a/trace/trace.go b/trace/trace.go index daf895596..f3791a7fa 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -28,12 +28,18 @@ import ( "go.opencensus.io/trace/tracestate" ) +type tracer struct{} + +var _ Span = &span{} + +var _ Tracer = &tracer{} + // Span represents a span of a trace. It has an associated SpanContext, and // stores data accumulated while the span is active. // // Ideally users should interact with Spans by calling the functions in this // package that take a Context parameter. -type Span struct { +type span struct { // data contains information recorded about the span. // // It will be non-nil if we are exporting the span or recording events for it. @@ -66,7 +72,7 @@ type Span struct { // IsRecordingEvents returns true if events are being recorded for this span. // Use this check to avoid computing expensive annotations when they will never // be used. -func (s *Span) IsRecordingEvents() bool { +func (s *span) IsRecordingEvents() bool { if s == nil { return false } @@ -108,14 +114,15 @@ type SpanContext struct { type contextKey struct{} -// FromContext returns the Span stored in a context, or nil if there isn't one. -func FromContext(ctx context.Context) *Span { - s, _ := ctx.Value(contextKey{}).(*Span) +// FromContext returns the *span stored in a context, or nil if there isn't one. +func (t *tracer) FromContext(ctx context.Context) Span { + s, _ := ctx.Value(contextKey{}).(*span) return s } -// NewContext returns a new context with the given Span attached. -func NewContext(parent context.Context, s *Span) context.Context { +// NewContext returns a new context with the given Span attached. This tracer +// accepts only the *span implementation of Span. +func (t *tracer) NewContext(parent context.Context, s Span) context.Context { return context.WithValue(parent, contextKey{}, s) } @@ -166,12 +173,14 @@ func WithSampler(sampler Sampler) StartOption { // // Returned context contains the newly created span. You can use it to // propagate the returned span in process. -func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, *Span) { +func (t *tracer) StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, Span) { var opts StartOptions var parent SpanContext - if p := FromContext(ctx); p != nil { - p.addChild() - parent = p.spanContext + if p := t.FromContext(ctx); p != nil { + if internalParent, ok := p.(*span); ok { + internalParent.addChild() + } + parent = p.SpanContext() } for _, op := range o { op(&opts) @@ -180,7 +189,7 @@ func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Cont ctx, end := startExecutionTracerTask(ctx, name) span.executionTracerTaskEnd = end - return NewContext(ctx, span), span + return t.NewContext(ctx, span), span } // StartSpanWithRemoteParent starts a new child span of the span from the given parent. @@ -190,7 +199,7 @@ func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Cont // // Returned context contains the newly created span. You can use it to // propagate the returned span in process. -func StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, *Span) { +func (t *tracer) StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, Span) { var opts StartOptions for _, op := range o { op(&opts) @@ -198,11 +207,11 @@ func StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanCont span := startSpanInternal(name, parent != SpanContext{}, parent, true, opts) ctx, end := startExecutionTracerTask(ctx, name) span.executionTracerTaskEnd = end - return NewContext(ctx, span), span + return t.NewContext(ctx, span), span } -func startSpanInternal(name string, hasParent bool, parent SpanContext, remoteParent bool, o StartOptions) *Span { - span := &Span{} +func startSpanInternal(name string, hasParent bool, parent SpanContext, remoteParent bool, o StartOptions) *span { + span := &span{} span.spanContext = parent cfg := config.Load().(*Config) @@ -266,7 +275,7 @@ func startSpanInternal(name string, hasParent bool, parent SpanContext, remotePa } // End ends the span. -func (s *Span) End() { +func (s *span) End() { if s == nil { return } @@ -296,9 +305,10 @@ func (s *Span) End() { // makeSpanData produces a SpanData representing the current state of the Span. // It requires that s.data is non-nil. -func (s *Span) makeSpanData() *SpanData { +func (s *span) makeSpanData() *SpanData { var sd SpanData s.mu.Lock() + defer s.mu.Unlock() sd = *s.data if s.lruAttributes.len() > 0 { sd.Attributes = s.lruAttributesToAttributeMap() @@ -316,12 +326,11 @@ func (s *Span) makeSpanData() *SpanData { sd.Links = s.interfaceArrayToLinksArray() sd.DroppedLinkCount = s.links.droppedCount } - s.mu.Unlock() return &sd } // SpanContext returns the SpanContext of the span. -func (s *Span) SpanContext() SpanContext { +func (s *span) SpanContext() SpanContext { if s == nil { return SpanContext{} } @@ -329,7 +338,7 @@ func (s *Span) SpanContext() SpanContext { } // SetName sets the name of the span, if it is recording events. -func (s *Span) SetName(name string) { +func (s *span) SetName(name string) { if !s.IsRecordingEvents() { return } @@ -339,7 +348,7 @@ func (s *Span) SetName(name string) { } // SetStatus sets the status of the span, if it is recording events. -func (s *Span) SetStatus(status Status) { +func (s *span) SetStatus(status Status) { if !s.IsRecordingEvents() { return } @@ -348,7 +357,7 @@ func (s *Span) SetStatus(status Status) { s.mu.Unlock() } -func (s *Span) interfaceArrayToLinksArray() []Link { +func (s *span) interfaceArrayToLinksArray() []Link { linksArr := make([]Link, 0, len(s.links.queue)) for _, value := range s.links.queue { linksArr = append(linksArr, value.(Link)) @@ -356,7 +365,7 @@ func (s *Span) interfaceArrayToLinksArray() []Link { return linksArr } -func (s *Span) interfaceArrayToMessageEventArray() []MessageEvent { +func (s *span) interfaceArrayToMessageEventArray() []MessageEvent { messageEventArr := make([]MessageEvent, 0, len(s.messageEvents.queue)) for _, value := range s.messageEvents.queue { messageEventArr = append(messageEventArr, value.(MessageEvent)) @@ -364,7 +373,7 @@ func (s *Span) interfaceArrayToMessageEventArray() []MessageEvent { return messageEventArr } -func (s *Span) interfaceArrayToAnnotationArray() []Annotation { +func (s *span) interfaceArrayToAnnotationArray() []Annotation { annotationArr := make([]Annotation, 0, len(s.annotations.queue)) for _, value := range s.annotations.queue { annotationArr = append(annotationArr, value.(Annotation)) @@ -372,7 +381,7 @@ func (s *Span) interfaceArrayToAnnotationArray() []Annotation { return annotationArr } -func (s *Span) lruAttributesToAttributeMap() map[string]interface{} { +func (s *span) lruAttributesToAttributeMap() map[string]interface{} { attributes := make(map[string]interface{}, s.lruAttributes.len()) for _, key := range s.lruAttributes.keys() { value, ok := s.lruAttributes.get(key) @@ -384,13 +393,13 @@ func (s *Span) lruAttributesToAttributeMap() map[string]interface{} { return attributes } -func (s *Span) copyToCappedAttributes(attributes []Attribute) { +func (s *span) copyToCappedAttributes(attributes []Attribute) { for _, a := range attributes { s.lruAttributes.add(a.key, a.value) } } -func (s *Span) addChild() { +func (s *span) addChild() { if !s.IsRecordingEvents() { return } @@ -402,7 +411,7 @@ func (s *Span) addChild() { // AddAttributes sets attributes in the span. // // Existing attributes whose keys appear in the attributes parameter are overwritten. -func (s *Span) AddAttributes(attributes ...Attribute) { +func (s *span) AddAttributes(attributes ...Attribute) { if !s.IsRecordingEvents() { return } @@ -418,7 +427,7 @@ func copyAttributes(m map[string]interface{}, attributes []Attribute) { } } -func (s *Span) lazyPrintfInternal(attributes []Attribute, format string, a ...interface{}) { +func (s *span) lazyPrintfInternal(attributes []Attribute, format string, a ...interface{}) { now := time.Now() msg := fmt.Sprintf(format, a...) var m map[string]interface{} @@ -435,7 +444,7 @@ func (s *Span) lazyPrintfInternal(attributes []Attribute, format string, a ...in s.mu.Unlock() } -func (s *Span) printStringInternal(attributes []Attribute, str string) { +func (s *span) printStringInternal(attributes []Attribute, str string) { now := time.Now() var a map[string]interface{} s.mu.Lock() @@ -453,7 +462,7 @@ func (s *Span) printStringInternal(attributes []Attribute, str string) { // Annotate adds an annotation with attributes. // Attributes can be nil. -func (s *Span) Annotate(attributes []Attribute, str string) { +func (s *span) Annotate(attributes []Attribute, str string) { if !s.IsRecordingEvents() { return } @@ -461,7 +470,7 @@ func (s *Span) Annotate(attributes []Attribute, str string) { } // Annotatef adds an annotation with attributes. -func (s *Span) Annotatef(attributes []Attribute, format string, a ...interface{}) { +func (s *span) Annotatef(attributes []Attribute, format string, a ...interface{}) { if !s.IsRecordingEvents() { return } @@ -474,7 +483,7 @@ func (s *Span) Annotatef(attributes []Attribute, format string, a ...interface{} // unique in this span and the same between the send event and the receive // event (this allows to identify a message between the sender and receiver). // For example, this could be a sequence id. -func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) { +func (s *span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) { if !s.IsRecordingEvents() { return } @@ -496,7 +505,7 @@ func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedBy // unique in this span and the same between the send event and the receive // event (this allows to identify a message between the sender and receiver). // For example, this could be a sequence id. -func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) { +func (s *span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) { if !s.IsRecordingEvents() { return } @@ -513,7 +522,7 @@ func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compresse } // AddLink adds a link to the span. -func (s *Span) AddLink(l Link) { +func (s *span) AddLink(l Link) { if !s.IsRecordingEvents() { return } @@ -522,8 +531,8 @@ func (s *Span) AddLink(l Link) { s.mu.Unlock() } -func (s *Span) String() string { - if s == nil { +func (s *span) String() string { + if !s.IsRecordingEvents() { return "" } if s.data == nil { diff --git a/trace/trace_api.go b/trace/trace_api.go new file mode 100644 index 000000000..5ef6f0294 --- /dev/null +++ b/trace/trace_api.go @@ -0,0 +1,137 @@ +// Copyright 2020, OpenCensus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package trace + +import ( + "context" +) + +// DefaultTracer is the tracer used when package-level exported functions are used. +var DefaultTracer Tracer = &tracer{} + +// Tracer can start spans and access context functions. +type Tracer interface { + + // StartSpan starts a new child span of the current span in the context. If + // there is no span in the context, creates a new trace and span. + // + // Returned context contains the newly created span. You can use it to + // propagate the returned span in process. + StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, Span) + + // StartSpanWithRemoteParent starts a new child span of the span from the given parent. + // + // If the incoming context contains a parent, it ignores. StartSpanWithRemoteParent is + // preferred for cases where the parent is propagated via an incoming request. + // + // Returned context contains the newly created span. You can use it to + // propagate the returned span in process. + StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, Span) + + // FromContext returns the Span stored in a context, or nil if there isn't one. + FromContext(ctx context.Context) Span + + // NewContext returns a new context with the given Span attached. + NewContext(parent context.Context, s Span) context.Context +} + +// StartSpan starts a new child span of the current span in the context. If +// there is no span in the context, creates a new trace and span. +// +// Returned context contains the newly created span. You can use it to +// propagate the returned span in process. +func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, Span) { + return DefaultTracer.StartSpan(ctx, name, o...) +} + +// StartSpanWithRemoteParent starts a new child span of the span from the given parent. +// +// If the incoming context contains a parent, it ignores. StartSpanWithRemoteParent is +// preferred for cases where the parent is propagated via an incoming request. +// +// Returned context contains the newly created span. You can use it to +// propagate the returned span in process. +func StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, Span) { + return DefaultTracer.StartSpanWithRemoteParent(ctx, name, parent, o...) +} + +// FromContext returns the Span stored in a context, or nil if there isn't one. +func FromContext(ctx context.Context) Span { + return DefaultTracer.FromContext(ctx) +} + +// NewContext returns a new context with the given Span attached. +func NewContext(parent context.Context, s Span) context.Context { + return DefaultTracer.NewContext(parent, s) +} + +// Span represents a span of a trace. It has an associated SpanContext, and +// stores data accumulated while the span is active. +// +// Ideally users should interact with Spans by calling the functions in this +// package that take a Context parameter. +type Span interface { + + // IsRecordingEvents returns true if events are being recorded for this span. + // Use this check to avoid computing expensive annotations when they will never + // be used. + IsRecordingEvents() bool + + // End ends the span. + End() + + // SpanContext returns the SpanContext of the span. + SpanContext() SpanContext + + // SetName sets the name of the span, if it is recording events. + SetName(name string) + + // SetStatus sets the status of the span, if it is recording events. + SetStatus(status Status) + + // AddAttributes sets attributes in the span. + // + // Existing attributes whose keys appear in the attributes parameter are overwritten. + AddAttributes(attributes ...Attribute) + + // Annotate adds an annotation with attributes. + // Attributes can be nil. + Annotate(attributes []Attribute, str string) + + // Annotatef adds an annotation with attributes. + Annotatef(attributes []Attribute, format string, a ...interface{}) + + // AddMessageSendEvent adds a message send event to the span. + // + // messageID is an identifier for the message, which is recommended to be + // unique in this span and the same between the send event and the receive + // event (this allows to identify a message between the sender and receiver). + // For example, this could be a sequence id. + AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) + + // AddMessageReceiveEvent adds a message receive event to the span. + // + // messageID is an identifier for the message, which is recommended to be + // unique in this span and the same between the send event and the receive + // event (this allows to identify a message between the sender and receiver). + // For example, this could be a sequence id. + AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) + + // AddLink adds a link to the span. + AddLink(l Link) + + // String prints a string representation of a span. + String() string +} diff --git a/trace/trace_test.go b/trace/trace_test.go index c2151367f..77b37633a 100644 --- a/trace/trace_test.go +++ b/trace/trace_test.go @@ -46,7 +46,7 @@ func TestStrings(t *testing.T) { } func TestFromContext(t *testing.T) { - want := &Span{} + want := &span{} ctx := NewContext(context.Background(), want) got := FromContext(ctx) if got != want { @@ -61,20 +61,20 @@ func (f foo) String() string { } // checkChild tests that c has fields set appropriately, given that it is a child span of p. -func checkChild(p SpanContext, c *Span) error { +func checkChild(p SpanContext, c Span) error { if c == nil { return fmt.Errorf("got nil child span, want non-nil") } - if got, want := c.spanContext.TraceID, p.TraceID; got != want { + if got, want := c.SpanContext().TraceID, p.TraceID; got != want { return fmt.Errorf("got child trace ID %s, want %s", got, want) } - if childID, parentID := c.spanContext.SpanID, p.SpanID; childID == parentID { + if childID, parentID := c.SpanContext().SpanID, p.SpanID; childID == parentID { return fmt.Errorf("got child span ID %s, parent span ID %s; want unequal IDs", childID, parentID) } - if got, want := c.spanContext.TraceOptions, p.TraceOptions; got != want { + if got, want := c.SpanContext().TraceOptions, p.TraceOptions; got != want { return fmt.Errorf("got child trace options %d, want %d", got, want) } - if got, want := c.spanContext.Tracestate, p.Tracestate; got != want { + if got, want := c.SpanContext().Tracestate, p.Tracestate; got != want { return fmt.Errorf("got child tracestate %v, want %v", got, want) } return nil @@ -82,7 +82,7 @@ func checkChild(p SpanContext, c *Span) error { func TestStartSpan(t *testing.T) { ctx, _ := StartSpan(context.Background(), "StartSpan") - if FromContext(ctx).data != nil { + if FromContext(ctx).(*span).data != nil { t.Error("StartSpan: new span is recording events") } } @@ -231,7 +231,7 @@ func TestStartSpanWithRemoteParent(t *testing.T) { } // startSpan returns a context with a new Span that is recording events and will be exported. -func startSpan(o StartOptions) *Span { +func startSpan(o StartOptions) Span { _, span := StartSpanWithRemoteParent(context.Background(), "span0", SpanContext{ TraceID: tid, @@ -255,7 +255,7 @@ func (t *testExporter) ExportSpan(s *SpanData) { // endSpan ends the Span in the context and returns the exported SpanData. // // It also does some tests on the Span, and tests and clears some fields in the SpanData. -func endSpan(span *Span) (*SpanData, error) { +func endSpan(span Span) (*SpanData, error) { if !span.IsRecordingEvents() { return nil, fmt.Errorf("IsRecordingEvents: got false, want true") @@ -579,10 +579,10 @@ func TestSetSpanName(t *testing.T) { func TestSetSpanNameUnsampledSpan(t *testing.T) { var nilSpanData *SpanData - span := startSpan(StartOptions{Sampler: NeverSample()}) - span.SetName("NoopName") + sp := startSpan(StartOptions{Sampler: NeverSample()}) + sp.SetName("NoopName") - if want, got := nilSpanData, span.data; want != got { + if want, got := nilSpanData, sp.(*span).data; want != got { t.Errorf("span.data=%+v; want %+v", got, want) } } @@ -861,7 +861,7 @@ func TestChildSpanCount(t *testing.T) { } func TestNilSpanEnd(t *testing.T) { - var span *Span + var span *span span.End() } @@ -871,25 +871,28 @@ func TestExecutionTracerTaskEnd(t *testing.T) { atomic.AddUint64(&n, 1) } - var spans []*Span - _, span := StartSpan(context.Background(), "foo", WithSampler(NeverSample())) - span.executionTracerTaskEnd = executionTracerTaskEnd - spans = append(spans, span) // never sample + var spans []Span + _, s := StartSpan(context.Background(), "foo", WithSampler(NeverSample())) + sp := s.(*span) + sp.executionTracerTaskEnd = executionTracerTaskEnd + spans = append(spans, sp) // never sample - _, span = StartSpanWithRemoteParent(context.Background(), "foo", SpanContext{ + _, s = StartSpanWithRemoteParent(context.Background(), "foo", SpanContext{ TraceID: TraceID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, SpanID: SpanID{0, 1, 2, 3, 4, 5, 6, 7}, TraceOptions: 0, }) - span.executionTracerTaskEnd = executionTracerTaskEnd - spans = append(spans, span) // parent not sampled + sp = s.(*span) + sp.executionTracerTaskEnd = executionTracerTaskEnd + spans = append(spans, sp) // parent not sampled - _, span = StartSpan(context.Background(), "foo", WithSampler(AlwaysSample())) - span.executionTracerTaskEnd = executionTracerTaskEnd - spans = append(spans, span) // always sample + _, s = StartSpan(context.Background(), "foo", WithSampler(AlwaysSample())) + sp = s.(*span) + sp.executionTracerTaskEnd = executionTracerTaskEnd + spans = append(spans, sp) // always sample - for _, span := range spans { - span.End() + for _, sp := range spans { + sp.End() } if got, want := n, uint64(len(spans)); got != want { t.Fatalf("Execution tracer task ended for %v spans; want %v", got, want)