1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-03 18:35:08 +02:00
Files
opentelemetry-go/sdk/trace/sampling.go
T

283 lines
8.0 KiB
Go
Raw Normal View History

// Copyright The OpenTelemetry Authors
2024-02-29 07:05:28 +01:00
// SPDX-License-Identifier: Apache-2.0
2019-08-02 13:52:55 -07:00
package trace // import "go.opentelemetry.io/otel/sdk/trace"
2019-08-02 13:52:55 -07:00
import (
2021-03-30 17:34:40 +00:00
"context"
"encoding/binary"
2020-03-10 11:25:11 -04:00
"fmt"
2021-02-18 12:59:37 -05:00
"go.opentelemetry.io/otel/attribute"
2020-11-06 23:13:31 +01:00
"go.opentelemetry.io/otel/trace"
2019-08-02 13:52:55 -07:00
)
// Sampler decides whether a trace should be sampled and exported.
2020-03-10 11:25:11 -04:00
type Sampler interface {
2021-06-18 14:56:11 +00:00
// 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
2021-06-18 14:56:11 +00:00
// 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.
2020-03-10 11:25:11 -04:00
Description() string
2021-06-18 14:56:11 +00:00
// DO NOT CHANGE: any modification will not be backwards compatible and
// must never be done outside of a new major release.
2020-03-10 11:25:11 -04:00
}
2019-08-02 13:52:55 -07:00
// SamplingParameters contains the values passed to a Sampler.
type SamplingParameters struct {
2021-03-30 17:34:40 +00:00
ParentContext context.Context
TraceID trace.TraceID
Name string
Kind trace.SpanKind
Attributes []attribute.KeyValue
Links []trace.Link
2020-03-10 11:25:11 -04:00
}
2020-09-22 10:34:43 -07:00
// SamplingDecision indicates whether a span is dropped, recorded and/or sampled.
2020-03-10 11:25:11 -04:00
type SamplingDecision uint8
2022-04-25 13:22:49 -07:00
// Valid sampling decisions.
2020-03-10 11:25:11 -04:00
const (
2022-04-25 13:22:49 -07:00
// Drop will not record the span and all attributes/events will be dropped.
2020-09-22 10:34:43 -07:00
Drop SamplingDecision = iota
// Record indicates the span's `IsRecording() == true`, but `Sampled` flag
2022-04-25 13:22:49 -07:00
// *must not* be set.
2020-09-22 10:34:43 -07:00
RecordOnly
// RecordAndSample has span's `IsRecording() == true` and `Sampled` flag
2022-04-25 13:22:49 -07:00
// *must* be set.
2020-09-22 10:34:43 -07:00
RecordAndSample
2020-03-10 11:25:11 -04:00
)
// SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate.
2020-03-10 11:25:11 -04:00
type SamplingResult struct {
Decision SamplingDecision
2021-02-18 12:59:37 -05:00
Attributes []attribute.KeyValue
Tracestate trace.TraceState
2020-03-10 11:25:11 -04:00
}
type traceIDRatioSampler struct {
2020-03-10 11:25:11 -04:00
traceIDUpperBound uint64
description string
2019-08-02 13:52:55 -07:00
}
func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult {
2021-03-30 17:34:40 +00:00
psc := trace.SpanContextFromContext(p.ParentContext)
x := binary.BigEndian.Uint64(p.TraceID[8:16]) >> 1
if x < ts.traceIDUpperBound {
return SamplingResult{
Decision: RecordAndSample,
2021-03-30 17:34:40 +00:00
Tracestate: psc.TraceState(),
}
}
return SamplingResult{
Decision: Drop,
2021-03-30 17:34:40 +00:00
Tracestate: psc.TraceState(),
2020-03-10 11:25:11 -04:00
}
}
func (ts traceIDRatioSampler) Description() string {
return ts.description
2019-08-02 13:52:55 -07:00
}
// 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.
2022-08-24 21:42:28 -05:00
//
//nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
func TraceIDRatioBased(fraction float64) Sampler {
if fraction >= 1 {
2019-08-02 13:52:55 -07:00
return AlwaysSample()
}
if fraction <= 0 {
fraction = 0
}
2020-03-10 11:25:11 -04:00
return &traceIDRatioSampler{
2020-03-10 11:25:11 -04:00
traceIDUpperBound: uint64(fraction * (1 << 63)),
description: fmt.Sprintf("TraceIDRatioBased{%g}", fraction),
}
2019-08-02 13:52:55 -07:00
}
2020-03-10 11:25:11 -04:00
type alwaysOnSampler struct{}
func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult {
return SamplingResult{
Decision: RecordAndSample,
2021-03-30 17:34:40 +00:00
Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
}
2020-03-10 11:25:11 -04:00
}
func (as alwaysOnSampler) Description() string {
return "AlwaysOnSampler"
}
2019-08-02 13:52:55 -07:00
// 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 {
2020-03-10 11:25:11 -04:00
return alwaysOnSampler{}
}
type alwaysOffSampler struct{}
func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult {
return SamplingResult{
Decision: Drop,
2021-03-30 17:34:40 +00:00
Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
}
2020-03-10 11:25:11 -04:00
}
func (as alwaysOffSampler) Description() string {
return "AlwaysOffSampler"
2019-08-02 13:52:55 -07:00
}
// NeverSample returns a Sampler that samples no traces.
func NeverSample() Sampler {
2020-03-10 11:25:11 -04:00
return alwaysOffSampler{}
2019-08-02 13:52:55 -07:00
}
2020-02-03 10:22:52 -08:00
2023-10-13 19:51:11 +02:00
// ParentBased returns a sampler decorator which behaves differently,
// based on the parent of the span. If the span has no parent,
2023-10-13 19:51:11 +02:00
// the decorated 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
2021-05-14 22:28:28 +02:00
config samplerConfig
}
2021-05-14 22:28:28 +02:00
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
}
2021-05-14 22:28:28 +02:00
// 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 {
2021-03-30 17:34:40 +00:00
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)
}
2021-03-30 17:34:40 +00:00
if psc.IsSampled() {
return pb.config.localParentSampled.ShouldSample(p)
}
return pb.config.localParentNotSampled.ShouldSample(p)
2020-03-10 11:25:11 -04:00
}
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(),
)
2020-02-03 10:22:52 -08:00
}