157 lines
4.9 KiB
Go
157 lines
4.9 KiB
Go
// Copyright The OpenTelemetry 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 "go.opentelemetry.io/otel/sdk/trace"
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"go.opentelemetry.io/otel/sdk/instrumentation"
|
|
"go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
type tracer struct {
|
|
provider *TracerProvider
|
|
instrumentationLibrary instrumentation.Library
|
|
}
|
|
|
|
var _ trace.Tracer = &tracer{}
|
|
|
|
// Start starts a Span and returns it along with a context containing it.
|
|
//
|
|
// 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.
|
|
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.(*recordingSpan); ok {
|
|
sdkSpan.addChild()
|
|
}
|
|
}
|
|
|
|
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, rw)
|
|
}
|
|
}
|
|
if rtt, ok := s.(runtimeTracer); ok {
|
|
ctx = rtt.runtimeTrace(ctx)
|
|
}
|
|
|
|
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{
|
|
// Do not pre-allocate the attributes slice here! Doing so will
|
|
// allocate memory that is likely never going to be used, or if used,
|
|
// will be over-sized. The default Go compiler has been tested to
|
|
// dynamically allocate needed space very well. Benchmarking has shown
|
|
// it to be more performant than what we can predetermine here,
|
|
// especially for the common use case of few to no added
|
|
// attributes.
|
|
|
|
parent: psc,
|
|
spanContext: sc,
|
|
spanKind: trace.ValidateSpanKind(config.SpanKind()),
|
|
name: name,
|
|
startTime: startTime,
|
|
events: newEvictedQueue(tr.provider.spanLimits.EventCountLimit),
|
|
links: newEvictedQueue(tr.provider.spanLimits.LinkCountLimit),
|
|
tracer: tr,
|
|
}
|
|
|
|
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}
|
|
}
|