sampler.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package otel
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "strconv"
  8. "strings"
  9. sdktrace "go.opentelemetry.io/otel/sdk/trace"
  10. "go.opentelemetry.io/otel/trace"
  11. )
  12. const (
  13. tracesSamplerKey = "OTEL_TRACES_SAMPLER"
  14. tracesSamplerArgKey = "OTEL_TRACES_SAMPLER_ARG"
  15. samplerTraceIDRatio = "traceidratio"
  16. )
  17. var (
  18. errNegativeTraceIDRatio = errors.New("invalid trace ID ratio: less than 0.0")
  19. errGreaterThanOneTraceIDRatio = errors.New("invalid trace ID ratio: greater than 1.0")
  20. )
  21. type samplerArgParseError struct {
  22. parseErr error
  23. }
  24. func (e samplerArgParseError) Error() string {
  25. return fmt.Sprintf("parsing sampler argument: %s", e.parseErr.Error())
  26. }
  27. func (e samplerArgParseError) Unwrap() error {
  28. return e.parseErr
  29. }
  30. type traceIDRatioSampler struct {
  31. traceIDUpperBound uint64
  32. description string
  33. }
  34. func (ts traceIDRatioSampler) ShouldSample(p sdktrace.SamplingParameters) sdktrace.SamplingResult {
  35. psc := trace.SpanContextFromContext(p.ParentContext)
  36. x := binary.BigEndian.Uint64(p.TraceID[len(p.TraceID)-8:]) >> 1
  37. if x < ts.traceIDUpperBound {
  38. return sdktrace.SamplingResult{
  39. Decision: sdktrace.RecordAndSample,
  40. Tracestate: psc.TraceState(),
  41. }
  42. }
  43. return sdktrace.SamplingResult{
  44. Decision: sdktrace.Drop,
  45. Tracestate: psc.TraceState(),
  46. }
  47. }
  48. func (ts traceIDRatioSampler) Description() string {
  49. return ts.description
  50. }
  51. func traceIDRatioBased(fraction float64) sdktrace.Sampler {
  52. if fraction >= 1 {
  53. return sdktrace.AlwaysSample()
  54. }
  55. if fraction <= 0 {
  56. fraction = 0
  57. }
  58. return &traceIDRatioSampler{
  59. traceIDUpperBound: uint64(fraction * (1 << 63)),
  60. description: fmt.Sprintf("traceIDRatioBased{%g}", fraction),
  61. }
  62. }
  63. func parseTraceIDRatio(arg string) (sdktrace.Sampler, error) {
  64. v, err := strconv.ParseFloat(arg, 64)
  65. if err != nil {
  66. return traceIDRatioBased(1.0), samplerArgParseError{err}
  67. }
  68. if v < 0.0 {
  69. return traceIDRatioBased(0.0), errNegativeTraceIDRatio
  70. }
  71. if v > 1.0 {
  72. return traceIDRatioBased(1.0), errGreaterThanOneTraceIDRatio
  73. }
  74. return traceIDRatioBased(v), nil
  75. }
  76. func addTraceIDRatioSampler(opts []sdktrace.TracerProviderOption) ([]sdktrace.TracerProviderOption, error) {
  77. samplerName, ok := os.LookupEnv(tracesSamplerKey)
  78. if !ok {
  79. return opts, nil
  80. }
  81. if strings.ToLower(strings.TrimSpace(samplerName)) != samplerTraceIDRatio {
  82. return opts, nil
  83. }
  84. samplerArg, hasSamplerArg := os.LookupEnv(tracesSamplerArgKey)
  85. samplerArg = strings.TrimSpace(samplerArg)
  86. var (
  87. sampler sdktrace.Sampler
  88. err error
  89. )
  90. if !hasSamplerArg {
  91. sampler = traceIDRatioBased(1.0)
  92. } else {
  93. sampler, err = parseTraceIDRatio(samplerArg)
  94. }
  95. return append(opts, sdktrace.WithSampler(sampler)), err
  96. }