sample.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. package trace
  2. import (
  3. "math/rand"
  4. "sync/atomic"
  5. "time"
  6. )
  7. const (
  8. slotLength = 2048
  9. )
  10. var ignoreds = []string{"/metrics", "/monitor/ping"}
  11. func init() {
  12. rand.Seed(time.Now().UnixNano())
  13. }
  14. func oneAtTimeHash(s string) (hash uint32) {
  15. b := []byte(s)
  16. for i := range b {
  17. hash += uint32(b[i])
  18. hash += hash << 10
  19. hash ^= hash >> 6
  20. }
  21. hash += hash << 3
  22. hash ^= hash >> 11
  23. hash += hash << 15
  24. return
  25. }
  26. // sampler decides whether a new trace should be sampled or not.
  27. type sampler interface {
  28. IsSampled(traceID uint64, operationName string) (bool, float32)
  29. Close() error
  30. }
  31. type probabilitySampling struct {
  32. probability float32
  33. slot [slotLength]int64
  34. }
  35. func (p *probabilitySampling) IsSampled(traceID uint64, operationName string) (bool, float32) {
  36. for _, ignored := range ignoreds {
  37. if operationName == ignored {
  38. return false, 0
  39. }
  40. }
  41. now := time.Now().Unix()
  42. idx := oneAtTimeHash(operationName) % slotLength
  43. old := atomic.LoadInt64(&p.slot[idx])
  44. if old != now {
  45. atomic.SwapInt64(&p.slot[idx], now)
  46. return true, 1
  47. }
  48. return rand.Float32() < float32(p.probability), float32(p.probability)
  49. }
  50. func (p *probabilitySampling) Close() error { return nil }
  51. // newSampler new probability sampler
  52. func newSampler(probability float32) sampler {
  53. if probability <= 0 || probability > 1 {
  54. panic("probability P ∈ (0, 1]")
  55. }
  56. return &probabilitySampling{probability: probability}
  57. }