github.com/Axway/agent-sdk@v1.1.101/pkg/traceability/sampling/globalsampling.go (about)

     1  package sampling
     2  
     3  import (
     4  	"math"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/Axway/agent-sdk/pkg/agent"
    11  	"github.com/Axway/agent-sdk/pkg/util/log"
    12  	"github.com/elastic/beats/v7/libbeat/publisher"
    13  	"github.com/shopspring/decimal"
    14  )
    15  
    16  // Global Agent samples
    17  var agentSamples *sample
    18  
    19  const (
    20  	qaSamplingPercentageEnvVar = "QA_TRACEABILITY_SAMPLING_PERCENTAGE"
    21  )
    22  
    23  // Sampling - configures the sampling of events the agent sends to Amplify
    24  type Sampling struct {
    25  	Percentage      float64 `config:"percentage"`
    26  	PerAPI          bool    `config:"per_api"`
    27  	PerSub          bool    `config:"per_subscription"`
    28  	OnlyErrors      bool    `config:"onlyErrors" yaml:"onlyErrors"`
    29  	countMax        int
    30  	shouldSampleMax int
    31  }
    32  
    33  // DefaultConfig - returns a default sampling config where all transactions are sent
    34  func DefaultConfig() Sampling {
    35  	return Sampling{
    36  		Percentage:      defaultSamplingRate,
    37  		PerAPI:          true,
    38  		PerSub:          true,
    39  		OnlyErrors:      false,
    40  		countMax:        countMax,
    41  		shouldSampleMax: defaultSamplingRate,
    42  	}
    43  }
    44  
    45  // GetGlobalSamplingPercentage -
    46  func GetGlobalSamplingPercentage() (float64, error) {
    47  	return agentSamples.config.Percentage, nil
    48  }
    49  
    50  // GetGlobalSampling -
    51  func GetGlobalSampling() Sampling {
    52  	return agentSamples.config
    53  }
    54  
    55  func getSamplingPercentageConfig(percentage float64) (float64, error) {
    56  	maxAllowedSampling := float64(maximumSamplingRate)
    57  	if !strings.HasPrefix(agent.GetCentralConfig().GetAPICDeployment(), "prod") {
    58  		if val := os.Getenv(qaSamplingPercentageEnvVar); val != "" {
    59  			if qaSamplingPercentage, err := strconv.ParseFloat(val, 64); err == nil {
    60  				log.Tracef("Using %s (%s) rather than the default (%d) for non-production", qaSamplingPercentageEnvVar, val, defaultSamplingRate)
    61  				percentage = qaSamplingPercentage
    62  				maxAllowedSampling = 100
    63  			} else {
    64  				log.Tracef("Could not use %s (%s) it is not a proper sampling percentage", qaSamplingPercentageEnvVar, val)
    65  			}
    66  		}
    67  	}
    68  
    69  	// Validate the config to make sure it is not out of bounds
    70  	if percentage < 0 || percentage > maxAllowedSampling {
    71  		return defaultSamplingRate, ErrSamplingCfg.FormatError(maximumSamplingRate, defaultSamplingRate)
    72  	}
    73  
    74  	return percentage, nil
    75  }
    76  
    77  // SetupSampling - set up the global sampling for use by traceability
    78  func SetupSampling(cfg Sampling, offlineMode bool) error {
    79  	var err error
    80  
    81  	if offlineMode {
    82  		cfg = Sampling{
    83  			Percentage: 0,
    84  			PerAPI:     false,
    85  			PerSub:     false,
    86  			OnlyErrors: false,
    87  		}
    88  	} else {
    89  		cfg.Percentage, err = getSamplingPercentageConfig(cfg.Percentage)
    90  		cfg.countMax = int(100 * math.Pow(10, float64(numberOfDecimals(cfg.Percentage))))
    91  		cfg.shouldSampleMax = int(float64(cfg.countMax) * cfg.Percentage / 100)
    92  	}
    93  
    94  	agentSamples = &sample{
    95  		config:        cfg,
    96  		currentCounts: make(map[string]int),
    97  		counterLock:   sync.Mutex{},
    98  	}
    99  
   100  	if err != nil {
   101  		return err
   102  	}
   103  	return nil
   104  }
   105  
   106  // ShouldSampleTransaction - receives the transaction details and returns true to sample it false to not
   107  func ShouldSampleTransaction(details TransactionDetails) (bool, error) {
   108  	if agentSamples == nil {
   109  		return false, ErrGlobalSamplingCfg
   110  	}
   111  	return agentSamples.ShouldSampleTransaction(details), nil
   112  }
   113  
   114  // FilterEvents - returns an array of events that are part of the sample
   115  func FilterEvents(events []publisher.Event) ([]publisher.Event, error) {
   116  	if agentSamples == nil {
   117  		return events, ErrGlobalSamplingCfg
   118  	}
   119  	return agentSamples.FilterEvents(events), nil
   120  }
   121  
   122  func numberOfDecimals(v float64) int {
   123  	dec := decimal.NewFromFloat(v)
   124  	x := dec.Exponent()
   125  	// Exponent returns positive values if number is a multiple of 10
   126  	if x > 0 {
   127  		return 0
   128  	}
   129  	// and negative if it contains non-zero decimals
   130  	return int(x) * (-1)
   131  }