mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-04-23 11:58:56 +02:00
Comment should be complete sentences outside of lists with sentence fragments. This adds the godot linter to check these complete sentences end with punctuation. If they do not, running fix will append a period.
293 lines
8.5 KiB
Go
293 lines
8.5 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
// Sampler decides whether a trace should be sampled and exported.
|
|
type Sampler interface {
|
|
// DO NOT CHANGE: any modification will not be backwards compatible and
|
|
// must never be done outside of a new major release.
|
|
|
|
// ShouldSample returns a SamplingResult based on a decision made from the
|
|
// passed parameters.
|
|
ShouldSample(parameters SamplingParameters) SamplingResult
|
|
// DO NOT CHANGE: any modification will not be backwards compatible and
|
|
// must never be done outside of a new major release.
|
|
|
|
// Description returns information describing the Sampler.
|
|
Description() string
|
|
// DO NOT CHANGE: any modification will not be backwards compatible and
|
|
// must never be done outside of a new major release.
|
|
}
|
|
|
|
// SamplingParameters contains the values passed to a Sampler.
|
|
type SamplingParameters struct {
|
|
ParentContext context.Context
|
|
TraceID trace.TraceID
|
|
Name string
|
|
Kind trace.SpanKind
|
|
Attributes []attribute.KeyValue
|
|
Links []trace.Link
|
|
}
|
|
|
|
// SamplingDecision indicates whether a span is dropped, recorded and/or sampled.
|
|
type SamplingDecision uint8
|
|
|
|
// Valid sampling decisions.
|
|
const (
|
|
// Drop will not record the span and all attributes/events will be dropped.
|
|
Drop SamplingDecision = iota
|
|
|
|
// Record indicates the span's `IsRecording() == true`, but `Sampled` flag
|
|
// *must not* be set.
|
|
RecordOnly
|
|
|
|
// RecordAndSample has span's `IsRecording() == true` and `Sampled` flag
|
|
// *must* be set.
|
|
RecordAndSample
|
|
)
|
|
|
|
// SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate.
|
|
type SamplingResult struct {
|
|
Decision SamplingDecision
|
|
Attributes []attribute.KeyValue
|
|
Tracestate trace.TraceState
|
|
}
|
|
|
|
type traceIDRatioSampler struct {
|
|
traceIDUpperBound uint64
|
|
description string
|
|
}
|
|
|
|
func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult {
|
|
psc := trace.SpanContextFromContext(p.ParentContext)
|
|
x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1
|
|
if x < ts.traceIDUpperBound {
|
|
return SamplingResult{
|
|
Decision: RecordAndSample,
|
|
Tracestate: psc.TraceState(),
|
|
}
|
|
}
|
|
return SamplingResult{
|
|
Decision: Drop,
|
|
Tracestate: psc.TraceState(),
|
|
}
|
|
}
|
|
|
|
func (ts traceIDRatioSampler) Description() string {
|
|
return ts.description
|
|
}
|
|
|
|
// TraceIDRatioBased samples a given fraction of traces. Fractions >= 1 will
|
|
// always sample. Fractions < 0 are treated as zero. To respect the
|
|
// parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used
|
|
// as a delegate of a `Parent` sampler.
|
|
//nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
|
|
func TraceIDRatioBased(fraction float64) Sampler {
|
|
if fraction >= 1 {
|
|
return AlwaysSample()
|
|
}
|
|
|
|
if fraction <= 0 {
|
|
fraction = 0
|
|
}
|
|
|
|
return &traceIDRatioSampler{
|
|
traceIDUpperBound: uint64(fraction * (1 << 63)),
|
|
description: fmt.Sprintf("TraceIDRatioBased{%g}", fraction),
|
|
}
|
|
}
|
|
|
|
type alwaysOnSampler struct{}
|
|
|
|
func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult {
|
|
return SamplingResult{
|
|
Decision: RecordAndSample,
|
|
Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
|
|
}
|
|
}
|
|
|
|
func (as alwaysOnSampler) Description() string {
|
|
return "AlwaysOnSampler"
|
|
}
|
|
|
|
// AlwaysSample returns a Sampler that samples every trace.
|
|
// Be careful about using this sampler in a production application with
|
|
// significant traffic: a new trace will be started and exported for every
|
|
// request.
|
|
func AlwaysSample() Sampler {
|
|
return alwaysOnSampler{}
|
|
}
|
|
|
|
type alwaysOffSampler struct{}
|
|
|
|
func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult {
|
|
return SamplingResult{
|
|
Decision: Drop,
|
|
Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
|
|
}
|
|
}
|
|
|
|
func (as alwaysOffSampler) Description() string {
|
|
return "AlwaysOffSampler"
|
|
}
|
|
|
|
// NeverSample returns a Sampler that samples no traces.
|
|
func NeverSample() Sampler {
|
|
return alwaysOffSampler{}
|
|
}
|
|
|
|
// ParentBased returns a composite sampler which behaves differently,
|
|
// based on the parent of the span. If the span has no parent,
|
|
// the root(Sampler) is used to make sampling decision. If the span has
|
|
// a parent, depending on whether the parent is remote and whether it
|
|
// is sampled, one of the following samplers will apply:
|
|
// - remoteParentSampled(Sampler) (default: AlwaysOn)
|
|
// - remoteParentNotSampled(Sampler) (default: AlwaysOff)
|
|
// - localParentSampled(Sampler) (default: AlwaysOn)
|
|
// - localParentNotSampled(Sampler) (default: AlwaysOff)
|
|
func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler {
|
|
return parentBased{
|
|
root: root,
|
|
config: configureSamplersForParentBased(samplers),
|
|
}
|
|
}
|
|
|
|
type parentBased struct {
|
|
root Sampler
|
|
config samplerConfig
|
|
}
|
|
|
|
func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) samplerConfig {
|
|
c := samplerConfig{
|
|
remoteParentSampled: AlwaysSample(),
|
|
remoteParentNotSampled: NeverSample(),
|
|
localParentSampled: AlwaysSample(),
|
|
localParentNotSampled: NeverSample(),
|
|
}
|
|
|
|
for _, so := range samplers {
|
|
c = so.apply(c)
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
// samplerConfig is a group of options for parentBased sampler.
|
|
type samplerConfig struct {
|
|
remoteParentSampled, remoteParentNotSampled Sampler
|
|
localParentSampled, localParentNotSampled Sampler
|
|
}
|
|
|
|
// ParentBasedSamplerOption configures the sampler for a particular sampling case.
|
|
type ParentBasedSamplerOption interface {
|
|
apply(samplerConfig) samplerConfig
|
|
}
|
|
|
|
// WithRemoteParentSampled sets the sampler for the case of sampled remote parent.
|
|
func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption {
|
|
return remoteParentSampledOption{s}
|
|
}
|
|
|
|
type remoteParentSampledOption struct {
|
|
s Sampler
|
|
}
|
|
|
|
func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig {
|
|
config.remoteParentSampled = o.s
|
|
return config
|
|
}
|
|
|
|
// WithRemoteParentNotSampled sets the sampler for the case of remote parent
|
|
// which is not sampled.
|
|
func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption {
|
|
return remoteParentNotSampledOption{s}
|
|
}
|
|
|
|
type remoteParentNotSampledOption struct {
|
|
s Sampler
|
|
}
|
|
|
|
func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig {
|
|
config.remoteParentNotSampled = o.s
|
|
return config
|
|
}
|
|
|
|
// WithLocalParentSampled sets the sampler for the case of sampled local parent.
|
|
func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption {
|
|
return localParentSampledOption{s}
|
|
}
|
|
|
|
type localParentSampledOption struct {
|
|
s Sampler
|
|
}
|
|
|
|
func (o localParentSampledOption) apply(config samplerConfig) samplerConfig {
|
|
config.localParentSampled = o.s
|
|
return config
|
|
}
|
|
|
|
// WithLocalParentNotSampled sets the sampler for the case of local parent
|
|
// which is not sampled.
|
|
func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOption {
|
|
return localParentNotSampledOption{s}
|
|
}
|
|
|
|
type localParentNotSampledOption struct {
|
|
s Sampler
|
|
}
|
|
|
|
func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig {
|
|
config.localParentNotSampled = o.s
|
|
return config
|
|
}
|
|
|
|
func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult {
|
|
psc := trace.SpanContextFromContext(p.ParentContext)
|
|
if psc.IsValid() {
|
|
if psc.IsRemote() {
|
|
if psc.IsSampled() {
|
|
return pb.config.remoteParentSampled.ShouldSample(p)
|
|
}
|
|
return pb.config.remoteParentNotSampled.ShouldSample(p)
|
|
}
|
|
|
|
if psc.IsSampled() {
|
|
return pb.config.localParentSampled.ShouldSample(p)
|
|
}
|
|
return pb.config.localParentNotSampled.ShouldSample(p)
|
|
}
|
|
return pb.root.ShouldSample(p)
|
|
}
|
|
|
|
func (pb parentBased) Description() string {
|
|
return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+
|
|
"remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}",
|
|
pb.root.Description(),
|
|
pb.config.remoteParentSampled.Description(),
|
|
pb.config.remoteParentNotSampled.Description(),
|
|
pb.config.localParentSampled.Description(),
|
|
pb.config.localParentNotSampled.Description(),
|
|
)
|
|
}
|