123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- // 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(),
- )
- }
|