-
Notifications
You must be signed in to change notification settings - Fork 0
/
sampler.go
108 lines (91 loc) · 3.22 KB
/
sampler.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
package bugsnagperformance
import (
"encoding/binary"
"math"
"math/big"
"go.opentelemetry.io/otel/attribute"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
)
var (
PROBABILITY_SCALE_FACTOR_64 = new(big.Float).SetUint64(math.MaxUint64) // (2 ** 64) - 1
)
const (
PROBABILITY_SCALE_FACTOR_32 float64 = 4_294_967_295 // (2 ** 32) - 1
)
type Sampler struct {
probMgr *probabilityManager
parser *tracestateParser
}
func createSampler(probManager *probabilityManager) *Sampler {
sampler := Sampler{
probMgr: probManager,
parser: &tracestateParser{},
}
return &sampler
}
func (s *Sampler) ShouldSample(parameters sdktrace.SamplingParameters) sdktrace.SamplingResult {
// NOTE: the probability could change at any time so we _must_ only read
// it once in this method, otherwise we could use different values
// for the sampling decision & p value attribute which would result
// in inconsistent data
probability := s.probMgr.getProbability()
parentSpanCtx := trace.SpanContextFromContext(parameters.ParentContext)
traceState := parentSpanCtx.TraceState()
var decision sdktrace.SamplingDecision
if s.sampleUsingProbabilityAndTrace(probability, traceState, parameters.TraceID) {
decision = sdktrace.RecordAndSample
} else {
decision = sdktrace.Drop
}
return sdktrace.SamplingResult{
Decision: decision,
Attributes: []attribute.KeyValue{{Key: samplingAttribute, Value: attribute.Float64Value(probability)}},
Tracestate: traceState,
}
}
func (s *Sampler) resample(span sdktrace.ReadOnlySpan) (managedSpan, bool) {
managedSpan := managedSpan{span: span}
attributes := attribute.NewSet(span.Attributes()...)
// sample all spans that are missing the p value attribute
if attributes.Len() == 0 || !attributes.HasValue(samplingAttribute) {
return managedSpan, true
}
probability := s.probMgr.getProbability()
value, _ := attributes.Value(samplingAttribute)
value64 := value.AsFloat64()
if value64 > probability {
value64 = probability
managedSpan.samplingProbability = &value64
}
result := s.sampleUsingProbabilityAndTrace(value64, span.SpanContext().TraceState(), span.SpanContext().TraceID())
return managedSpan, result
}
func (s *Sampler) sampleUsingProbabilityAndTrace(probability float64, traceState trace.TraceState, traceID trace.TraceID) bool {
parsedState := s.parser.parse(traceState)
if parsedState.isValid() {
if parsedState.isValue32() {
rValue := parsedState.getRValue32()
pValue := uint32(math.Floor(probability * PROBABILITY_SCALE_FACTOR_32))
return pValue >= rValue
} else {
rValue := parsedState.getRValue64()
probabilityBig := new(big.Float).SetFloat64(probability)
pValueRes := new(big.Float)
pValueRes = pValueRes.Mul(probabilityBig, PROBABILITY_SCALE_FACTOR_64)
pValue, _ := pValueRes.Uint64()
return pValue >= rValue
}
} else {
traceIDRaw := [16]byte(traceID)
rValue := binary.BigEndian.Uint64(traceIDRaw[8:])
probabilityBig := new(big.Float).SetFloat64(probability)
pValueRes := new(big.Float)
pValueRes = pValueRes.Mul(probabilityBig, PROBABILITY_SCALE_FACTOR_64)
pValue, _ := pValueRes.Uint64()
return pValue >= rValue
}
}
func (s *Sampler) Description() string {
return "Bugsnag Go Performance SDK Sampler"
}