github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/plugin/cb/stats_collector.go (about)

     1  package cb
     2  
     3  import (
     4  	"errors"
     5  
     6  	metricCollector "github.com/afex/hystrix-go/hystrix/metric_collector"
     7  	"github.com/hellofresh/stats-go/bucket"
     8  	"github.com/hellofresh/stats-go/client"
     9  	"github.com/hellofresh/stats-go/timer"
    10  	log "github.com/sirupsen/logrus"
    11  )
    12  
    13  // StatsCollector fulfills the metricCollector interface allowing users to ship circuit
    14  // stats to a metric backend. To use users must call InitializeStatsCollector before
    15  // circuits are started.
    16  type StatsCollector struct {
    17  	client                  client.Client
    18  	section                 string
    19  	name                    string
    20  	circuitOpenPrefix       string
    21  	attemptsPrefix          string
    22  	errorsPrefix            string
    23  	successesPrefix         string
    24  	failuresPrefix          string
    25  	rejectsPrefix           string
    26  	shortCircuitsPrefix     string
    27  	timeoutsPrefix          string
    28  	fallbackSuccessesPrefix string
    29  	fallbackFailuresPrefix  string
    30  	canceledPrefix          string
    31  	deadlinePrefix          string
    32  	totalDurationPrefix     string
    33  	runDurationPrefix       string
    34  }
    35  
    36  // NewCollectorRegistry returns a function to be registerd with metricCollector.Registry.Register(NewCollectorRegistry).
    37  func NewCollectorRegistry(client client.Client) func(string) metricCollector.MetricCollector {
    38  	return func(name string) metricCollector.MetricCollector {
    39  		c, err := NewStatsCollector(name, client)
    40  		if err != nil {
    41  			log.WithError(err).Error("could not initialize the stats collector")
    42  		}
    43  
    44  		return c
    45  	}
    46  }
    47  
    48  // NewStatsCollector creates a collector for a specific circuit. The
    49  // prefix given to this circuit will be {config.Prefix}.{circuit_name}.{metric}.
    50  // Circuits with "/" in their names will have them replaced with ".".
    51  func NewStatsCollector(name string, client client.Client) (*StatsCollector, error) {
    52  	if client == nil {
    53  		return nil, errors.New("metrics client must be initialized before circuits are created")
    54  	}
    55  
    56  	return &StatsCollector{
    57  		client:                  client,
    58  		name:                    name,
    59  		circuitOpenPrefix:       "circuitOpen",
    60  		attemptsPrefix:          "attempts",
    61  		errorsPrefix:            "errors",
    62  		successesPrefix:         "successes",
    63  		failuresPrefix:          "failures",
    64  		rejectsPrefix:           "rejects",
    65  		shortCircuitsPrefix:     "shortCircuits",
    66  		timeoutsPrefix:          "timeouts",
    67  		fallbackSuccessesPrefix: "fallbackSuccesses",
    68  		fallbackFailuresPrefix:  "fallbackFailures",
    69  		canceledPrefix:          "contextCanceled",
    70  		deadlinePrefix:          "contextDeadlineExceeded",
    71  		totalDurationPrefix:     "totalDuration",
    72  		runDurationPrefix:       "runDuration",
    73  	}, nil
    74  }
    75  
    76  // Update metrics
    77  func (g *StatsCollector) Update(r metricCollector.MetricResult) {
    78  	if r.Successes > 0 {
    79  		g.client.TrackState(g.section, bucket.MetricOperation{g.name, g.circuitOpenPrefix}, 0)
    80  	} else if r.ShortCircuits > 0 {
    81  		g.client.TrackState(g.section, bucket.MetricOperation{g.name, g.circuitOpenPrefix}, 1)
    82  	}
    83  
    84  	g.incrementCounterMetric(g.attemptsPrefix, r.Attempts)
    85  	g.incrementCounterMetric(g.errorsPrefix, r.Errors)
    86  	g.incrementCounterMetric(g.successesPrefix, r.Successes)
    87  	g.incrementCounterMetric(g.failuresPrefix, r.Failures)
    88  	g.incrementCounterMetric(g.rejectsPrefix, r.Rejects)
    89  	g.incrementCounterMetric(g.shortCircuitsPrefix, r.ShortCircuits)
    90  	g.incrementCounterMetric(g.timeoutsPrefix, r.Timeouts)
    91  	g.incrementCounterMetric(g.fallbackSuccessesPrefix, r.FallbackSuccesses)
    92  	g.incrementCounterMetric(g.fallbackFailuresPrefix, r.FallbackFailures)
    93  	g.incrementCounterMetric(g.canceledPrefix, r.ContextCanceled)
    94  	g.incrementCounterMetric(g.deadlinePrefix, r.ContextDeadlineExceeded)
    95  
    96  	g.client.TrackOperation(
    97  		g.section,
    98  		bucket.MetricOperation{g.name, g.totalDurationPrefix},
    99  		timer.NewDuration(r.TotalDuration),
   100  		r.Successes > 0,
   101  	)
   102  
   103  	g.client.TrackOperation(
   104  		g.section,
   105  		bucket.MetricOperation{g.name, g.runDurationPrefix},
   106  		timer.NewDuration(r.RunDuration),
   107  		r.Successes > 0,
   108  	)
   109  }
   110  
   111  // Reset is a noop operation in this collector.
   112  func (g *StatsCollector) Reset() {}
   113  
   114  func (g *StatsCollector) incrementCounterMetric(prefix string, i float64) {
   115  	if i == 0 {
   116  		return
   117  	}
   118  	g.client.TrackMetricN(g.section, bucket.MetricOperation{g.name, prefix}, int(i))
   119  }