// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package trace // import "go.opentelemetry.io/otel/trace"

import (
	
	
	
	
	
	
	
	
	
	
	
	
	

	
	
	semconv 
	
	
)

// newAutoTracerProvider returns an auto-instrumentable [trace.TracerProvider].
// If an [go.opentelemetry.io/auto.Instrumentation] is configured to instrument
// the process using the returned TracerProvider, all of the telemetry it
// produces will be processed and handled by that Instrumentation. By default,
// if no Instrumentation instruments the TracerProvider it will not generate
// any trace telemetry.
func newAutoTracerProvider() TracerProvider { return tracerProviderInstance }

var tracerProviderInstance = new(autoTracerProvider)

type autoTracerProvider struct{ embedded.TracerProvider }

var _ TracerProvider = autoTracerProvider{}

func (autoTracerProvider) ( string,  ...TracerOption) Tracer {
	 := NewTracerConfig(...)
	return autoTracer{
		name:      ,
		version:   .InstrumentationVersion(),
		schemaURL: .SchemaURL(),
	}
}

type autoTracer struct {
	embedded.Tracer

	name, schemaURL, version string
}

var _ Tracer = autoTracer{}

func ( autoTracer) ( context.Context,  string,  ...SpanStartOption) (context.Context, Span) {
	var ,  SpanContext
	 := true
	 := new(autoSpan)

	// Ask eBPF for sampling decision and span context info.
	.start(, , &, &, &)

	.sampled.Store()
	.spanContext = 

	 = ContextWithSpan(, )

	if  {
		// Only build traces if sampled.
		 := NewSpanStartConfig(...)
		.traces, .span = .traces(, , .spanContext, )
	}

	return , 
}

// Expected to be implemented in eBPF.
//
//go:noinline
func (*autoTracer) (
	 context.Context,
	 *autoSpan,
	 *SpanContext,
	 *bool,
	 *SpanContext,
) {
	start(, , , , )
}

// start is used for testing.
var start = func(context.Context, *autoSpan, *SpanContext, *bool, *SpanContext) {}

func ( autoTracer) ( string,  SpanConfig, ,  SpanContext) (*telemetry.Traces, *telemetry.Span) {
	 := &telemetry.Span{
		TraceID:      telemetry.TraceID(.TraceID()),
		SpanID:       telemetry.SpanID(.SpanID()),
		Flags:        uint32(.TraceFlags()),
		TraceState:   .TraceState().String(),
		ParentSpanID: telemetry.SpanID(.SpanID()),
		Name:         ,
		Kind:         spanKind(.SpanKind()),
	}

	.Attrs, .DroppedAttrs = convCappedAttrs(maxSpan.Attrs, .Attributes())

	 := .Links()
	if  := maxSpan.Links;  == 0 {
		 := int64(len())
		if  > 0 {
			.DroppedLinks = uint32(min(, math.MaxUint32)) // nolint: gosec  // Bounds checked.
		}
	} else {
		if  > 0 {
			 := int64(max(len()-, 0))
			.DroppedLinks = uint32(min(, math.MaxUint32)) // nolint: gosec  // Bounds checked.
			 = [:]
		}
		.Links = convLinks()
	}

	if  := .Timestamp(); !.IsZero() {
		.StartTime = .Timestamp()
	} else {
		.StartTime = time.Now()
	}

	return &telemetry.Traces{
		ResourceSpans: []*telemetry.ResourceSpans{
			{
				ScopeSpans: []*telemetry.ScopeSpans{
					{
						Scope: &telemetry.Scope{
							Name:    .name,
							Version: .version,
						},
						Spans:     []*telemetry.Span{},
						SchemaURL: .schemaURL,
					},
				},
			},
		},
	}, 
}

func spanKind( SpanKind) telemetry.SpanKind {
	switch  {
	case SpanKindInternal:
		return telemetry.SpanKindInternal
	case SpanKindServer:
		return telemetry.SpanKindServer
	case SpanKindClient:
		return telemetry.SpanKindClient
	case SpanKindProducer:
		return telemetry.SpanKindProducer
	case SpanKindConsumer:
		return telemetry.SpanKindConsumer
	}
	return telemetry.SpanKind(0) // undefined.
}

type autoSpan struct {
	embedded.Span

	spanContext SpanContext
	sampled     atomic.Bool

	mu     sync.Mutex
	traces *telemetry.Traces
	span   *telemetry.Span
}

func ( *autoSpan) () SpanContext {
	if  == nil {
		return SpanContext{}
	}
	// s.spanContext is immutable, do not acquire lock s.mu.
	return .spanContext
}

func ( *autoSpan) () bool {
	if  == nil {
		return false
	}

	return .sampled.Load()
}

func ( *autoSpan) ( codes.Code,  string) {
	if  == nil || !.sampled.Load() {
		return
	}

	.mu.Lock()
	defer .mu.Unlock()

	if .span.Status == nil {
		.span.Status = new(telemetry.Status)
	}

	.span.Status.Message = 

	switch  {
	case codes.Unset:
		.span.Status.Code = telemetry.StatusCodeUnset
	case codes.Error:
		.span.Status.Code = telemetry.StatusCodeError
	case codes.Ok:
		.span.Status.Code = telemetry.StatusCodeOK
	}
}

func ( *autoSpan) ( ...attribute.KeyValue) {
	if  == nil || !.sampled.Load() {
		return
	}

	.mu.Lock()
	defer .mu.Unlock()

	 := maxSpan.Attrs
	if  == 0 {
		// No attributes allowed.
		 := int64(len())
		if  > 0 {
			.span.DroppedAttrs += uint32(min(, math.MaxUint32)) // nolint: gosec  // Bounds checked.
		}
		return
	}

	 := make(map[string]int)
	for ,  := range .span.Attrs {
		[.Key] = 
	}

	for ,  := range  {
		 := convAttrValue(.Value)
		if .Empty() {
			.span.DroppedAttrs++
			continue
		}

		if ,  := [string(.Key)];  {
			.span.Attrs[] = telemetry.Attr{
				Key:   string(.Key),
				Value: ,
			}
		} else if  < 0 || len(.span.Attrs) <  {
			.span.Attrs = append(.span.Attrs, telemetry.Attr{
				Key:   string(.Key),
				Value: ,
			})
			[string(.Key)] = len(.span.Attrs) - 1
		} else {
			.span.DroppedAttrs++
		}
	}
}

// convCappedAttrs converts up to limit attrs into a []telemetry.Attr. The
// number of dropped attributes is also returned.
func convCappedAttrs( int,  []attribute.KeyValue) ([]telemetry.Attr, uint32) {
	 := len()
	if  == 0 {
		var  uint32
		if  > 0 {
			 = uint32(min(int64(), math.MaxUint32)) // nolint: gosec  // Bounds checked.
		}
		return nil, 
	}

	if  < 0 {
		// Unlimited.
		return convAttrs(), 0
	}

	if  < 0 {
		 = 0
	}

	 = min(, )
	return convAttrs([:]), uint32( - ) // nolint: gosec  // Bounds checked.
}

func convAttrs( []attribute.KeyValue) []telemetry.Attr {
	if len() == 0 {
		// Avoid allocations if not necessary.
		return nil
	}

	 := make([]telemetry.Attr, 0, len())
	for ,  := range  {
		 := string(.Key)
		 := convAttrValue(.Value)
		if .Empty() {
			continue
		}
		 = append(, telemetry.Attr{Key: , Value: })
	}
	return 
}

func convAttrValue( attribute.Value) telemetry.Value {
	switch .Type() {
	case attribute.BOOL:
		return telemetry.BoolValue(.AsBool())
	case attribute.INT64:
		return telemetry.Int64Value(.AsInt64())
	case attribute.FLOAT64:
		return telemetry.Float64Value(.AsFloat64())
	case attribute.STRING:
		 := truncate(maxSpan.AttrValueLen, .AsString())
		return telemetry.StringValue()
	case attribute.BOOLSLICE:
		 := .AsBoolSlice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = append(, telemetry.BoolValue())
		}
		return telemetry.SliceValue(...)
	case attribute.INT64SLICE:
		 := .AsInt64Slice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = append(, telemetry.Int64Value())
		}
		return telemetry.SliceValue(...)
	case attribute.FLOAT64SLICE:
		 := .AsFloat64Slice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = append(, telemetry.Float64Value())
		}
		return telemetry.SliceValue(...)
	case attribute.STRINGSLICE:
		 := .AsStringSlice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = truncate(maxSpan.AttrValueLen, )
			 = append(, telemetry.StringValue())
		}
		return telemetry.SliceValue(...)
	}
	return telemetry.Value{}
}

// 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.
	var  strings.Builder
	 := 0
	for ,  := 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()
}

func ( *autoSpan) ( ...SpanEndOption) {
	if  == nil || !.sampled.Swap(false) {
		return
	}

	// s.end exists so the lock (s.mu) is not held while s.ended is called.
	.ended(.end())
}

func ( *autoSpan) ( []SpanEndOption) []byte {
	.mu.Lock()
	defer .mu.Unlock()

	 := NewSpanEndConfig(...)
	if  := .Timestamp(); !.IsZero() {
		.span.EndTime = .Timestamp()
	} else {
		.span.EndTime = time.Now()
	}

	,  := json.Marshal(.traces) // TODO: do not ignore this error.
	return 
}

// Expected to be implemented in eBPF.
//
//go:noinline
func (*autoSpan) ( []byte) { ended() }

// ended is used for testing.
var ended = func([]byte) {}

func ( *autoSpan) ( error,  ...EventOption) {
	if  == nil ||  == nil || !.sampled.Load() {
		return
	}

	 := NewEventConfig(...)

	 := .Attributes()
	 = append(,
		semconv.ExceptionType(typeStr()),
		semconv.ExceptionMessage(.Error()),
	)
	if .StackTrace() {
		 := make([]byte, 2048)
		 := runtime.Stack(, false)
		 = append(, semconv.ExceptionStacktrace(string([0:])))
	}

	.mu.Lock()
	defer .mu.Unlock()

	.addEvent(semconv.ExceptionEventName, .Timestamp(), )
}

func typeStr( any) string {
	 := reflect.TypeOf()
	if .PkgPath() == "" && .Name() == "" {
		// Likely a builtin type.
		return .String()
	}
	return fmt.Sprintf("%s.%s", .PkgPath(), .Name())
}

func ( *autoSpan) ( string,  ...EventOption) {
	if  == nil || !.sampled.Load() {
		return
	}

	 := NewEventConfig(...)

	.mu.Lock()
	defer .mu.Unlock()

	.addEvent(, .Timestamp(), .Attributes())
}

// addEvent adds an event with name and attrs at tStamp to the span. The span
// lock (s.mu) needs to be held by the caller.
func ( *autoSpan) ( string,  time.Time,  []attribute.KeyValue) {
	 := maxSpan.Events

	if  == 0 {
		.span.DroppedEvents++
		return
	}

	if  > 0 && len(.span.Events) ==  {
		// Drop head while avoiding allocation of more capacity.
		copy(.span.Events[:-1], .span.Events[1:])
		.span.Events = .span.Events[:-1]
		.span.DroppedEvents++
	}

	 := &telemetry.SpanEvent{Time: , Name: }
	.Attrs, .DroppedAttrs = convCappedAttrs(maxSpan.EventAttrs, )

	.span.Events = append(.span.Events, )
}

func ( *autoSpan) ( Link) {
	if  == nil || !.sampled.Load() {
		return
	}

	 := maxSpan.Links

	.mu.Lock()
	defer .mu.Unlock()

	if  == 0 {
		.span.DroppedLinks++
		return
	}

	if  > 0 && len(.span.Links) ==  {
		// Drop head while avoiding allocation of more capacity.
		copy(.span.Links[:-1], .span.Links[1:])
		.span.Links = .span.Links[:-1]
		.span.DroppedLinks++
	}

	.span.Links = append(.span.Links, convLink())
}

func convLinks( []Link) []*telemetry.SpanLink {
	 := make([]*telemetry.SpanLink, 0, len())
	for ,  := range  {
		 = append(, convLink())
	}
	return 
}

func convLink( Link) *telemetry.SpanLink {
	 := &telemetry.SpanLink{
		TraceID:    telemetry.TraceID(.SpanContext.TraceID()),
		SpanID:     telemetry.SpanID(.SpanContext.SpanID()),
		TraceState: .SpanContext.TraceState().String(),
		Flags:      uint32(.SpanContext.TraceFlags()),
	}
	.Attrs, .DroppedAttrs = convCappedAttrs(maxSpan.LinkAttrs, .Attributes)

	return 
}

func ( *autoSpan) ( string) {
	if  == nil || !.sampled.Load() {
		return
	}

	.mu.Lock()
	defer .mu.Unlock()

	.span.Name = 
}

func (*autoSpan) () TracerProvider { return newAutoTracerProvider() }

// maxSpan are the span limits resolved during startup.
var maxSpan = newSpanLimits()

type spanLimits struct {
	// Attrs is the number of allowed attributes for a span.
	//
	// This is resolved from the environment variable value for the
	// OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the
	// environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT, or 128 if
	// that is not set, is used.
	Attrs int
	// AttrValueLen is the maximum attribute value length allowed for a span.
	//
	// This is resolved from the environment variable value for the
	// OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the
	// environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, or -1
	// if that is not set, is used.
	AttrValueLen int
	// Events is the number of allowed events for a span.
	//
	// This is resolved from the environment variable value for the
	// OTEL_SPAN_EVENT_COUNT_LIMIT key, or 128 is used if that is not set.
	Events int
	// EventAttrs is the number of allowed attributes for a span event.
	//
	// The is resolved from the environment variable value for the
	// OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key, or 128 is used if that is not set.
	EventAttrs int
	// Links is the number of allowed Links for a span.
	//
	// This is resolved from the environment variable value for the
	// OTEL_SPAN_LINK_COUNT_LIMIT, or 128 is used if that is not set.
	Links int
	// LinkAttrs is the number of allowed attributes for a span link.
	//
	// This is resolved from the environment variable value for the
	// OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, or 128 is used if that is not set.
	LinkAttrs int
}

func newSpanLimits() spanLimits {
	return spanLimits{
		Attrs: firstEnv(
			128,
			"OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT",
			"OTEL_ATTRIBUTE_COUNT_LIMIT",
		),
		AttrValueLen: firstEnv(
			-1, // Unlimited.
			"OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT",
			"OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT",
		),
		Events:     firstEnv(128, "OTEL_SPAN_EVENT_COUNT_LIMIT"),
		EventAttrs: firstEnv(128, "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT"),
		Links:      firstEnv(128, "OTEL_SPAN_LINK_COUNT_LIMIT"),
		LinkAttrs:  firstEnv(128, "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT"),
	}
}

// firstEnv returns the parsed integer value of the first matching environment
// variable from keys. The defaultVal is returned if the value is not an
// integer or no match is found.
func firstEnv( int,  ...string) int {
	for ,  := range  {
		 := os.Getenv()
		if  == "" {
			continue
		}

		,  := strconv.Atoi()
		if  == nil {
			return 
		}
		// Ignore invalid environment variable.
	}

	return 
}