gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

sampling.go (8683B)


      1 // Copyright The OpenTelemetry Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package trace // import "go.opentelemetry.io/otel/sdk/trace"
     16 
     17 import (
     18 	"context"
     19 	"encoding/binary"
     20 	"fmt"
     21 
     22 	"go.opentelemetry.io/otel/attribute"
     23 	"go.opentelemetry.io/otel/trace"
     24 )
     25 
     26 // Sampler decides whether a trace should be sampled and exported.
     27 type Sampler interface {
     28 	// DO NOT CHANGE: any modification will not be backwards compatible and
     29 	// must never be done outside of a new major release.
     30 
     31 	// ShouldSample returns a SamplingResult based on a decision made from the
     32 	// passed parameters.
     33 	ShouldSample(parameters SamplingParameters) SamplingResult
     34 	// DO NOT CHANGE: any modification will not be backwards compatible and
     35 	// must never be done outside of a new major release.
     36 
     37 	// Description returns information describing the Sampler.
     38 	Description() string
     39 	// DO NOT CHANGE: any modification will not be backwards compatible and
     40 	// must never be done outside of a new major release.
     41 }
     42 
     43 // SamplingParameters contains the values passed to a Sampler.
     44 type SamplingParameters struct {
     45 	ParentContext context.Context
     46 	TraceID       trace.TraceID
     47 	Name          string
     48 	Kind          trace.SpanKind
     49 	Attributes    []attribute.KeyValue
     50 	Links         []trace.Link
     51 }
     52 
     53 // SamplingDecision indicates whether a span is dropped, recorded and/or sampled.
     54 type SamplingDecision uint8
     55 
     56 // Valid sampling decisions.
     57 const (
     58 	// Drop will not record the span and all attributes/events will be dropped.
     59 	Drop SamplingDecision = iota
     60 
     61 	// Record indicates the span's `IsRecording() == true`, but `Sampled` flag
     62 	// *must not* be set.
     63 	RecordOnly
     64 
     65 	// RecordAndSample has span's `IsRecording() == true` and `Sampled` flag
     66 	// *must* be set.
     67 	RecordAndSample
     68 )
     69 
     70 // SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate.
     71 type SamplingResult struct {
     72 	Decision   SamplingDecision
     73 	Attributes []attribute.KeyValue
     74 	Tracestate trace.TraceState
     75 }
     76 
     77 type traceIDRatioSampler struct {
     78 	traceIDUpperBound uint64
     79 	description       string
     80 }
     81 
     82 func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult {
     83 	psc := trace.SpanContextFromContext(p.ParentContext)
     84 	x := binary.BigEndian.Uint64(p.TraceID[8:16]) >> 1
     85 	if x < ts.traceIDUpperBound {
     86 		return SamplingResult{
     87 			Decision:   RecordAndSample,
     88 			Tracestate: psc.TraceState(),
     89 		}
     90 	}
     91 	return SamplingResult{
     92 		Decision:   Drop,
     93 		Tracestate: psc.TraceState(),
     94 	}
     95 }
     96 
     97 func (ts traceIDRatioSampler) Description() string {
     98 	return ts.description
     99 }
    100 
    101 // TraceIDRatioBased samples a given fraction of traces. Fractions >= 1 will
    102 // always sample. Fractions < 0 are treated as zero. To respect the
    103 // parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used
    104 // as a delegate of a `Parent` sampler.
    105 //
    106 //nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
    107 func TraceIDRatioBased(fraction float64) Sampler {
    108 	if fraction >= 1 {
    109 		return AlwaysSample()
    110 	}
    111 
    112 	if fraction <= 0 {
    113 		fraction = 0
    114 	}
    115 
    116 	return &traceIDRatioSampler{
    117 		traceIDUpperBound: uint64(fraction * (1 << 63)),
    118 		description:       fmt.Sprintf("TraceIDRatioBased{%g}", fraction),
    119 	}
    120 }
    121 
    122 type alwaysOnSampler struct{}
    123 
    124 func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult {
    125 	return SamplingResult{
    126 		Decision:   RecordAndSample,
    127 		Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
    128 	}
    129 }
    130 
    131 func (as alwaysOnSampler) Description() string {
    132 	return "AlwaysOnSampler"
    133 }
    134 
    135 // AlwaysSample returns a Sampler that samples every trace.
    136 // Be careful about using this sampler in a production application with
    137 // significant traffic: a new trace will be started and exported for every
    138 // request.
    139 func AlwaysSample() Sampler {
    140 	return alwaysOnSampler{}
    141 }
    142 
    143 type alwaysOffSampler struct{}
    144 
    145 func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult {
    146 	return SamplingResult{
    147 		Decision:   Drop,
    148 		Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
    149 	}
    150 }
    151 
    152 func (as alwaysOffSampler) Description() string {
    153 	return "AlwaysOffSampler"
    154 }
    155 
    156 // NeverSample returns a Sampler that samples no traces.
    157 func NeverSample() Sampler {
    158 	return alwaysOffSampler{}
    159 }
    160 
    161 // ParentBased returns a composite sampler which behaves differently,
    162 // based on the parent of the span. If the span has no parent,
    163 // the root(Sampler) is used to make sampling decision. If the span has
    164 // a parent, depending on whether the parent is remote and whether it
    165 // is sampled, one of the following samplers will apply:
    166 //   - remoteParentSampled(Sampler) (default: AlwaysOn)
    167 //   - remoteParentNotSampled(Sampler) (default: AlwaysOff)
    168 //   - localParentSampled(Sampler) (default: AlwaysOn)
    169 //   - localParentNotSampled(Sampler) (default: AlwaysOff)
    170 func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler {
    171 	return parentBased{
    172 		root:   root,
    173 		config: configureSamplersForParentBased(samplers),
    174 	}
    175 }
    176 
    177 type parentBased struct {
    178 	root   Sampler
    179 	config samplerConfig
    180 }
    181 
    182 func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) samplerConfig {
    183 	c := samplerConfig{
    184 		remoteParentSampled:    AlwaysSample(),
    185 		remoteParentNotSampled: NeverSample(),
    186 		localParentSampled:     AlwaysSample(),
    187 		localParentNotSampled:  NeverSample(),
    188 	}
    189 
    190 	for _, so := range samplers {
    191 		c = so.apply(c)
    192 	}
    193 
    194 	return c
    195 }
    196 
    197 // samplerConfig is a group of options for parentBased sampler.
    198 type samplerConfig struct {
    199 	remoteParentSampled, remoteParentNotSampled Sampler
    200 	localParentSampled, localParentNotSampled   Sampler
    201 }
    202 
    203 // ParentBasedSamplerOption configures the sampler for a particular sampling case.
    204 type ParentBasedSamplerOption interface {
    205 	apply(samplerConfig) samplerConfig
    206 }
    207 
    208 // WithRemoteParentSampled sets the sampler for the case of sampled remote parent.
    209 func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption {
    210 	return remoteParentSampledOption{s}
    211 }
    212 
    213 type remoteParentSampledOption struct {
    214 	s Sampler
    215 }
    216 
    217 func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig {
    218 	config.remoteParentSampled = o.s
    219 	return config
    220 }
    221 
    222 // WithRemoteParentNotSampled sets the sampler for the case of remote parent
    223 // which is not sampled.
    224 func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption {
    225 	return remoteParentNotSampledOption{s}
    226 }
    227 
    228 type remoteParentNotSampledOption struct {
    229 	s Sampler
    230 }
    231 
    232 func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig {
    233 	config.remoteParentNotSampled = o.s
    234 	return config
    235 }
    236 
    237 // WithLocalParentSampled sets the sampler for the case of sampled local parent.
    238 func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption {
    239 	return localParentSampledOption{s}
    240 }
    241 
    242 type localParentSampledOption struct {
    243 	s Sampler
    244 }
    245 
    246 func (o localParentSampledOption) apply(config samplerConfig) samplerConfig {
    247 	config.localParentSampled = o.s
    248 	return config
    249 }
    250 
    251 // WithLocalParentNotSampled sets the sampler for the case of local parent
    252 // which is not sampled.
    253 func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOption {
    254 	return localParentNotSampledOption{s}
    255 }
    256 
    257 type localParentNotSampledOption struct {
    258 	s Sampler
    259 }
    260 
    261 func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig {
    262 	config.localParentNotSampled = o.s
    263 	return config
    264 }
    265 
    266 func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult {
    267 	psc := trace.SpanContextFromContext(p.ParentContext)
    268 	if psc.IsValid() {
    269 		if psc.IsRemote() {
    270 			if psc.IsSampled() {
    271 				return pb.config.remoteParentSampled.ShouldSample(p)
    272 			}
    273 			return pb.config.remoteParentNotSampled.ShouldSample(p)
    274 		}
    275 
    276 		if psc.IsSampled() {
    277 			return pb.config.localParentSampled.ShouldSample(p)
    278 		}
    279 		return pb.config.localParentNotSampled.ShouldSample(p)
    280 	}
    281 	return pb.root.ShouldSample(p)
    282 }
    283 
    284 func (pb parentBased) Description() string {
    285 	return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+
    286 		"remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}",
    287 		pb.root.Description(),
    288 		pb.config.remoteParentSampled.Description(),
    289 		pb.config.remoteParentNotSampled.Description(),
    290 		pb.config.localParentSampled.Description(),
    291 		pb.config.localParentNotSampled.Description(),
    292 	)
    293 }