sampling.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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[8:16]) >> 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. //
  90. //nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
  91. func TraceIDRatioBased(fraction float64) Sampler {
  92. if fraction >= 1 {
  93. return AlwaysSample()
  94. }
  95. if fraction <= 0 {
  96. fraction = 0
  97. }
  98. return &traceIDRatioSampler{
  99. traceIDUpperBound: uint64(fraction * (1 << 63)),
  100. description: fmt.Sprintf("TraceIDRatioBased{%g}", fraction),
  101. }
  102. }
  103. type alwaysOnSampler struct{}
  104. func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult {
  105. return SamplingResult{
  106. Decision: RecordAndSample,
  107. Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
  108. }
  109. }
  110. func (as alwaysOnSampler) Description() string {
  111. return "AlwaysOnSampler"
  112. }
  113. // AlwaysSample returns a Sampler that samples every trace.
  114. // Be careful about using this sampler in a production application with
  115. // significant traffic: a new trace will be started and exported for every
  116. // request.
  117. func AlwaysSample() Sampler {
  118. return alwaysOnSampler{}
  119. }
  120. type alwaysOffSampler struct{}
  121. func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult {
  122. return SamplingResult{
  123. Decision: Drop,
  124. Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
  125. }
  126. }
  127. func (as alwaysOffSampler) Description() string {
  128. return "AlwaysOffSampler"
  129. }
  130. // NeverSample returns a Sampler that samples no traces.
  131. func NeverSample() Sampler {
  132. return alwaysOffSampler{}
  133. }
  134. // ParentBased returns a composite sampler which behaves differently,
  135. // based on the parent of the span. If the span has no parent,
  136. // the root(Sampler) is used to make sampling decision. If the span has
  137. // a parent, depending on whether the parent is remote and whether it
  138. // is sampled, one of the following samplers will apply:
  139. // - remoteParentSampled(Sampler) (default: AlwaysOn)
  140. // - remoteParentNotSampled(Sampler) (default: AlwaysOff)
  141. // - localParentSampled(Sampler) (default: AlwaysOn)
  142. // - localParentNotSampled(Sampler) (default: AlwaysOff)
  143. func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler {
  144. return parentBased{
  145. root: root,
  146. config: configureSamplersForParentBased(samplers),
  147. }
  148. }
  149. type parentBased struct {
  150. root Sampler
  151. config samplerConfig
  152. }
  153. func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) samplerConfig {
  154. c := samplerConfig{
  155. remoteParentSampled: AlwaysSample(),
  156. remoteParentNotSampled: NeverSample(),
  157. localParentSampled: AlwaysSample(),
  158. localParentNotSampled: NeverSample(),
  159. }
  160. for _, so := range samplers {
  161. c = so.apply(c)
  162. }
  163. return c
  164. }
  165. // samplerConfig is a group of options for parentBased sampler.
  166. type samplerConfig struct {
  167. remoteParentSampled, remoteParentNotSampled Sampler
  168. localParentSampled, localParentNotSampled Sampler
  169. }
  170. // ParentBasedSamplerOption configures the sampler for a particular sampling case.
  171. type ParentBasedSamplerOption interface {
  172. apply(samplerConfig) samplerConfig
  173. }
  174. // WithRemoteParentSampled sets the sampler for the case of sampled remote parent.
  175. func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption {
  176. return remoteParentSampledOption{s}
  177. }
  178. type remoteParentSampledOption struct {
  179. s Sampler
  180. }
  181. func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig {
  182. config.remoteParentSampled = o.s
  183. return config
  184. }
  185. // WithRemoteParentNotSampled sets the sampler for the case of remote parent
  186. // which is not sampled.
  187. func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption {
  188. return remoteParentNotSampledOption{s}
  189. }
  190. type remoteParentNotSampledOption struct {
  191. s Sampler
  192. }
  193. func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig {
  194. config.remoteParentNotSampled = o.s
  195. return config
  196. }
  197. // WithLocalParentSampled sets the sampler for the case of sampled local parent.
  198. func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption {
  199. return localParentSampledOption{s}
  200. }
  201. type localParentSampledOption struct {
  202. s Sampler
  203. }
  204. func (o localParentSampledOption) apply(config samplerConfig) samplerConfig {
  205. config.localParentSampled = o.s
  206. return config
  207. }
  208. // WithLocalParentNotSampled sets the sampler for the case of local parent
  209. // which is not sampled.
  210. func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOption {
  211. return localParentNotSampledOption{s}
  212. }
  213. type localParentNotSampledOption struct {
  214. s Sampler
  215. }
  216. func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig {
  217. config.localParentNotSampled = o.s
  218. return config
  219. }
  220. func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult {
  221. psc := trace.SpanContextFromContext(p.ParentContext)
  222. if psc.IsValid() {
  223. if psc.IsRemote() {
  224. if psc.IsSampled() {
  225. return pb.config.remoteParentSampled.ShouldSample(p)
  226. }
  227. return pb.config.remoteParentNotSampled.ShouldSample(p)
  228. }
  229. if psc.IsSampled() {
  230. return pb.config.localParentSampled.ShouldSample(p)
  231. }
  232. return pb.config.localParentNotSampled.ShouldSample(p)
  233. }
  234. return pb.root.ShouldSample(p)
  235. }
  236. func (pb parentBased) Description() string {
  237. return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+
  238. "remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}",
  239. pb.root.Description(),
  240. pb.config.remoteParentSampled.Description(),
  241. pb.config.remoteParentNotSampled.Description(),
  242. pb.config.localParentSampled.Description(),
  243. pb.config.localParentNotSampled.Description(),
  244. )
  245. }