github.com/honeycombio/honeytail@v1.9.0/sample/deterministic_sampler.go (about) 1 package sample 2 3 import ( 4 "crypto/sha1" 5 "errors" 6 "math" 7 ) 8 9 var ( 10 ErrInvalidSampleRate = errors.New("sample rate must be >= 1") 11 ) 12 13 func init() { 14 } 15 16 // DeterministicSampler allows for distributed sampling based on a common field 17 // such as a request or trace ID. It accepts a sample rate N and will 18 // deterministically sample 1/N events based on the target field. Hence, two or 19 // more programs can decide whether or not to sample related events without 20 // communication. 21 type DeterministicSampler struct { 22 sampleRate int 23 upperBound uint32 24 } 25 26 func NewDeterministicSampler(sampleRate uint) (*DeterministicSampler, error) { 27 if sampleRate < 1 { 28 return nil, ErrInvalidSampleRate 29 } 30 31 // Get the actual upper bound - the largest possible value divided by 32 // the sample rate. In the case where the sample rate is 1, this should 33 // sample every value. 34 upperBound := math.MaxUint32 / uint32(sampleRate) 35 return &DeterministicSampler{ 36 sampleRate: int(sampleRate), 37 upperBound: upperBound, 38 }, nil 39 } 40 41 // bytesToUint32 takes a slice of 4 bytes representing a big endian 32 bit 42 // unsigned value and returns the equivalent uint32. 43 func bytesToUint32be(b []byte) uint32 { 44 return uint32(b[3]) | (uint32(b[2]) << 8) | (uint32(b[1]) << 16) | (uint32(b[0]) << 24) 45 } 46 47 func (ds *DeterministicSampler) Sample(determinant string) bool { 48 sum := sha1.Sum([]byte(determinant)) 49 v := bytesToUint32be(sum[:4]) 50 return v <= ds.upperBound 51 }