-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathsampling.go
156 lines (129 loc) · 4.25 KB
/
sampling.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 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 (
"encoding/binary"
"fmt"
api "go.opentelemetry.io/otel/api/trace"
"go.opentelemetry.io/otel/label"
)
// Sampler decides whether a trace should be sampled and exported.
type Sampler interface {
ShouldSample(SamplingParameters) SamplingResult
Description() string
}
// SamplingParameters contains the values passed to a Sampler.
type SamplingParameters struct {
ParentContext api.SpanContext
TraceID api.ID
Name string
HasRemoteParent bool
Kind api.SpanKind
Attributes []label.KeyValue
Links []api.Link
}
// SamplingDecision indicates whether a span is recorded and sampled.
type SamplingDecision uint8
// Valid sampling decisions
const (
NotRecord SamplingDecision = iota
Record
RecordAndSampled
)
// SamplingResult conveys a SamplingDecision and a set of Attributes.
type SamplingResult struct {
Decision SamplingDecision
Attributes []label.KeyValue
}
type probabilitySampler struct {
traceIDUpperBound uint64
description string
}
func (ps probabilitySampler) ShouldSample(p SamplingParameters) SamplingResult {
if p.ParentContext.IsSampled() {
return SamplingResult{Decision: RecordAndSampled}
}
x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1
if x < ps.traceIDUpperBound {
return SamplingResult{Decision: RecordAndSampled}
}
return SamplingResult{Decision: NotRecord}
}
func (ps probabilitySampler) Description() string {
return ps.description
}
// ProbabilitySampler samples a given fraction of traces. Fractions >= 1 will
// always sample. If the parent span is sampled, then it's child spans will
// automatically be sampled. Fractions < 0 are treated as zero, but spans may
// still be sampled if their parent is.
func ProbabilitySampler(fraction float64) Sampler {
if fraction >= 1 {
return AlwaysSample()
}
if fraction <= 0 {
fraction = 0
}
return &probabilitySampler{
traceIDUpperBound: uint64(fraction * (1 << 63)),
description: fmt.Sprintf("ProbabilitySampler{%g}", fraction),
}
}
type alwaysOnSampler struct{}
func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult {
return SamplingResult{Decision: RecordAndSampled}
}
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: NotRecord}
}
func (as alwaysOffSampler) Description() string {
return "AlwaysOffSampler"
}
// NeverSample returns a Sampler that samples no traces.
func NeverSample() Sampler {
return alwaysOffSampler{}
}
// ParentSample returns a Sampler that samples a trace only
// if the the span has a parent span and it is sampled. If the span has
// parent span but it is not sampled, neither will this span. If the span
// does not have a parent the fallback Sampler is used to determine if the
// span should be sampled.
func ParentSample(fallback Sampler) Sampler {
return parentSampler{fallback}
}
type parentSampler struct {
fallback Sampler
}
func (ps parentSampler) ShouldSample(p SamplingParameters) SamplingResult {
if p.ParentContext.IsValid() {
if p.ParentContext.IsSampled() {
return SamplingResult{Decision: RecordAndSampled}
}
return SamplingResult{Decision: NotRecord}
}
return ps.fallback.ShouldSample(p)
}
func (ps parentSampler) Description() string {
return fmt.Sprintf("ParentOrElse{%s}", ps.fallback.Description())
}