sampling.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // Copyright The OpenTelemetry Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package trace // import "go.opentelemetry.io/otel/sdk/trace"
  15. import (
  16. "context"
  17. "encoding/binary"
  18. "fmt"
  19. "go.opentelemetry.io/otel/attribute"
  20. "go.opentelemetry.io/otel/trace"
  21. )
  22. // Sampler decides whether a trace should be sampled and exported.
  23. type Sampler interface {
  24. // DO NOT CHANGE: any modification will not be backwards compatible and
  25. // must never be done outside of a new major release.
  26. // ShouldSample returns a SamplingResult based on a decision made from the
  27. // passed parameters.
  28. ShouldSample(parameters SamplingParameters) SamplingResult
  29. // DO NOT CHANGE: any modification will not be backwards compatible and
  30. // must never be done outside of a new major release.
  31. // Description returns information describing the Sampler.
  32. Description() string
  33. // DO NOT CHANGE: any modification will not be backwards compatible and
  34. // must never be done outside of a new major release.
  35. }
  36. // SamplingParameters contains the values passed to a Sampler.
  37. type SamplingParameters struct {
  38. ParentContext context.Context
  39. TraceID trace.TraceID
  40. Name string
  41. Kind trace.SpanKind
  42. Attributes []attribute.KeyValue
  43. Links []trace.Link
  44. }
  45. // SamplingDecision indicates whether a span is dropped, recorded and/or sampled.
  46. type SamplingDecision uint8
  47. // Valid sampling decisions.
  48. const (
  49. // Drop will not record the span and all attributes/events will be dropped.
  50. Drop SamplingDecision = iota
  51. // Record indicates the span's `IsRecording() == true`, but `Sampled` flag
  52. // *must not* be set.
  53. RecordOnly
  54. // RecordAndSample has span's `IsRecording() == true` and `Sampled` flag
  55. // *must* be set.
  56. RecordAndSample
  57. )
  58. // SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate.
  59. type SamplingResult struct {
  60. Decision SamplingDecision
  61. Attributes []attribute.KeyValue
  62. Tracestate trace.TraceState
  63. }
  64. type traceIDRatioSampler struct {
  65. traceIDUpperBound uint64
  66. description string
  67. }
  68. func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult {
  69. psc := trace.SpanContextFromContext(p.ParentContext)
  70. x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1
  71. if x < ts.traceIDUpperBound {
  72. return SamplingResult{
  73. Decision: RecordAndSample,
  74. Tracestate: psc.TraceState(),
  75. }
  76. }
  77. return SamplingResult{
  78. Decision: Drop,
  79. Tracestate: psc.TraceState(),
  80. }
  81. }
  82. func (ts traceIDRatioSampler) Description() string {
  83. return ts.description
  84. }
  85. // TraceIDRatioBased samples a given fraction of traces. Fractions >= 1 will
  86. // always sample. Fractions < 0 are treated as zero. To respect the
  87. // parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used
  88. // as a delegate of a `Parent` sampler.
  89. //nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
  90. func TraceIDRatioBased(fraction float64) Sampler {
  91. if fraction >= 1 {
  92. return AlwaysSample()
  93. }
  94. if fraction <= 0 {
  95. fraction = 0
  96. }
  97. return &traceIDRatioSampler{
  98. traceIDUpperBound: uint64(fraction * (1 << 63)),
  99. description: fmt.Sprintf("TraceIDRatioBased{%g}", fraction),
  100. }
  101. }
  102. type alwaysOnSampler struct{}
  103. func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult {
  104. return SamplingResult{
  105. Decision: RecordAndSample,
  106. Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
  107. }
  108. }
  109. func (as alwaysOnSampler) Description() string {
  110. return "AlwaysOnSampler"
  111. }
  112. // AlwaysSample returns a Sampler that samples every trace.
  113. // Be careful about using this sampler in a production application with
  114. // significant traffic: a new trace will be started and exported for every
  115. // request.
  116. func AlwaysSample() Sampler {
  117. return alwaysOnSampler{}
  118. }
  119. type alwaysOffSampler struct{}
  120. func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult {
  121. return SamplingResult{
  122. Decision: Drop,
  123. Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
  124. }
  125. }
  126. func (as alwaysOffSampler) Description() string {
  127. return "AlwaysOffSampler"
  128. }
  129. // NeverSample returns a Sampler that samples no traces.
  130. func NeverSample() Sampler {
  131. return alwaysOffSampler{}
  132. }
  133. // ParentBased returns a composite sampler which behaves differently,
  134. // based on the parent of the span. If the span has no parent,
  135. // the root(Sampler) is used to make sampling decision. If the span has
  136. // a parent, depending on whether the parent is remote and whether it
  137. // is sampled, one of the following samplers will apply:
  138. // - remoteParentSampled(Sampler) (default: AlwaysOn)
  139. // - remoteParentNotSampled(Sampler) (default: AlwaysOff)
  140. // - localParentSampled(Sampler) (default: AlwaysOn)
  141. // - localParentNotSampled(Sampler) (default: AlwaysOff)
  142. func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler {
  143. return parentBased{
  144. root: root,
  145. config: configureSamplersForParentBased(samplers),
  146. }
  147. }
  148. type parentBased struct {
  149. root Sampler
  150. config samplerConfig
  151. }
  152. func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) samplerConfig {
  153. c := samplerConfig{
  154. remoteParentSampled: AlwaysSample(),
  155. remoteParentNotSampled: NeverSample(),
  156. localParentSampled: AlwaysSample(),
  157. localParentNotSampled: NeverSample(),
  158. }
  159. for _, so := range samplers {
  160. c = so.apply(c)
  161. }
  162. return c
  163. }
  164. // samplerConfig is a group of options for parentBased sampler.
  165. type samplerConfig struct {
  166. remoteParentSampled, remoteParentNotSampled Sampler
  167. localParentSampled, localParentNotSampled Sampler
  168. }
  169. // ParentBasedSamplerOption configures the sampler for a particular sampling case.
  170. type ParentBasedSamplerOption interface {
  171. apply(samplerConfig) samplerConfig
  172. }
  173. // WithRemoteParentSampled sets the sampler for the case of sampled remote parent.
  174. func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption {
  175. return remoteParentSampledOption{s}
  176. }
  177. type remoteParentSampledOption struct {
  178. s Sampler
  179. }
  180. func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig {
  181. config.remoteParentSampled = o.s
  182. return config
  183. }
  184. // WithRemoteParentNotSampled sets the sampler for the case of remote parent
  185. // which is not sampled.
  186. func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption {
  187. return remoteParentNotSampledOption{s}
  188. }
  189. type remoteParentNotSampledOption struct {
  190. s Sampler
  191. }
  192. func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig {
  193. config.remoteParentNotSampled = o.s
  194. return config
  195. }
  196. // WithLocalParentSampled sets the sampler for the case of sampled local parent.
  197. func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption {
  198. return localParentSampledOption{s}
  199. }
  200. type localParentSampledOption struct {
  201. s Sampler
  202. }
  203. func (o localParentSampledOption) apply(config samplerConfig) samplerConfig {
  204. config.localParentSampled = o.s
  205. return config
  206. }
  207. // WithLocalParentNotSampled sets the sampler for the case of local parent
  208. // which is not sampled.
  209. func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOption {
  210. return localParentNotSampledOption{s}
  211. }
  212. type localParentNotSampledOption struct {
  213. s Sampler
  214. }
  215. func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig {
  216. config.localParentNotSampled = o.s
  217. return config
  218. }
  219. func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult {
  220. psc := trace.SpanContextFromContext(p.ParentContext)
  221. if psc.IsValid() {
  222. if psc.IsRemote() {
  223. if psc.IsSampled() {
  224. return pb.config.remoteParentSampled.ShouldSample(p)
  225. }
  226. return pb.config.remoteParentNotSampled.ShouldSample(p)
  227. }
  228. if psc.IsSampled() {
  229. return pb.config.localParentSampled.ShouldSample(p)
  230. }
  231. return pb.config.localParentNotSampled.ShouldSample(p)
  232. }
  233. return pb.root.ShouldSample(p)
  234. }
  235. func (pb parentBased) Description() string {
  236. return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+
  237. "remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}",
  238. pb.root.Description(),
  239. pb.config.remoteParentSampled.Description(),
  240. pb.config.remoteParentNotSampled.Description(),
  241. pb.config.localParentSampled.Description(),
  242. pb.config.localParentNotSampled.Description(),
  243. )
  244. }