github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/metrics.go (about)

     1  // +build !js
     2  
     3  package goa
     4  
     5  import (
     6  	"regexp"
     7  	"strings"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"github.com/armon/go-metrics"
    12  )
    13  
    14  const (
    15  	allMatcher      string = "*/*"
    16  	allReplacement  string = "all"
    17  	normalizedToken string = "_"
    18  )
    19  
    20  var (
    21  	// metriks atomic value storage
    22  	metriks atomic.Value
    23  
    24  	// invalidCharactersRE is the invert match of validCharactersRE
    25  	invalidCharactersRE = regexp.MustCompile(`[\*/]`)
    26  
    27  	// Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/desc.go
    28  	validCharactersRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`)
    29  )
    30  
    31  func init() {
    32  	m, err := metrics.New(metrics.DefaultConfig("service"), NewNoOpSink())
    33  	if err != nil {
    34  		panic("Unable to instantiate default metrics sink")
    35  	}
    36  
    37  	SetMetrics(m)
    38  }
    39  
    40  // NewNoOpSink returns a NOOP sink.
    41  func NewNoOpSink() metrics.MetricSink {
    42  	return &NoOpSink{}
    43  }
    44  
    45  // NoOpSink default NOOP metrics recorder
    46  type NoOpSink struct{}
    47  
    48  // SetGauge method
    49  func (md *NoOpSink) SetGauge(key []string, val float32) {}
    50  
    51  // EmitKey method
    52  func (md *NoOpSink) EmitKey(key []string, val float32) {}
    53  
    54  // IncrCounter method
    55  func (md *NoOpSink) IncrCounter(key []string, val float32) {}
    56  
    57  // AddSample method
    58  func (md *NoOpSink) AddSample(key []string, val float32) {}
    59  
    60  // MeasureSince method
    61  func (md *NoOpSink) MeasureSince(key []string, start time.Time) {}
    62  
    63  // NewMetrics initializes goa's metrics instance with the supplied
    64  // configuration and metrics sink
    65  // This method is deprecated and SetMetrics should be used instead.
    66  func NewMetrics(conf *metrics.Config, sink metrics.MetricSink) (err error) {
    67  	m, err := metrics.NewGlobal(conf, sink)
    68  	SetMetrics(m)
    69  
    70  	return nil
    71  }
    72  
    73  // SetMetrics initializes goa's metrics instance with the supplied metrics adapter interface.
    74  func SetMetrics(m *metrics.Metrics) {
    75  	metriks.Store(m)
    76  }
    77  
    78  // AddSample adds a sample to an aggregated metric
    79  // reporting count, min, max, mean, and std deviation
    80  // Usage:
    81  //     AddSample([]string{"my","namespace","key"}, 15.0)
    82  func AddSample(key []string, val float32) {
    83  	normalizeKeys(key)
    84  
    85  	metriks.Load().(*metrics.Metrics).AddSample(key, val)
    86  }
    87  
    88  // EmitKey emits a key/value pair
    89  // Usage:
    90  //     EmitKey([]string{"my","namespace","key"}, 15.0)
    91  func EmitKey(key []string, val float32) {
    92  	normalizeKeys(key)
    93  
    94  	metriks.Load().(*metrics.Metrics).EmitKey(key, val)
    95  }
    96  
    97  // IncrCounter increments the counter named by `key`
    98  // Usage:
    99  //     IncrCounter([]key{"my","namespace","counter"}, 1.0)
   100  func IncrCounter(key []string, val float32) {
   101  	normalizeKeys(key)
   102  
   103  	metriks.Load().(*metrics.Metrics).IncrCounter(key, val)
   104  }
   105  
   106  // MeasureSince creates a timing metric that records
   107  // the duration of elapsed time since `start`
   108  // Usage:
   109  //     MeasureSince([]string{"my","namespace","action"}, time.Now())
   110  // Frequently used in a defer:
   111  //    defer MeasureSince([]string{"my","namespace","action"}, time.Now())
   112  func MeasureSince(key []string, start time.Time) {
   113  	normalizeKeys(key)
   114  
   115  	metriks.Load().(*metrics.Metrics).MeasureSince(key, start)
   116  }
   117  
   118  // SetGauge sets the named gauge to the specified value
   119  // Usage:
   120  //     SetGauge([]string{"my","namespace"}, 2.0)
   121  func SetGauge(key []string, val float32) {
   122  	normalizeKeys(key)
   123  
   124  	metriks.Load().(*metrics.Metrics).SetGauge(key, val)
   125  }
   126  
   127  // This function is used to make metric names safe for all metric services. Specifically, prometheus does
   128  // not support * or / in metric names.
   129  func normalizeKeys(key []string) {
   130  	for i, k := range key {
   131  		if !validCharactersRE.MatchString(k) {
   132  			// first replace */* with all
   133  			k = strings.Replace(k, allMatcher, allReplacement, -1)
   134  
   135  			// now replace all other invalid characters with a safe one.
   136  			key[i] = invalidCharactersRE.ReplaceAllString(k, normalizedToken)
   137  		}
   138  	}
   139  }