mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-24 20:14:40 +02:00
Refactor SDK span creation and implementation (#2213)
* Refactor startSpanInternal into a tracer method The span creation and configuration process is split across the tracer Start method and the startSpanInternal function, each living in different files. This adds confusion when developing. It requires the developer to remember certain parts of the configuration happen in one place or the other. This unifies the creation and configuration of a new span. It makes this unification by creating a newSpan method on the tracer. This method will do all the configuration needed for a new span other than setting the execution task tracker. This remains a part of the Start method to preserve any existing timing and tracing the already exists. * Add a non-recording span to the SDK * Assign returned context from runtime trace task Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
This commit is contained in:
parent
db317fcea5
commit
c7ae470a16
@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
rt "runtime/trace"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -98,9 +99,9 @@ type ReadWriteSpan interface {
|
||||
ReadOnlySpan
|
||||
}
|
||||
|
||||
// span is an implementation of the OpenTelemetry Span API representing the
|
||||
// individual component of a trace.
|
||||
type span struct {
|
||||
// recordingSpan is an implementation of the OpenTelemetry Span API
|
||||
// representing the individual component of a trace that is sampled.
|
||||
type recordingSpan struct {
|
||||
// mu protects the contents of this span.
|
||||
mu sync.Mutex
|
||||
|
||||
@ -157,10 +158,11 @@ type span struct {
|
||||
spanLimits SpanLimits
|
||||
}
|
||||
|
||||
var _ trace.Span = &span{}
|
||||
var _ ReadWriteSpan = (*recordingSpan)(nil)
|
||||
var _ runtimeTracer = (*recordingSpan)(nil)
|
||||
|
||||
// SpanContext returns the SpanContext of this span.
|
||||
func (s *span) SpanContext() trace.SpanContext {
|
||||
func (s *recordingSpan) SpanContext() trace.SpanContext {
|
||||
if s == nil {
|
||||
return trace.SpanContext{}
|
||||
}
|
||||
@ -169,21 +171,21 @@ func (s *span) SpanContext() trace.SpanContext {
|
||||
|
||||
// IsRecording returns if this span is being recorded. If this span has ended
|
||||
// this will return false.
|
||||
func (s *span) IsRecording() bool {
|
||||
func (s *recordingSpan) IsRecording() bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return !s.startTime.IsZero() && s.endTime.IsZero()
|
||||
return s.endTime.IsZero()
|
||||
}
|
||||
|
||||
// SetStatus sets the status of the Span in the form of a code and a
|
||||
// description, overriding previous values set. The description is only
|
||||
// included in the set status when the code is for an error. If this span is
|
||||
// not being recorded than this method does nothing.
|
||||
func (s *span) SetStatus(code codes.Code, description string) {
|
||||
func (s *recordingSpan) SetStatus(code codes.Code, description string) {
|
||||
if !s.IsRecording() {
|
||||
return
|
||||
}
|
||||
@ -204,7 +206,7 @@ func (s *span) SetStatus(code codes.Code, description string) {
|
||||
// will be overwritten with the value contained in attributes.
|
||||
//
|
||||
// If this span is not being recorded than this method does nothing.
|
||||
func (s *span) SetAttributes(attributes ...attribute.KeyValue) {
|
||||
func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
|
||||
if !s.IsRecording() {
|
||||
return
|
||||
}
|
||||
@ -219,7 +221,7 @@ func (s *span) SetAttributes(attributes ...attribute.KeyValue) {
|
||||
//
|
||||
// If this method is called while panicking an error event is added to the
|
||||
// Span before ending it and the panic is continued.
|
||||
func (s *span) End(options ...trace.SpanEndOption) {
|
||||
func (s *recordingSpan) End(options ...trace.SpanEndOption) {
|
||||
// Do not start by checking if the span is being recorded which requires
|
||||
// acquiring a lock. Make a minimal check that the span is not nil.
|
||||
if s == nil {
|
||||
@ -284,7 +286,7 @@ func (s *span) End(options ...trace.SpanEndOption) {
|
||||
// SetStatus is required if the Status of the Span should be set to Error, this method
|
||||
// does not change the Span status. If this span is not being recorded or err is nil
|
||||
// than this method does nothing.
|
||||
func (s *span) RecordError(err error, opts ...trace.EventOption) {
|
||||
func (s *recordingSpan) RecordError(err error, opts ...trace.EventOption) {
|
||||
if s == nil || err == nil || !s.IsRecording() {
|
||||
return
|
||||
}
|
||||
@ -322,14 +324,14 @@ func recordStackTrace() string {
|
||||
|
||||
// AddEvent adds an event with the provided name and options. If this span is
|
||||
// not being recorded than this method does nothing.
|
||||
func (s *span) AddEvent(name string, o ...trace.EventOption) {
|
||||
func (s *recordingSpan) AddEvent(name string, o ...trace.EventOption) {
|
||||
if !s.IsRecording() {
|
||||
return
|
||||
}
|
||||
s.addEvent(name, o...)
|
||||
}
|
||||
|
||||
func (s *span) addEvent(name string, o ...trace.EventOption) {
|
||||
func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) {
|
||||
c := trace.NewEventConfig(o...)
|
||||
|
||||
// Discard over limited attributes
|
||||
@ -351,7 +353,7 @@ func (s *span) addEvent(name string, o ...trace.EventOption) {
|
||||
|
||||
// SetName sets the name of this span. If this span is not being recorded than
|
||||
// this method does nothing.
|
||||
func (s *span) SetName(name string) {
|
||||
func (s *recordingSpan) SetName(name string) {
|
||||
if !s.IsRecording() {
|
||||
return
|
||||
}
|
||||
@ -362,28 +364,28 @@ func (s *span) SetName(name string) {
|
||||
}
|
||||
|
||||
// Name returns the name of this span.
|
||||
func (s *span) Name() string {
|
||||
func (s *recordingSpan) Name() string {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.name
|
||||
}
|
||||
|
||||
// Name returns the SpanContext of this span's parent span.
|
||||
func (s *span) Parent() trace.SpanContext {
|
||||
func (s *recordingSpan) Parent() trace.SpanContext {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.parent
|
||||
}
|
||||
|
||||
// SpanKind returns the SpanKind of this span.
|
||||
func (s *span) SpanKind() trace.SpanKind {
|
||||
func (s *recordingSpan) SpanKind() trace.SpanKind {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.spanKind
|
||||
}
|
||||
|
||||
// StartTime returns the time this span started.
|
||||
func (s *span) StartTime() time.Time {
|
||||
func (s *recordingSpan) StartTime() time.Time {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.startTime
|
||||
@ -391,14 +393,14 @@ func (s *span) StartTime() time.Time {
|
||||
|
||||
// EndTime returns the time this span ended. For spans that have not yet
|
||||
// ended, the returned value will be the zero value of time.Time.
|
||||
func (s *span) EndTime() time.Time {
|
||||
func (s *recordingSpan) EndTime() time.Time {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.endTime
|
||||
}
|
||||
|
||||
// Attributes returns the attributes of this span.
|
||||
func (s *span) Attributes() []attribute.KeyValue {
|
||||
func (s *recordingSpan) Attributes() []attribute.KeyValue {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.attributes.evictList.Len() == 0 {
|
||||
@ -408,7 +410,7 @@ func (s *span) Attributes() []attribute.KeyValue {
|
||||
}
|
||||
|
||||
// Links returns the links of this span.
|
||||
func (s *span) Links() []Link {
|
||||
func (s *recordingSpan) Links() []Link {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if len(s.links.queue) == 0 {
|
||||
@ -418,7 +420,7 @@ func (s *span) Links() []Link {
|
||||
}
|
||||
|
||||
// Events returns the events of this span.
|
||||
func (s *span) Events() []Event {
|
||||
func (s *recordingSpan) Events() []Event {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if len(s.events.queue) == 0 {
|
||||
@ -428,7 +430,7 @@ func (s *span) Events() []Event {
|
||||
}
|
||||
|
||||
// Status returns the status of this span.
|
||||
func (s *span) Status() Status {
|
||||
func (s *recordingSpan) Status() Status {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.status
|
||||
@ -436,7 +438,7 @@ func (s *span) Status() Status {
|
||||
|
||||
// InstrumentationLibrary returns the instrumentation.Library associated with
|
||||
// the Tracer that created this span.
|
||||
func (s *span) InstrumentationLibrary() instrumentation.Library {
|
||||
func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.instrumentationLibrary
|
||||
@ -444,13 +446,13 @@ func (s *span) InstrumentationLibrary() instrumentation.Library {
|
||||
|
||||
// Resource returns the Resource associated with the Tracer that created this
|
||||
// span.
|
||||
func (s *span) Resource() *resource.Resource {
|
||||
func (s *recordingSpan) Resource() *resource.Resource {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.resource
|
||||
}
|
||||
|
||||
func (s *span) addLink(link trace.Link) {
|
||||
func (s *recordingSpan) addLink(link trace.Link) {
|
||||
if !s.IsRecording() {
|
||||
return
|
||||
}
|
||||
@ -470,7 +472,7 @@ func (s *span) addLink(link trace.Link) {
|
||||
|
||||
// DroppedAttributes returns the number of attributes dropped by the span
|
||||
// due to limits being reached.
|
||||
func (s *span) DroppedAttributes() int {
|
||||
func (s *recordingSpan) DroppedAttributes() int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.attributes.droppedCount
|
||||
@ -478,7 +480,7 @@ func (s *span) DroppedAttributes() int {
|
||||
|
||||
// DroppedLinks returns the number of links dropped by the span due to limits
|
||||
// being reached.
|
||||
func (s *span) DroppedLinks() int {
|
||||
func (s *recordingSpan) DroppedLinks() int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.links.droppedCount
|
||||
@ -486,7 +488,7 @@ func (s *span) DroppedLinks() int {
|
||||
|
||||
// DroppedEvents returns the number of events dropped by the span due to
|
||||
// limits being reached.
|
||||
func (s *span) DroppedEvents() int {
|
||||
func (s *recordingSpan) DroppedEvents() int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.events.droppedCount
|
||||
@ -494,7 +496,7 @@ func (s *span) DroppedEvents() int {
|
||||
|
||||
// ChildSpanCount returns the count of spans that consider the span a
|
||||
// direct parent.
|
||||
func (s *span) ChildSpanCount() int {
|
||||
func (s *recordingSpan) ChildSpanCount() int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.childSpanCount
|
||||
@ -502,12 +504,12 @@ func (s *span) ChildSpanCount() int {
|
||||
|
||||
// TracerProvider returns a trace.TracerProvider that can be used to generate
|
||||
// additional Spans on the same telemetry pipeline as the current Span.
|
||||
func (s *span) TracerProvider() trace.TracerProvider {
|
||||
func (s *recordingSpan) TracerProvider() trace.TracerProvider {
|
||||
return s.tracer.provider
|
||||
}
|
||||
|
||||
// snapshot creates a read-only copy of the current state of the span.
|
||||
func (s *span) snapshot() ReadOnlySpan {
|
||||
func (s *recordingSpan) snapshot() ReadOnlySpan {
|
||||
var sd snapshot
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@ -538,7 +540,7 @@ func (s *span) snapshot() ReadOnlySpan {
|
||||
return &sd
|
||||
}
|
||||
|
||||
func (s *span) interfaceArrayToLinksArray() []Link {
|
||||
func (s *recordingSpan) interfaceArrayToLinksArray() []Link {
|
||||
linkArr := make([]Link, 0)
|
||||
for _, value := range s.links.queue {
|
||||
linkArr = append(linkArr, value.(Link))
|
||||
@ -546,7 +548,7 @@ func (s *span) interfaceArrayToLinksArray() []Link {
|
||||
return linkArr
|
||||
}
|
||||
|
||||
func (s *span) interfaceArrayToEventArray() []Event {
|
||||
func (s *recordingSpan) interfaceArrayToEventArray() []Event {
|
||||
eventArr := make([]Event, 0)
|
||||
for _, value := range s.events.queue {
|
||||
eventArr = append(eventArr, value.(Event))
|
||||
@ -554,7 +556,7 @@ func (s *span) interfaceArrayToEventArray() []Event {
|
||||
return eventArr
|
||||
}
|
||||
|
||||
func (s *span) copyToCappedAttributes(attributes ...attribute.KeyValue) {
|
||||
func (s *recordingSpan) copyToCappedAttributes(attributes ...attribute.KeyValue) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, a := range attributes {
|
||||
@ -566,7 +568,7 @@ func (s *span) copyToCappedAttributes(attributes ...attribute.KeyValue) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *span) addChild() {
|
||||
func (s *recordingSpan) addChild() {
|
||||
if !s.IsRecording() {
|
||||
return
|
||||
}
|
||||
@ -575,82 +577,66 @@ func (s *span) addChild() {
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (*span) private() {}
|
||||
func (*recordingSpan) private() {}
|
||||
|
||||
func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.SpanConfig) *span {
|
||||
span := &span{}
|
||||
|
||||
provider := tr.provider
|
||||
|
||||
// If told explicitly to make this a new root use a zero value SpanContext
|
||||
// as a parent which contains an invalid trace ID and is not remote.
|
||||
var psc trace.SpanContext
|
||||
if o.NewRoot() {
|
||||
ctx = trace.ContextWithSpanContext(ctx, psc)
|
||||
} else {
|
||||
psc = trace.SpanContextFromContext(ctx)
|
||||
// runtimeTrace starts a "runtime/trace".Task for the span and returns a
|
||||
// context containing the task.
|
||||
func (s *recordingSpan) runtimeTrace(ctx context.Context) context.Context {
|
||||
if !rt.IsEnabled() {
|
||||
// Avoid additional overhead if runtime/trace is not enabled.
|
||||
return ctx
|
||||
}
|
||||
nctx, task := rt.NewTask(ctx, s.name)
|
||||
|
||||
// If there is a valid parent trace ID, use it to ensure the continuity of
|
||||
// the trace. Always generate a new span ID so other components can rely
|
||||
// on a unique span ID, even if the Span is non-recording.
|
||||
var tid trace.TraceID
|
||||
var sid trace.SpanID
|
||||
if !psc.TraceID().IsValid() {
|
||||
tid, sid = provider.idGenerator.NewIDs(ctx)
|
||||
} else {
|
||||
tid = psc.TraceID()
|
||||
sid = provider.idGenerator.NewSpanID(ctx, tid)
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.executionTracerTaskEnd = task.End
|
||||
s.mu.Unlock()
|
||||
|
||||
spanLimits := provider.spanLimits
|
||||
span.attributes = newAttributesMap(spanLimits.AttributeCountLimit)
|
||||
span.events = newEvictedQueue(spanLimits.EventCountLimit)
|
||||
span.links = newEvictedQueue(spanLimits.LinkCountLimit)
|
||||
span.spanLimits = spanLimits
|
||||
|
||||
samplingResult := provider.sampler.ShouldSample(SamplingParameters{
|
||||
ParentContext: ctx,
|
||||
TraceID: tid,
|
||||
Name: name,
|
||||
Kind: o.SpanKind(),
|
||||
Attributes: o.Attributes(),
|
||||
Links: o.Links(),
|
||||
})
|
||||
|
||||
scc := trace.SpanContextConfig{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceState: samplingResult.Tracestate,
|
||||
}
|
||||
if isSampled(samplingResult) {
|
||||
scc.TraceFlags = psc.TraceFlags() | trace.FlagsSampled
|
||||
} else {
|
||||
scc.TraceFlags = psc.TraceFlags() &^ trace.FlagsSampled
|
||||
}
|
||||
span.spanContext = trace.NewSpanContext(scc)
|
||||
|
||||
if !isRecording(samplingResult) {
|
||||
return span
|
||||
}
|
||||
|
||||
startTime := o.Timestamp()
|
||||
if startTime.IsZero() {
|
||||
startTime = time.Now()
|
||||
}
|
||||
span.startTime = startTime
|
||||
|
||||
span.spanKind = trace.ValidateSpanKind(o.SpanKind())
|
||||
span.name = name
|
||||
span.parent = psc
|
||||
span.resource = provider.resource
|
||||
span.instrumentationLibrary = tr.instrumentationLibrary
|
||||
|
||||
span.SetAttributes(samplingResult.Attributes...)
|
||||
|
||||
return span
|
||||
return nctx
|
||||
}
|
||||
|
||||
// nonRecordingSpan is a minimal implementation of the OpenTelemetry Span API
|
||||
// that wraps a SpanContext. It performs no operations other than to return
|
||||
// the wrapped SpanContext or TracerProvider that created it.
|
||||
type nonRecordingSpan struct {
|
||||
// tracer is the SDK tracer that created this span.
|
||||
tracer *tracer
|
||||
sc trace.SpanContext
|
||||
}
|
||||
|
||||
var _ trace.Span = nonRecordingSpan{}
|
||||
|
||||
// SpanContext returns the wrapped SpanContext.
|
||||
func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc }
|
||||
|
||||
// IsRecording always returns false.
|
||||
func (nonRecordingSpan) IsRecording() bool { return false }
|
||||
|
||||
// SetStatus does nothing.
|
||||
func (nonRecordingSpan) SetStatus(codes.Code, string) {}
|
||||
|
||||
// SetError does nothing.
|
||||
func (nonRecordingSpan) SetError(bool) {}
|
||||
|
||||
// SetAttributes does nothing.
|
||||
func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {}
|
||||
|
||||
// End does nothing.
|
||||
func (nonRecordingSpan) End(...trace.SpanEndOption) {}
|
||||
|
||||
// RecordError does nothing.
|
||||
func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {}
|
||||
|
||||
// AddEvent does nothing.
|
||||
func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {}
|
||||
|
||||
// SetName does nothing.
|
||||
func (nonRecordingSpan) SetName(string) {}
|
||||
|
||||
// TracerProvider returns the trace.TracerProvider that provided the Tracer
|
||||
// that created this span.
|
||||
func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider }
|
||||
|
||||
func isRecording(s SamplingResult) bool {
|
||||
return s.Decision == RecordOnly || s.Decision == RecordAndSample
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func TestSetName(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
sp := startNamedSpan(tp, "SetName", tt.name)
|
||||
if sdkspan, ok := sp.(*span); ok {
|
||||
if sdkspan, ok := sp.(*recordingSpan); ok {
|
||||
if sdkspan.Name() != tt.name {
|
||||
t.Errorf("%d: invalid name at span creation, expected %v, got %v", idx, tt.name, sdkspan.Name())
|
||||
}
|
||||
@ -203,7 +203,7 @@ func TestSetName(t *testing.T) {
|
||||
t.Errorf("%d: unable to coerce span to SDK span, is type %T", idx, sp)
|
||||
}
|
||||
sp.SetName(tt.newName)
|
||||
if sdkspan, ok := sp.(*span); ok {
|
||||
if sdkspan, ok := sp.(*recordingSpan); ok {
|
||||
if sdkspan.Name() != tt.newName {
|
||||
t.Errorf("%d: span name not changed, expected %v, got %v", idx, tt.newName, sdkspan.Name())
|
||||
}
|
||||
@ -806,7 +806,7 @@ func cmpDiff(x, y interface{}) string {
|
||||
// checkChild is test utility function that tests that c has fields set appropriately,
|
||||
// given that it is a child span of p.
|
||||
func checkChild(t *testing.T, p trace.SpanContext, apiSpan trace.Span) error {
|
||||
s := apiSpan.(*span)
|
||||
s := apiSpan.(*recordingSpan)
|
||||
if s == nil {
|
||||
return fmt.Errorf("got nil child span, want non-nil")
|
||||
}
|
||||
@ -1016,58 +1016,39 @@ func TestChildSpanCount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNilSpanEnd(t *testing.T) {
|
||||
var span *span
|
||||
var span *recordingSpan
|
||||
span.End()
|
||||
}
|
||||
|
||||
func TestExecutionTracerTaskEnd(t *testing.T) {
|
||||
var n uint64
|
||||
func TestNonRecordingSpanDoesNotTrackRuntimeTracerTask(t *testing.T) {
|
||||
tp := NewTracerProvider(WithSampler(NeverSample()))
|
||||
tr := tp.Tracer("Execution Tracer Task End")
|
||||
tr := tp.Tracer("TestNonRecordingSpanDoesNotTrackRuntimeTracerTask")
|
||||
|
||||
_, apiSpan := tr.Start(context.Background(), "foo")
|
||||
if _, ok := apiSpan.(runtimeTracer); ok {
|
||||
t.Fatalf("non recording span implements runtime trace task tracking")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordingSpanRuntimeTracerTaskEnd(t *testing.T) {
|
||||
tp := NewTracerProvider(WithSampler(AlwaysSample()))
|
||||
tr := tp.Tracer("TestRecordingSpanRuntimeTracerTaskEnd")
|
||||
|
||||
var n uint64
|
||||
executionTracerTaskEnd := func() {
|
||||
atomic.AddUint64(&n, 1)
|
||||
}
|
||||
|
||||
var spans []*span
|
||||
_, apiSpan := tr.Start(context.Background(), "foo")
|
||||
s := apiSpan.(*span)
|
||||
|
||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||
spans = append(spans, s) // never sample
|
||||
|
||||
tID, _ := trace.TraceIDFromHex("0102030405060708090a0b0c0d0e0f")
|
||||
sID, _ := trace.SpanIDFromHex("0001020304050607")
|
||||
ctx := context.Background()
|
||||
|
||||
ctx = trace.ContextWithRemoteSpanContext(ctx,
|
||||
trace.NewSpanContext(trace.SpanContextConfig{
|
||||
TraceID: tID,
|
||||
SpanID: sID,
|
||||
TraceFlags: 0,
|
||||
}),
|
||||
)
|
||||
_, apiSpan = tr.Start(
|
||||
ctx,
|
||||
"foo",
|
||||
)
|
||||
s = apiSpan.(*span)
|
||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||
spans = append(spans, s) // parent not sampled
|
||||
|
||||
tp.sampler = AlwaysSample()
|
||||
_, apiSpan = tr.Start(context.Background(), "foo")
|
||||
s = apiSpan.(*span)
|
||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||
spans = append(spans, s) // always sample
|
||||
|
||||
for _, span := range spans {
|
||||
span.End()
|
||||
s, ok := apiSpan.(*recordingSpan)
|
||||
if !ok {
|
||||
t.Fatal("recording span not returned from always sampled Tracer")
|
||||
}
|
||||
// Only one span should be sampled meaning only one execution of
|
||||
// executionTracerTaskEnd.
|
||||
if got, want := n, uint64(1); got != want {
|
||||
t.Fatalf("Execution tracer task ended for %v spans; want %v", got, want)
|
||||
|
||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||
s.End()
|
||||
|
||||
if n != 1 {
|
||||
t.Error("recording span did not end runtime trace task")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1202,8 +1183,8 @@ func TestRecordErrorWithStackTrace(t *testing.T) {
|
||||
assert.Equal(t, got.events[0].Attributes[1].Value.AsString(), want.events[0].Attributes[1].Value.AsString())
|
||||
gotStackTraceFunctionName := strings.Split(got.events[0].Attributes[2].Value.AsString(), "\n")
|
||||
|
||||
assert.True(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"))
|
||||
assert.True(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*span).RecordError"))
|
||||
assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace", gotStackTraceFunctionName[1])
|
||||
assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError", gotStackTraceFunctionName[3])
|
||||
}
|
||||
|
||||
func TestRecordErrorNil(t *testing.T) {
|
||||
@ -1434,9 +1415,8 @@ func TestSpanCapturesPanicWithStackTrace(t *testing.T) {
|
||||
assert.Equal(t, spans[0].Events()[0].Attributes[1].Value.AsString(), "error message")
|
||||
|
||||
gotStackTraceFunctionName := strings.Split(spans[0].Events()[0].Attributes[2].Value.AsString(), "\n")
|
||||
fmt.Println(strings.Split(gotStackTraceFunctionName[1], "(0x")[0])
|
||||
assert.True(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"))
|
||||
assert.True(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*span).End"))
|
||||
assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace", gotStackTraceFunctionName[1])
|
||||
assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End", gotStackTraceFunctionName[3])
|
||||
}
|
||||
|
||||
func TestReadOnlySpan(t *testing.T) {
|
||||
@ -1498,9 +1478,9 @@ func TestReadOnlySpan(t *testing.T) {
|
||||
|
||||
// Verify snapshot() returns snapshots that are independent from the
|
||||
// original span and from one another.
|
||||
d1 := s.(*span).snapshot()
|
||||
d1 := s.(*recordingSpan).snapshot()
|
||||
s.AddEvent("baz")
|
||||
d2 := s.(*span).snapshot()
|
||||
d2 := s.(*recordingSpan).snapshot()
|
||||
for _, e := range d1.Events() {
|
||||
if e.Name == "baz" {
|
||||
t.Errorf("Didn't expect to find 'baz' event")
|
||||
|
@ -16,7 +16,7 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
||||
import (
|
||||
"context"
|
||||
rt "runtime/trace"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
@ -34,42 +34,120 @@ var _ trace.Tracer = &tracer{}
|
||||
//
|
||||
// The Span is created with the provided name and as a child of any existing
|
||||
// span context found in the passed context. The created Span will be
|
||||
// configured appropriately by any SpanOption passed. Any Timestamp option
|
||||
// passed will be used as the start time of the Span's life-cycle.
|
||||
// configured appropriately by any SpanOption passed.
|
||||
func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
config := trace.NewSpanStartConfig(options...)
|
||||
|
||||
// For local spans created by this SDK, track child span count.
|
||||
if p := trace.SpanFromContext(ctx); p != nil {
|
||||
if sdkSpan, ok := p.(*span); ok {
|
||||
if sdkSpan, ok := p.(*recordingSpan); ok {
|
||||
sdkSpan.addChild()
|
||||
}
|
||||
}
|
||||
|
||||
span := startSpanInternal(ctx, tr, name, &config)
|
||||
for _, l := range config.Links() {
|
||||
span.addLink(l)
|
||||
}
|
||||
span.SetAttributes(config.Attributes()...)
|
||||
|
||||
span.tracer = tr
|
||||
|
||||
if span.IsRecording() {
|
||||
s := tr.newSpan(ctx, name, &config)
|
||||
if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() {
|
||||
sps, _ := tr.provider.spanProcessors.Load().(spanProcessorStates)
|
||||
for _, sp := range sps {
|
||||
sp.sp.OnStart(ctx, span)
|
||||
sp.sp.OnStart(ctx, rw)
|
||||
}
|
||||
}
|
||||
if rtt, ok := s.(runtimeTracer); ok {
|
||||
ctx = rtt.runtimeTrace(ctx)
|
||||
}
|
||||
|
||||
ctx, span.executionTracerTaskEnd = func(ctx context.Context) (context.Context, func()) {
|
||||
if !rt.IsEnabled() {
|
||||
// Avoid additional overhead if
|
||||
// runtime/trace is not enabled.
|
||||
return ctx, func() {}
|
||||
}
|
||||
nctx, task := rt.NewTask(ctx, name)
|
||||
return nctx, task.End
|
||||
}(ctx)
|
||||
|
||||
return trace.ContextWithSpan(ctx, span), span
|
||||
return trace.ContextWithSpan(ctx, s), s
|
||||
}
|
||||
|
||||
type runtimeTracer interface {
|
||||
// runtimeTrace starts a "runtime/trace".Task for the span and
|
||||
// returns a context containing the task.
|
||||
runtimeTrace(ctx context.Context) context.Context
|
||||
}
|
||||
|
||||
// newSpan returns a new configured span.
|
||||
func (tr *tracer) newSpan(ctx context.Context, name string, config *trace.SpanConfig) trace.Span {
|
||||
// If told explicitly to make this a new root use a zero value SpanContext
|
||||
// as a parent which contains an invalid trace ID and is not remote.
|
||||
var psc trace.SpanContext
|
||||
if config.NewRoot() {
|
||||
ctx = trace.ContextWithSpanContext(ctx, psc)
|
||||
} else {
|
||||
psc = trace.SpanContextFromContext(ctx)
|
||||
}
|
||||
|
||||
// If there is a valid parent trace ID, use it to ensure the continuity of
|
||||
// the trace. Always generate a new span ID so other components can rely
|
||||
// on a unique span ID, even if the Span is non-recording.
|
||||
var tid trace.TraceID
|
||||
var sid trace.SpanID
|
||||
if !psc.TraceID().IsValid() {
|
||||
tid, sid = tr.provider.idGenerator.NewIDs(ctx)
|
||||
} else {
|
||||
tid = psc.TraceID()
|
||||
sid = tr.provider.idGenerator.NewSpanID(ctx, tid)
|
||||
}
|
||||
|
||||
samplingResult := tr.provider.sampler.ShouldSample(SamplingParameters{
|
||||
ParentContext: ctx,
|
||||
TraceID: tid,
|
||||
Name: name,
|
||||
Kind: config.SpanKind(),
|
||||
Attributes: config.Attributes(),
|
||||
Links: config.Links(),
|
||||
})
|
||||
|
||||
scc := trace.SpanContextConfig{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceState: samplingResult.Tracestate,
|
||||
}
|
||||
if isSampled(samplingResult) {
|
||||
scc.TraceFlags = psc.TraceFlags() | trace.FlagsSampled
|
||||
} else {
|
||||
scc.TraceFlags = psc.TraceFlags() &^ trace.FlagsSampled
|
||||
}
|
||||
sc := trace.NewSpanContext(scc)
|
||||
|
||||
if !isRecording(samplingResult) {
|
||||
return tr.newNonRecordingSpan(sc)
|
||||
}
|
||||
return tr.newRecordingSpan(psc, sc, name, samplingResult, config)
|
||||
}
|
||||
|
||||
// newRecordingSpan returns a new configured recordingSpan.
|
||||
func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr SamplingResult, config *trace.SpanConfig) *recordingSpan {
|
||||
startTime := config.Timestamp()
|
||||
if startTime.IsZero() {
|
||||
startTime = time.Now()
|
||||
}
|
||||
|
||||
s := &recordingSpan{
|
||||
parent: psc,
|
||||
spanContext: sc,
|
||||
spanKind: trace.ValidateSpanKind(config.SpanKind()),
|
||||
name: name,
|
||||
startTime: startTime,
|
||||
attributes: newAttributesMap(tr.provider.spanLimits.AttributeCountLimit),
|
||||
events: newEvictedQueue(tr.provider.spanLimits.EventCountLimit),
|
||||
links: newEvictedQueue(tr.provider.spanLimits.LinkCountLimit),
|
||||
tracer: tr,
|
||||
spanLimits: tr.provider.spanLimits,
|
||||
resource: tr.provider.resource,
|
||||
instrumentationLibrary: tr.instrumentationLibrary,
|
||||
}
|
||||
|
||||
for _, l := range config.Links() {
|
||||
s.addLink(l)
|
||||
}
|
||||
|
||||
s.SetAttributes(sr.Attributes...)
|
||||
s.SetAttributes(config.Attributes()...)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// newNonRecordingSpan returns a new configured nonRecordingSpan.
|
||||
func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan {
|
||||
return nonRecordingSpan{tracer: tr, sc: sc}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user