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

     1  package sampling
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/elastic/beats/v7/libbeat/publisher"
     8  )
     9  
    10  // sample - private struct that is used to keep track of the samples being taken
    11  type sample struct {
    12  	config        Sampling
    13  	currentCounts map[string]int
    14  	counterLock   sync.Mutex
    15  }
    16  
    17  // ShouldSampleTransaction - receives the transaction details and returns true to sample it false to not
    18  func (s *sample) ShouldSampleTransaction(details TransactionDetails) bool {
    19  	hasFailedStatus := details.Status == "Failure"
    20  	// sample only failed transaction if OnlyErrors is set to `true` and the transaction summary's status is an error
    21  	if !hasFailedStatus && s.config.OnlyErrors {
    22  		return false
    23  	}
    24  
    25  	sampleGlobal := s.shouldSampleWithCounter(globalCounter)
    26  	perAPIEnabled := s.config.PerAPI && details.APIID != ""
    27  
    28  	if s.config.PerSub && details.SubID != "" {
    29  		apiSamp := false
    30  		if perAPIEnabled {
    31  			apiSamp = s.shouldSampleWithCounter(details.APIID)
    32  		}
    33  		return s.shouldSampleWithCounter(fmt.Sprintf("%s-%s", details.APIID, details.SubID)) || apiSamp
    34  	}
    35  
    36  	if perAPIEnabled {
    37  		return s.shouldSampleWithCounter(details.APIID)
    38  	}
    39  
    40  	return sampleGlobal
    41  }
    42  
    43  func (s *sample) shouldSampleWithCounter(counterName string) bool {
    44  	s.counterLock.Lock()
    45  	defer s.counterLock.Unlock()
    46  	// check if counter needs initiated
    47  	if _, found := s.currentCounts[counterName]; !found {
    48  		s.currentCounts[counterName] = 0
    49  	}
    50  
    51  	// Only sampling on percentage, not currently looking at the details
    52  	shouldSample := false
    53  	if s.currentCounts[counterName] < s.config.shouldSampleMax {
    54  		shouldSample = true
    55  	}
    56  	s.currentCounts[counterName]++
    57  
    58  	// reset the count once we hit 100 * 10^(nb_decimals) messages
    59  	if s.currentCounts[counterName] == s.config.countMax {
    60  		s.currentCounts[counterName] = 0
    61  	}
    62  
    63  	// return if we should sample this transaction
    64  	return shouldSample
    65  }
    66  
    67  // FilterEvents - returns an array of events that are part of the sample
    68  func (s *sample) FilterEvents(events []publisher.Event) []publisher.Event {
    69  	if s.config.Percentage == countMax {
    70  		return events // all events are sampled by default
    71  	}
    72  
    73  	sampledEvents := make([]publisher.Event, 0)
    74  	for _, event := range events {
    75  		if _, sampled := event.Content.Meta[SampleKey]; sampled {
    76  			sampledEvents = append(sampledEvents, event)
    77  		}
    78  	}
    79  
    80  	return sampledEvents
    81  }