github.com/shogo82148/goa-v1@v1.6.2/metrics.go (about)

     1  //go:build !js
     2  // +build !js
     3  
     4  package goa
     5  
     6  import (
     7  	"regexp"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	metrics "github.com/armon/go-metrics"
    13  )
    14  
    15  const (
    16  	allMatcher      string = "*/*"
    17  	allReplacement  string = "all"
    18  	normalizedToken string = "_"
    19  )
    20  
    21  var (
    22  	// metriks contains current collector
    23  	metriks Collector
    24  
    25  	// metriksMu is mutex for metriks variable
    26  	metriksMu sync.Mutex
    27  
    28  	// invalidCharactersRE is the invert match of validCharactersRE
    29  	invalidCharactersRE = regexp.MustCompile(`[\*/]`)
    30  
    31  	// Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/desc.go
    32  	validCharactersRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`)
    33  )
    34  
    35  // Collector is the interface used for collecting metrics.
    36  type Collector interface {
    37  	AddSample(key []string, val float32)
    38  	EmitKey(key []string, val float32)
    39  	IncrCounter(key []string, val float32)
    40  	MeasureSince(key []string, start time.Time)
    41  	SetGauge(key []string, val float32)
    42  }
    43  
    44  func init() {
    45  	SetMetrics(NewNoOpCollector())
    46  }
    47  
    48  // newNoOpCollector implements Collector, but provides no collection.
    49  type noOpCollector struct{}
    50  
    51  func (*noOpCollector) AddSample(key []string, val float32)        {}
    52  func (*noOpCollector) EmitKey(key []string, val float32)          {}
    53  func (*noOpCollector) IncrCounter(key []string, val float32)      {}
    54  func (*noOpCollector) MeasureSince(key []string, start time.Time) {}
    55  func (*noOpCollector) SetGauge(key []string, val float32)         {}
    56  
    57  // NewNoOpCollector returns a Collector that does no collection.
    58  func NewNoOpCollector() Collector {
    59  	return &noOpCollector{}
    60  }
    61  
    62  // NewNoOpSink returns a NOOP sink.
    63  func NewNoOpSink() metrics.MetricSink {
    64  	return &NoOpSink{}
    65  }
    66  
    67  // NoOpSink default NOOP metrics recorder
    68  type NoOpSink struct{}
    69  
    70  // SetGauge implements metrics.MetricSin.
    71  func (*NoOpSink) SetGauge(key []string, val float32) {}
    72  
    73  // SetGaugeWithLabels implements metrics.MetricSin.
    74  func (*NoOpSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) {}
    75  
    76  // EmitKey implements metrics.MetricSin.
    77  func (*NoOpSink) EmitKey(key []string, val float32) {}
    78  
    79  // IncrCounter implements metrics.MetricSin.
    80  func (*NoOpSink) IncrCounter(key []string, val float32) {}
    81  
    82  // IncrCounterWithLabels implements metrics.MetricSin.
    83  func (*NoOpSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) {}
    84  
    85  // AddSample implements metrics.MetricSin.
    86  func (*NoOpSink) AddSample(key []string, val float32) {}
    87  
    88  // AddSampleWithLabels implements metrics.MetricSin.
    89  func (*NoOpSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) {}
    90  
    91  // Shutdown implements metrics.MetricSin.
    92  func (*NoOpSink) Shutdown() {}
    93  
    94  // NewMetrics initializes goa's metrics instance with the supplied
    95  // configuration and metrics sink
    96  // This method is deprecated and SetMetrics should be used instead.
    97  func NewMetrics(conf *metrics.Config, sink metrics.MetricSink) error {
    98  	m, err := metrics.NewGlobal(conf, sink)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	SetMetrics(m)
   103  
   104  	return nil
   105  }
   106  
   107  // SetMetrics initializes goa's metrics instance with the supplied metrics adapter interface.
   108  func SetMetrics(m Collector) {
   109  	metriksMu.Lock()
   110  	metriks = m
   111  	metriksMu.Unlock()
   112  }
   113  
   114  // GetMetrics returns goa's metrics collector adapter interface.
   115  func GetMetrics() Collector {
   116  	metriksMu.Lock()
   117  	m := metriks
   118  	metriksMu.Unlock()
   119  	return m
   120  }
   121  
   122  // AddSample adds a sample to an aggregated metric
   123  // reporting count, min, max, mean, and std deviation
   124  // Usage:
   125  //     AddSample([]string{"my","namespace","key"}, 15.0)
   126  func AddSample(key []string, val float32) {
   127  	normalizeKeys(key)
   128  
   129  	GetMetrics().AddSample(key, val)
   130  }
   131  
   132  // EmitKey emits a key/value pair
   133  // Usage:
   134  //     EmitKey([]string{"my","namespace","key"}, 15.0)
   135  func EmitKey(key []string, val float32) {
   136  	normalizeKeys(key)
   137  
   138  	GetMetrics().EmitKey(key, val)
   139  }
   140  
   141  // IncrCounter increments the counter named by `key`
   142  // Usage:
   143  //     IncrCounter([]key{"my","namespace","counter"}, 1.0)
   144  func IncrCounter(key []string, val float32) {
   145  	normalizeKeys(key)
   146  
   147  	GetMetrics().IncrCounter(key, val)
   148  }
   149  
   150  // MeasureSince creates a timing metric that records
   151  // the duration of elapsed time since `start`
   152  // Usage:
   153  //     MeasureSince([]string{"my","namespace","action}, time.Now())
   154  // Frequently used in a defer:
   155  //    defer MeasureSince([]string{"my","namespace","action}, time.Now())
   156  func MeasureSince(key []string, start time.Time) {
   157  	normalizeKeys(key)
   158  
   159  	GetMetrics().MeasureSince(key, start)
   160  }
   161  
   162  // SetGauge sets the named gauge to the specified value
   163  // Usage:
   164  //     SetGauge([]string{"my","namespace"}, 2.0)
   165  func SetGauge(key []string, val float32) {
   166  	normalizeKeys(key)
   167  
   168  	GetMetrics().SetGauge(key, val)
   169  }
   170  
   171  // This function is used to make metric names safe for all metric services. Specifically, prometheus does
   172  // not support * or / in metric names.
   173  func normalizeKeys(key []string) {
   174  	for i, k := range key {
   175  		if !validCharactersRE.MatchString(k) {
   176  			// first replace */* with all
   177  			k = strings.Replace(k, allMatcher, allReplacement, -1)
   178  
   179  			// now replace all other invalid characters with a safe one.
   180  			key[i] = invalidCharactersRE.ReplaceAllString(k, normalizedToken)
   181  		}
   182  	}
   183  }