// Copyright The OpenTelemetry Authors// SPDX-License-Identifier: Apache-2.0package trace // import "go.opentelemetry.io/otel/sdk/trace"import (rtsemconv)// ReadOnlySpan allows reading information from the data structure underlying a// trace.Span. It is used in places where reading information from a span is// necessary but changing the span isn't necessary or allowed.//// Warning: methods may be added to this interface in minor releases.typeReadOnlySpaninterface {// Name returns the name of the span.Name() string// SpanContext returns the unique SpanContext that identifies the span.SpanContext() trace.SpanContext// Parent returns the unique SpanContext that identifies the parent of the // span if one exists. If the span has no parent the returned SpanContext // will be invalid.Parent() trace.SpanContext// SpanKind returns the role the span plays in a Trace.SpanKind() trace.SpanKind// StartTime returns the time the span started recording.StartTime() time.Time// EndTime returns the time the span stopped recording. It will be zero if // the span has not ended.EndTime() time.Time// Attributes returns the defining attributes of the span. // The order of the returned attributes is not guaranteed to be stable across invocations.Attributes() []attribute.KeyValue// Links returns all the links the span has to other spans.Links() []Link// Events returns all the events that occurred within in the spans // lifetime.Events() []Event// Status returns the spans status.Status() Status// InstrumentationScope returns information about the instrumentation // scope that created the span.InstrumentationScope() instrumentation.Scope// InstrumentationLibrary returns information about the instrumentation // library that created the span. // // Deprecated: please use InstrumentationScope instead.InstrumentationLibrary() instrumentation.Library//nolint:staticcheck // This method needs to be define for backwards compatibility// Resource returns information about the entity that produced the span.Resource() *resource.Resource// DroppedAttributes returns the number of attributes dropped by the span // due to limits being reached.DroppedAttributes() int// DroppedLinks returns the number of links dropped by the span due to // limits being reached.DroppedLinks() int// DroppedEvents returns the number of events dropped by the span due to // limits being reached.DroppedEvents() int// ChildSpanCount returns the count of spans that consider the span a // direct parent.ChildSpanCount() int// A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private()}// ReadWriteSpan exposes the same methods as trace.Span and in addition allows// reading information from the underlying data structure.// This interface exposes the union of the methods of trace.Span (which is a// "write-only" span) and ReadOnlySpan. New methods for writing or reading span// information should be added under trace.Span or ReadOnlySpan, respectively.//// Warning: methods may be added to this interface in minor releases.typeReadWriteSpaninterface {trace.SpanReadOnlySpan}// recordingSpan is an implementation of the OpenTelemetry Span API// representing the individual component of a trace that is sampled.type recordingSpan struct {embedded.Span// mu protects the contents of this span. mu sync.Mutex// parent holds the parent span of this span as a trace.SpanContext. parent trace.SpanContext// spanKind represents the kind of this span as a trace.SpanKind. spanKind trace.SpanKind// name is the name of this span. name string// startTime is the time at which this span was started. startTime time.Time// endTime is the time at which this span was ended. It contains the zero // value of time.Time until the span is ended. endTime time.Time// status is the status of this span. status Status// childSpanCount holds the number of child spans created for this span. childSpanCount int// spanContext holds the SpanContext of this span. spanContext trace.SpanContext// attributes is a collection of user provided key/values. The collection // is constrained by a configurable maximum held by the parent // TracerProvider. When additional attributes are added after this maximum // is reached these attributes the user is attempting to add are dropped. // This dropped number of attributes is tracked and reported in the // ReadOnlySpan exported when the span ends. attributes []attribute.KeyValue droppedAttributes int logDropAttrsOnce sync.Once// events are stored in FIFO queue capped by configured limit. events evictedQueue[Event]// links are stored in FIFO queue capped by configured limit. links evictedQueue[Link]// executionTracerTaskEnd ends the execution tracer span. executionTracerTaskEnd func()// tracer is the SDK tracer that created this span. tracer *tracer}var ( _ ReadWriteSpan = (*recordingSpan)(nil) _ runtimeTracer = (*recordingSpan)(nil))// SpanContext returns the SpanContext of this span.func ( *recordingSpan) () trace.SpanContext {if == nil {returntrace.SpanContext{} }return .spanContext}// IsRecording reports whether this span is being recorded. If this span has ended// this will return false.func ( *recordingSpan) () bool {if == nil {returnfalse } .mu.Lock()defer .mu.Unlock()return .isRecording()}// isRecording reports whether this span is being recorded. If this span has ended// this will return false.//// This method assumes s.mu.Lock is held by the caller.func ( *recordingSpan) () bool {if == nil {returnfalse }return .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 ( *recordingSpan) ( codes.Code, string) {if == nil {return } .mu.Lock()defer .mu.Unlock()if !.isRecording() {return }if .status.Code > {return } := Status{Code: }if == codes.Error { .Description = } .status = }// SetAttributes sets attributes of this span.//// If a key from attributes already exists the value associated with that key// will be overwritten with the value contained in attributes.//// If this span is not being recorded than this method does nothing.//// If adding attributes to the span would exceed the maximum amount of// attributes the span is configured to have, the last added attributes will// be dropped.func ( *recordingSpan) ( ...attribute.KeyValue) {if == nil || len() == 0 {return } .mu.Lock()defer .mu.Unlock()if !.isRecording() {return } := .tracer.provider.spanLimits.AttributeCountLimitif == 0 {// No attributes allowed. .addDroppedAttr(len())return }// If adding these attributes could exceed the capacity of s perform a // de-duplication and truncation while adding to avoid over allocation.if > 0 && len(.attributes)+len() > { .addOverCapAttrs(, )return }// Otherwise, add without deduplication. When attributes are read they // will be deduplicated, optimizing the operation. .attributes = slices.Grow(.attributes, len())for , := range {if !.Valid() {// Drop all invalid attributes. .addDroppedAttr(1)continue } = truncateAttr(.tracer.provider.spanLimits.AttributeValueLengthLimit, ) .attributes = append(.attributes, ) }}// Declared as a var so tests can override.var logDropAttrs = func() {global.Warn("limit reached: dropping trace Span attributes")}// addDroppedAttr adds incr to the count of dropped attributes.//// The first, and only the first, time this method is called a warning will be// logged.//// This method assumes s.mu.Lock is held by the caller.func ( *recordingSpan) ( int) { .droppedAttributes += .logDropAttrsOnce.Do(logDropAttrs)}// addOverCapAttrs adds the attributes attrs to the span s while// de-duplicating the attributes of s and attrs and dropping attributes that// exceed the limit.//// This method assumes s.mu.Lock is held by the caller.//// This method should only be called when there is a possibility that adding// attrs to s will exceed the limit. Otherwise, attrs should be added to s// without checking for duplicates and all retrieval methods of the attributes// for s will de-duplicate as needed.//// This method assumes limit is a value > 0. The argument should be validated// by the caller.func ( *recordingSpan) ( int, []attribute.KeyValue) {// In order to not allocate more capacity to s.attributes than needed, // prune and truncate this addition of attributes while adding.// Do not set a capacity when creating this map. Benchmark testing has // showed this to only add unused memory allocations in general use. := make(map[attribute.Key]int, len(.attributes)) .dedupeAttrsFromRecord()// Now that s.attributes is deduplicated, adding unique attributes up to // the capacity of s will not over allocate s.attributes.// max size = limit := min(len()+len(.attributes), )ifcap(.attributes) < { .attributes = slices.Grow(.attributes, -cap(.attributes)) }for , := range {if !.Valid() {// Drop all invalid attributes. .addDroppedAttr(1)continue }if , := [.Key]; {// Perform all updates before dropping, even when at capacity. = truncateAttr(.tracer.provider.spanLimits.AttributeValueLengthLimit, ) .attributes[] = continue }iflen(.attributes) >= {// Do not just drop all of the remaining attributes, make sure // updates are checked and performed. .addDroppedAttr(1) } else { = truncateAttr(.tracer.provider.spanLimits.AttributeValueLengthLimit, ) .attributes = append(.attributes, ) [.Key] = len(.attributes) - 1 } }}// truncateAttr returns a truncated version of attr. Only string and string// slice attribute values are truncated. String values are truncated to at// most a length of limit. Each string slice value is truncated in this fashion// (the slice length itself is unaffected).//// No truncation is performed for a negative limit.func truncateAttr( int, attribute.KeyValue) attribute.KeyValue {if < 0 {return }switch .Value.Type() {caseattribute.STRING: := .Value.AsString()return .Key.String(truncate(, ))caseattribute.STRINGSLICE: := .Value.AsStringSlice()for := range { [] = truncate(, []) }return .Key.StringSlice() }return}// truncate returns a truncated version of s such that it contains less than// the limit number of characters. Truncation is applied by returning the limit// number of valid characters contained in s.//// If limit is negative, it returns the original string.//// UTF-8 is supported. When truncating, all invalid characters are dropped// before applying truncation.//// If s already contains less than the limit number of bytes, it is returned// unchanged. No invalid characters are removed.func truncate( int, string) string {// This prioritize performance in the following order based on the most // common expected use-cases. // // - Short values less than the default limit (128). // - Strings with valid encodings that exceed the limit. // - No limit. // - Strings with invalid encodings that exceed the limit.if < 0 || len() <= {return }// Optimistically, assume all valid UTF-8.varstrings.Builder := 0for , := range {if != utf8.RuneError { ++if > {return [:] }continue } , := utf8.DecodeRuneInString([:])if == 1 {// Invalid encoding. .Grow(len() - 1) _, _ = .WriteString([:]) = [:]break } }// Fast-path, no invalid input.if .Cap() == 0 {return }// Truncate while validating UTF-8.for := 0; < len() && < ; { := []if < utf8.RuneSelf {// Optimization for single byte runes (common case). _ = .WriteByte() ++ ++continue } , := utf8.DecodeRuneInString([:])if == 1 {// We checked for all 1-byte runes above, this is a RuneError. ++continue } _, _ = .WriteString([ : +]) += ++ }return .String()}// End ends the span. This method does nothing if the span is already ended or// is not being recorded.//// The only SpanEndOption currently supported are [trace.WithTimestamp], and// [trace.WithStackTrace].//// If this method is called while panicking an error event is added to the// Span before ending it and the panic is continued.func ( *recordingSpan) ( ...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 == nil {return }// Store the end time as soon as possible to avoid artificially increasing // the span's duration in case some operation below takes a while. := monotonicEndTime(.startTime)// Lock the span now that we have an end time and see if we need to do any more processing. .mu.Lock()if !.isRecording() { .mu.Unlock()return } := trace.NewSpanEndConfig(...)if := recover(); != nil {// Record but don't stop the panic.deferpanic() := []trace.EventOption{trace.WithAttributes(semconv.ExceptionType(typeStr()),semconv.ExceptionMessage(fmt.Sprint()), ), }if .StackTrace() { = append(, trace.WithAttributes(semconv.ExceptionStacktrace(recordStackTrace()), )) } .addEvent(semconv.ExceptionEventName, ...) }if .executionTracerTaskEnd != nil { .mu.Unlock() .executionTracerTaskEnd() .mu.Lock() }// Setting endTime to non-zero marks the span as ended and not recording.if .Timestamp().IsZero() { .endTime = } else { .endTime = .Timestamp() } .mu.Unlock()if .tracer.selfObservabilityEnabled {deferfunc() {// Add the span to the context to ensure the metric is recorded // with the correct span context. := trace.ContextWithSpan(context.Background(), ) := spanLiveSet(.spanContext.IsSampled()) .tracer.spanLiveMetric.AddSet(, -1, ) }() } := .tracer.provider.getSpanProcessors()iflen() == 0 {return } := .snapshot()for , := range { .sp.OnEnd() }}// monotonicEndTime returns the end time at present but offset from start,// monotonically.//// The monotonic clock is used in subtractions hence the duration since start// added back to start gives end as a monotonic time. See// https://golang.org/pkg/time/#hdr-Monotonic_Clocksfunc monotonicEndTime( time.Time) time.Time {return .Add(time.Since())}// RecordError will record err as a span event for this span. An additional call to// 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 ( *recordingSpan) ( error, ...trace.EventOption) {if == nil || == nil {return } .mu.Lock()defer .mu.Unlock()if !.isRecording() {return } = append(, trace.WithAttributes(semconv.ExceptionType(typeStr()),semconv.ExceptionMessage(.Error()), )) := trace.NewEventConfig(...)if .StackTrace() { = append(, trace.WithAttributes(semconv.ExceptionStacktrace(recordStackTrace()), )) } .addEvent(semconv.ExceptionEventName, ...)}func typeStr( any) string { := reflect.TypeOf()if .PkgPath() == "" && .Name() == "" {// Likely a builtin type.return .String() }returnfmt.Sprintf("%s.%s", .PkgPath(), .Name())}func recordStackTrace() string { := make([]byte, 2048) := runtime.Stack(, false)returnstring([0:])}// AddEvent adds an event with the provided name and options. If this span is// not being recorded then this method does nothing.func ( *recordingSpan) ( string, ...trace.EventOption) {if == nil {return } .mu.Lock()defer .mu.Unlock()if !.isRecording() {return } .addEvent(, ...)}// addEvent adds an event with the provided name and options.//// This method assumes s.mu.Lock is held by the caller.func ( *recordingSpan) ( string, ...trace.EventOption) { := trace.NewEventConfig(...) := Event{Name: , Attributes: .Attributes(), Time: .Timestamp()}// Discard attributes over limit. := .tracer.provider.spanLimits.AttributePerEventCountLimitif == 0 {// Drop all attributes. .DroppedAttributeCount = len(.Attributes) .Attributes = nil } elseif > 0 && len(.Attributes) > {// Drop over capacity. .DroppedAttributeCount = len(.Attributes) - .Attributes = .Attributes[:] } .events.add()}// SetName sets the name of this span. If this span is not being recorded than// this method does nothing.func ( *recordingSpan) ( string) {if == nil {return } .mu.Lock()defer .mu.Unlock()if !.isRecording() {return } .name = }// Name returns the name of this span.func ( *recordingSpan) () string { .mu.Lock()defer .mu.Unlock()return .name}// Name returns the SpanContext of this span's parent span.func ( *recordingSpan) () trace.SpanContext { .mu.Lock()defer .mu.Unlock()return .parent}// SpanKind returns the SpanKind of this span.func ( *recordingSpan) () trace.SpanKind { .mu.Lock()defer .mu.Unlock()return .spanKind}// StartTime returns the time this span started.func ( *recordingSpan) () time.Time { .mu.Lock()defer .mu.Unlock()return .startTime}// 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 ( *recordingSpan) () time.Time { .mu.Lock()defer .mu.Unlock()return .endTime}// Attributes returns the attributes of this span.//// The order of the returned attributes is not guaranteed to be stable.func ( *recordingSpan) () []attribute.KeyValue { .mu.Lock()defer .mu.Unlock() .dedupeAttrs()return .attributes}// dedupeAttrs deduplicates the attributes of s to fit capacity.//// This method assumes s.mu.Lock is held by the caller.func ( *recordingSpan) () {// Do not set a capacity when creating this map. Benchmark testing has // showed this to only add unused memory allocations in general use. := make(map[attribute.Key]int, len(.attributes)) .dedupeAttrsFromRecord()}// dedupeAttrsFromRecord deduplicates the attributes of s to fit capacity// using record as the record of unique attribute keys to their index.//// This method assumes s.mu.Lock is held by the caller.func ( *recordingSpan) ( map[attribute.Key]int) {// Use the fact that slices share the same backing array. := .attributes[:0]for , := range .attributes {if , := [.Key]; { [] = } else { = append(, ) [.Key] = len() - 1 } }clear(.attributes[len():]) // Erase unneeded elements to let GC collect objects. .attributes = }// Links returns the links of this span.func ( *recordingSpan) () []Link { .mu.Lock()defer .mu.Unlock()iflen(.links.queue) == 0 {return []Link{} }return .links.copy()}// Events returns the events of this span.func ( *recordingSpan) () []Event { .mu.Lock()defer .mu.Unlock()iflen(.events.queue) == 0 {return []Event{} }return .events.copy()}// Status returns the status of this span.func ( *recordingSpan) () Status { .mu.Lock()defer .mu.Unlock()return .status}// InstrumentationScope returns the instrumentation.Scope associated with// the Tracer that created this span.func ( *recordingSpan) () instrumentation.Scope { .mu.Lock()defer .mu.Unlock()return .tracer.instrumentationScope}// InstrumentationLibrary returns the instrumentation.Library associated with// the Tracer that created this span.func ( *recordingSpan) () instrumentation.Library { //nolint:staticcheck // This method needs to be define for backwards compatibility .mu.Lock()defer .mu.Unlock()return .tracer.instrumentationScope}// Resource returns the Resource associated with the Tracer that created this// span.func ( *recordingSpan) () *resource.Resource { .mu.Lock()defer .mu.Unlock()return .tracer.provider.resource}func ( *recordingSpan) ( trace.Link) {if == nil {return }if !.SpanContext.IsValid() && len(.Attributes) == 0 && .SpanContext.TraceState().Len() == 0 {return } .mu.Lock()defer .mu.Unlock()if !.isRecording() {return } := Link{SpanContext: .SpanContext, Attributes: .Attributes}// Discard attributes over limit. := .tracer.provider.spanLimits.AttributePerLinkCountLimitif == 0 {// Drop all attributes. .DroppedAttributeCount = len(.Attributes) .Attributes = nil } elseif > 0 && len(.Attributes) > { .DroppedAttributeCount = len(.Attributes) - .Attributes = .Attributes[:] } .links.add()}// DroppedAttributes returns the number of attributes dropped by the span// due to limits being reached.func ( *recordingSpan) () int { .mu.Lock()defer .mu.Unlock()return .droppedAttributes}// DroppedLinks returns the number of links dropped by the span due to limits// being reached.func ( *recordingSpan) () int { .mu.Lock()defer .mu.Unlock()return .links.droppedCount}// DroppedEvents returns the number of events dropped by the span due to// limits being reached.func ( *recordingSpan) () int { .mu.Lock()defer .mu.Unlock()return .events.droppedCount}// ChildSpanCount returns the count of spans that consider the span a// direct parent.func ( *recordingSpan) () int { .mu.Lock()defer .mu.Unlock()return .childSpanCount}// TracerProvider returns a trace.TracerProvider that can be used to generate// additional Spans on the same telemetry pipeline as the current Span.func ( *recordingSpan) () trace.TracerProvider {return .tracer.provider}// snapshot creates a read-only copy of the current state of the span.func ( *recordingSpan) () ReadOnlySpan {varsnapshot .mu.Lock()defer .mu.Unlock() .endTime = .endTime .instrumentationScope = .tracer.instrumentationScope .name = .name .parent = .parent .resource = .tracer.provider.resource .spanContext = .spanContext .spanKind = .spanKind .startTime = .startTime .status = .status .childSpanCount = .childSpanCountiflen(.attributes) > 0 { .dedupeAttrs() .attributes = .attributes } .droppedAttributeCount = .droppedAttributesiflen(.events.queue) > 0 { .events = .events.copy() .droppedEventCount = .events.droppedCount }iflen(.links.queue) > 0 { .links = .links.copy() .droppedLinkCount = .links.droppedCount }return &}func ( *recordingSpan) () {if == nil {return } .mu.Lock()defer .mu.Unlock()if !.isRecording() {return } .childSpanCount++}func (*recordingSpan) () {}// runtimeTrace starts a "runtime/trace".Task for the span and returns a// context containing the task.func ( *recordingSpan) ( context.Context) context.Context {if !rt.IsEnabled() {// Avoid additional overhead if runtime/trace is not enabled.return } , := rt.NewTask(, .name) .mu.Lock() .executionTracerTaskEnd = .End .mu.Unlock()return}// 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 {embedded.Span// tracer is the SDK tracer that created this span. tracer *tracer sc trace.SpanContext}var _ trace.Span = nonRecordingSpan{}// SpanContext returns the wrapped SpanContext.func ( nonRecordingSpan) () trace.SpanContext { return .sc }// IsRecording always returns false.func (nonRecordingSpan) () bool { returnfalse }// SetStatus does nothing.func (nonRecordingSpan) (codes.Code, string) {}// SetError does nothing.func (nonRecordingSpan) (bool) {}// SetAttributes does nothing.func (nonRecordingSpan) (...attribute.KeyValue) {}// End does nothing.func (nonRecordingSpan) (...trace.SpanEndOption) {}// RecordError does nothing.func (nonRecordingSpan) (error, ...trace.EventOption) {}// AddEvent does nothing.func (nonRecordingSpan) (string, ...trace.EventOption) {}// AddLink does nothing.func (nonRecordingSpan) (trace.Link) {}// SetName does nothing.func (nonRecordingSpan) (string) {}// TracerProvider returns the trace.TracerProvider that provided the Tracer// that created this span.func ( nonRecordingSpan) () trace.TracerProvider { return .tracer.provider }func isRecording( SamplingResult) bool {return .Decision == RecordOnly || .Decision == RecordAndSample}func isSampled( SamplingResult) bool {return .Decision == RecordAndSample}// Status is the classified state of a Span.typeStatusstruct {// Code is an identifier of a Spans state classification. Code codes.Code// Description is a user hint about why that status was set. It is only // applicable when Code is Error. Description string}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.