github.com/waldiirawan/apm-agent-go/v2@v2.2.2/sampler.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package apm // import "github.com/waldiirawan/apm-agent-go/v2"
    19  
    20  import (
    21  	"encoding/binary"
    22  	"math"
    23  	"math/big"
    24  
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  // Sampler provides a means of sampling transactions.
    29  type Sampler interface {
    30  	// Sample indicates whether or not a transaction
    31  	// should be sampled, and the sampling rate in
    32  	// effect at the time.This method will be invoked
    33  	// by calls to Tracer.StartTransaction for the root
    34  	// of a trace, so it must be goroutine-safe, and
    35  	// should avoid synchronization as far as possible.
    36  	Sample(SampleParams) SampleResult
    37  }
    38  
    39  // SampleParams holds parameters for Sampler.Sample.
    40  type SampleParams struct {
    41  	// TraceContext holds the newly-generated TraceContext
    42  	// for the root transaction which is being sampled.
    43  	TraceContext TraceContext
    44  }
    45  
    46  // SampleResult holds information about a sampling decision.
    47  type SampleResult struct {
    48  	// Sampled holds the sampling decision.
    49  	Sampled bool
    50  
    51  	// SampleRate holds the sample rate in effect at the
    52  	// time of the sampling decision. This is used for
    53  	// propagating the value downstream, and for inclusion
    54  	// in events sent to APM Server.
    55  	//
    56  	// The sample rate will be rounded to 4 decimal places
    57  	// half away from zero, except if it is in the interval
    58  	// (0, 0.0001], in which case it is set to 0.0001. The
    59  	// Sampler implementation should also internally apply
    60  	// this logic to ensure consistency.
    61  	SampleRate float64
    62  }
    63  
    64  // NewRatioSampler returns a new Sampler with the given ratio
    65  //
    66  // A ratio of 1.0 samples 100% of transactions, a ratio of 0.5
    67  // samples ~50%, and so on. If the ratio provided does not lie
    68  // within the range [0,1.0], NewRatioSampler will panic.
    69  //
    70  // Sampling rate is rounded to 4 digits half away from zero,
    71  // except if it is in the interval (0, 0.0001], in which case
    72  // is set to 0.0001.
    73  //
    74  // The returned Sampler bases its decision on the value of the
    75  // transaction ID, so there is no synchronization involved.
    76  func NewRatioSampler(r float64) Sampler {
    77  	if r < 0 || r > 1.0 {
    78  		panic(errors.Errorf("ratio %v out of range [0,1.0]", r))
    79  	}
    80  	r = roundSampleRate(r)
    81  	var x big.Float
    82  	x.SetUint64(math.MaxUint64)
    83  	x.Mul(&x, big.NewFloat(r))
    84  	ceil, _ := x.Uint64()
    85  	return ratioSampler{r, ceil}
    86  }
    87  
    88  type ratioSampler struct {
    89  	ratio float64
    90  	ceil  uint64
    91  }
    92  
    93  // Sample samples the transaction according to the configured
    94  // ratio and pseudo-random source.
    95  func (s ratioSampler) Sample(args SampleParams) SampleResult {
    96  	v := binary.BigEndian.Uint64(args.TraceContext.Span[:])
    97  	result := SampleResult{
    98  		Sampled:    v > 0 && v-1 < s.ceil,
    99  		SampleRate: s.ratio,
   100  	}
   101  	return result
   102  }
   103  
   104  // roundSampleRate rounds r to 4 decimal places half away from zero,
   105  // with the exception of values > 0 and < 0.0001, which are set to 0.0001.
   106  func roundSampleRate(r float64) float64 {
   107  	if r > 0 && r < 0.0001 {
   108  		r = 0.0001
   109  	}
   110  	return math.Round(r*10000) / 10000
   111  }