mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 23:02:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			294 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			294 lines
		
	
	
	
		
			8.5 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" | ||
|  | 	"encoding/binary" | ||
|  | 	"fmt" | ||
|  | 
 | ||
|  | 	"go.opentelemetry.io/otel/attribute" | ||
|  | 	"go.opentelemetry.io/otel/trace" | ||
|  | ) | ||
|  | 
 | ||
|  | // Sampler decides whether a trace should be sampled and exported. | ||
|  | type Sampler interface { | ||
|  | 	// DO NOT CHANGE: any modification will not be backwards compatible and | ||
|  | 	// must never be done outside of a new major release. | ||
|  | 
 | ||
|  | 	// ShouldSample returns a SamplingResult based on a decision made from the | ||
|  | 	// passed parameters. | ||
|  | 	ShouldSample(parameters SamplingParameters) SamplingResult | ||
|  | 	// DO NOT CHANGE: any modification will not be backwards compatible and | ||
|  | 	// must never be done outside of a new major release. | ||
|  | 
 | ||
|  | 	// Description returns information describing the Sampler. | ||
|  | 	Description() string | ||
|  | 	// DO NOT CHANGE: any modification will not be backwards compatible and | ||
|  | 	// must never be done outside of a new major release. | ||
|  | } | ||
|  | 
 | ||
|  | // SamplingParameters contains the values passed to a Sampler. | ||
|  | type SamplingParameters struct { | ||
|  | 	ParentContext context.Context | ||
|  | 	TraceID       trace.TraceID | ||
|  | 	Name          string | ||
|  | 	Kind          trace.SpanKind | ||
|  | 	Attributes    []attribute.KeyValue | ||
|  | 	Links         []trace.Link | ||
|  | } | ||
|  | 
 | ||
|  | // SamplingDecision indicates whether a span is dropped, recorded and/or sampled. | ||
|  | type SamplingDecision uint8 | ||
|  | 
 | ||
|  | // Valid sampling decisions. | ||
|  | const ( | ||
|  | 	// Drop will not record the span and all attributes/events will be dropped. | ||
|  | 	Drop SamplingDecision = iota | ||
|  | 
 | ||
|  | 	// Record indicates the span's `IsRecording() == true`, but `Sampled` flag | ||
|  | 	// *must not* be set. | ||
|  | 	RecordOnly | ||
|  | 
 | ||
|  | 	// RecordAndSample has span's `IsRecording() == true` and `Sampled` flag | ||
|  | 	// *must* be set. | ||
|  | 	RecordAndSample | ||
|  | ) | ||
|  | 
 | ||
|  | // SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate. | ||
|  | type SamplingResult struct { | ||
|  | 	Decision   SamplingDecision | ||
|  | 	Attributes []attribute.KeyValue | ||
|  | 	Tracestate trace.TraceState | ||
|  | } | ||
|  | 
 | ||
|  | type traceIDRatioSampler struct { | ||
|  | 	traceIDUpperBound uint64 | ||
|  | 	description       string | ||
|  | } | ||
|  | 
 | ||
|  | func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult { | ||
|  | 	psc := trace.SpanContextFromContext(p.ParentContext) | ||
|  | 	x := binary.BigEndian.Uint64(p.TraceID[8:16]) >> 1 | ||
|  | 	if x < ts.traceIDUpperBound { | ||
|  | 		return SamplingResult{ | ||
|  | 			Decision:   RecordAndSample, | ||
|  | 			Tracestate: psc.TraceState(), | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return SamplingResult{ | ||
|  | 		Decision:   Drop, | ||
|  | 		Tracestate: psc.TraceState(), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (ts traceIDRatioSampler) Description() string { | ||
|  | 	return ts.description | ||
|  | } | ||
|  | 
 | ||
|  | // TraceIDRatioBased samples a given fraction of traces. Fractions >= 1 will | ||
|  | // always sample. Fractions < 0 are treated as zero. To respect the | ||
|  | // parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used | ||
|  | // as a delegate of a `Parent` sampler. | ||
|  | // | ||
|  | //nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased` | ||
|  | func TraceIDRatioBased(fraction float64) Sampler { | ||
|  | 	if fraction >= 1 { | ||
|  | 		return AlwaysSample() | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if fraction <= 0 { | ||
|  | 		fraction = 0 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return &traceIDRatioSampler{ | ||
|  | 		traceIDUpperBound: uint64(fraction * (1 << 63)), | ||
|  | 		description:       fmt.Sprintf("TraceIDRatioBased{%g}", fraction), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | type alwaysOnSampler struct{} | ||
|  | 
 | ||
|  | func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult { | ||
|  | 	return SamplingResult{ | ||
|  | 		Decision:   RecordAndSample, | ||
|  | 		Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (as alwaysOnSampler) Description() string { | ||
|  | 	return "AlwaysOnSampler" | ||
|  | } | ||
|  | 
 | ||
|  | // AlwaysSample returns a Sampler that samples every trace. | ||
|  | // Be careful about using this sampler in a production application with | ||
|  | // significant traffic: a new trace will be started and exported for every | ||
|  | // request. | ||
|  | func AlwaysSample() Sampler { | ||
|  | 	return alwaysOnSampler{} | ||
|  | } | ||
|  | 
 | ||
|  | type alwaysOffSampler struct{} | ||
|  | 
 | ||
|  | func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult { | ||
|  | 	return SamplingResult{ | ||
|  | 		Decision:   Drop, | ||
|  | 		Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (as alwaysOffSampler) Description() string { | ||
|  | 	return "AlwaysOffSampler" | ||
|  | } | ||
|  | 
 | ||
|  | // NeverSample returns a Sampler that samples no traces. | ||
|  | func NeverSample() Sampler { | ||
|  | 	return alwaysOffSampler{} | ||
|  | } | ||
|  | 
 | ||
|  | // ParentBased returns a composite sampler which behaves differently, | ||
|  | // based on the parent of the span. If the span has no parent, | ||
|  | // the root(Sampler) is used to make sampling decision. If the span has | ||
|  | // a parent, depending on whether the parent is remote and whether it | ||
|  | // is sampled, one of the following samplers will apply: | ||
|  | //   - remoteParentSampled(Sampler) (default: AlwaysOn) | ||
|  | //   - remoteParentNotSampled(Sampler) (default: AlwaysOff) | ||
|  | //   - localParentSampled(Sampler) (default: AlwaysOn) | ||
|  | //   - localParentNotSampled(Sampler) (default: AlwaysOff) | ||
|  | func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler { | ||
|  | 	return parentBased{ | ||
|  | 		root:   root, | ||
|  | 		config: configureSamplersForParentBased(samplers), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | type parentBased struct { | ||
|  | 	root   Sampler | ||
|  | 	config samplerConfig | ||
|  | } | ||
|  | 
 | ||
|  | func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) samplerConfig { | ||
|  | 	c := samplerConfig{ | ||
|  | 		remoteParentSampled:    AlwaysSample(), | ||
|  | 		remoteParentNotSampled: NeverSample(), | ||
|  | 		localParentSampled:     AlwaysSample(), | ||
|  | 		localParentNotSampled:  NeverSample(), | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for _, so := range samplers { | ||
|  | 		c = so.apply(c) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return c | ||
|  | } | ||
|  | 
 | ||
|  | // samplerConfig is a group of options for parentBased sampler. | ||
|  | type samplerConfig struct { | ||
|  | 	remoteParentSampled, remoteParentNotSampled Sampler | ||
|  | 	localParentSampled, localParentNotSampled   Sampler | ||
|  | } | ||
|  | 
 | ||
|  | // ParentBasedSamplerOption configures the sampler for a particular sampling case. | ||
|  | type ParentBasedSamplerOption interface { | ||
|  | 	apply(samplerConfig) samplerConfig | ||
|  | } | ||
|  | 
 | ||
|  | // WithRemoteParentSampled sets the sampler for the case of sampled remote parent. | ||
|  | func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption { | ||
|  | 	return remoteParentSampledOption{s} | ||
|  | } | ||
|  | 
 | ||
|  | type remoteParentSampledOption struct { | ||
|  | 	s Sampler | ||
|  | } | ||
|  | 
 | ||
|  | func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig { | ||
|  | 	config.remoteParentSampled = o.s | ||
|  | 	return config | ||
|  | } | ||
|  | 
 | ||
|  | // WithRemoteParentNotSampled sets the sampler for the case of remote parent | ||
|  | // which is not sampled. | ||
|  | func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption { | ||
|  | 	return remoteParentNotSampledOption{s} | ||
|  | } | ||
|  | 
 | ||
|  | type remoteParentNotSampledOption struct { | ||
|  | 	s Sampler | ||
|  | } | ||
|  | 
 | ||
|  | func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig { | ||
|  | 	config.remoteParentNotSampled = o.s | ||
|  | 	return config | ||
|  | } | ||
|  | 
 | ||
|  | // WithLocalParentSampled sets the sampler for the case of sampled local parent. | ||
|  | func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption { | ||
|  | 	return localParentSampledOption{s} | ||
|  | } | ||
|  | 
 | ||
|  | type localParentSampledOption struct { | ||
|  | 	s Sampler | ||
|  | } | ||
|  | 
 | ||
|  | func (o localParentSampledOption) apply(config samplerConfig) samplerConfig { | ||
|  | 	config.localParentSampled = o.s | ||
|  | 	return config | ||
|  | } | ||
|  | 
 | ||
|  | // WithLocalParentNotSampled sets the sampler for the case of local parent | ||
|  | // which is not sampled. | ||
|  | func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOption { | ||
|  | 	return localParentNotSampledOption{s} | ||
|  | } | ||
|  | 
 | ||
|  | type localParentNotSampledOption struct { | ||
|  | 	s Sampler | ||
|  | } | ||
|  | 
 | ||
|  | func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig { | ||
|  | 	config.localParentNotSampled = o.s | ||
|  | 	return config | ||
|  | } | ||
|  | 
 | ||
|  | func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult { | ||
|  | 	psc := trace.SpanContextFromContext(p.ParentContext) | ||
|  | 	if psc.IsValid() { | ||
|  | 		if psc.IsRemote() { | ||
|  | 			if psc.IsSampled() { | ||
|  | 				return pb.config.remoteParentSampled.ShouldSample(p) | ||
|  | 			} | ||
|  | 			return pb.config.remoteParentNotSampled.ShouldSample(p) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if psc.IsSampled() { | ||
|  | 			return pb.config.localParentSampled.ShouldSample(p) | ||
|  | 		} | ||
|  | 		return pb.config.localParentNotSampled.ShouldSample(p) | ||
|  | 	} | ||
|  | 	return pb.root.ShouldSample(p) | ||
|  | } | ||
|  | 
 | ||
|  | func (pb parentBased) Description() string { | ||
|  | 	return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+ | ||
|  | 		"remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}", | ||
|  | 		pb.root.Description(), | ||
|  | 		pb.config.remoteParentSampled.Description(), | ||
|  | 		pb.config.remoteParentNotSampled.Description(), | ||
|  | 		pb.config.localParentSampled.Description(), | ||
|  | 		pb.config.localParentNotSampled.Description(), | ||
|  | 	) | ||
|  | } |