github.com/orofarne/hammy@v0.0.0-20130409105742-374fadfd6ecb/src/hammy/metrics.go (about)

     1  package hammy
     2  
     3  import (
     4  	"expvar"
     5  	"time"
     6  	"sync"
     7  	"encoding/json"
     8  )
     9  
    10  type MetricType int
    11  
    12  const (
    13  	METRIC_COUNTER MetricType = iota
    14  	METRIC_TIMER
    15  )
    16  
    17  type metric struct {
    18  	Type MetricType
    19  	Name string
    20  	Value interface{}
    21  }
    22  
    23  type metricState struct {
    24  	Type MetricType
    25  	Value interface{}
    26  }
    27  
    28  type metricTimerState struct {
    29  	Counter uint64
    30  	Sum uint64
    31  }
    32  
    33  // Namespaced set of metrics
    34  type MetricSet struct {
    35  	results map[string]interface{}
    36  	states map[string]*metricState
    37  	metricChan chan metric
    38  	mutex sync.Mutex
    39  	tickTime time.Duration
    40  	prevTick time.Time
    41  }
    42  
    43  // Creates new metric set or panic if namespace exists
    44  func NewMetricSet(namespace string, tickTime time.Duration) *MetricSet {
    45  	ms := new(MetricSet)
    46  	ms.results = make(map[string]interface{})
    47  	ms.states = make(map[string]*metricState)
    48  	ms.metricChan = make(chan metric, 100)
    49  	ms.tickTime = tickTime
    50  
    51  	var exportFunc expvar.Func
    52  	exportFunc = func() interface{} {
    53  		return ms.getVars()
    54  	}
    55  	expvar.Publish(namespace, exportFunc)
    56  
    57  	go ms.collect()
    58  
    59  	return ms
    60  }
    61  
    62  func (ms *MetricSet) getVars() interface{} {
    63  	ms.mutex.Lock()
    64  	defer func() { ms.mutex.Unlock() }()
    65  	//FIXME achtung!
    66  	var res interface{}
    67  	buf, err := json.Marshal(ms.results)
    68  	if err != nil { panic(err) }
    69  	err = json.Unmarshal(buf, &res)
    70  	if err != nil { panic(err) }
    71  	return res
    72  }
    73  
    74  func (ms *MetricSet) collect() {
    75  	ticker := time.Tick(ms.tickTime)
    76  	ms.prevTick = time.Now()
    77  
    78  	for {
    79  		select {
    80  			case m := <- ms.metricChan:
    81  				ms.collectMetric(m)
    82  			case t := <- ticker:
    83  				Δ := t.Sub(ms.prevTick)
    84  				ms.prevTick = t
    85  				ms.doResults(Δ)
    86  		}
    87  	}
    88  }
    89  
    90  func (ms *MetricSet) collectMetric(m metric) {
    91  	state, found := ms.states[m.Name]
    92  
    93  	if !found {
    94  		state = &metricState{
    95  			Type: m.Type,
    96  		}
    97  	} else {
    98  		if state.Type != m.Type {
    99  			panic("Invalid metric type")
   100  		}
   101  	}
   102  
   103  	switch m.Type {
   104  		case METRIC_COUNTER:
   105  			if state.Value == nil {
   106  				state.Value = m.Value.(uint64)
   107  			} else {
   108  				state.Value = state.Value.(uint64) + m.Value.(uint64)
   109  			}
   110  		case METRIC_TIMER:
   111  			τ := uint64(m.Value.(time.Duration).Nanoseconds())
   112  			if state.Value == nil {
   113  				state.Value = metricTimerState{
   114  					Counter: 1,
   115  					Sum: τ,
   116  				}
   117  			} else {
   118  				pState := state.Value.(metricTimerState)
   119  				state.Value = metricTimerState{
   120  					Counter: (pState.Counter + 1),
   121  					Sum: (pState.Sum + τ),
   122  				}
   123  			}
   124  	}
   125  
   126  	ms.states[m.Name] = state
   127  }
   128  
   129  func (ms *MetricSet) doResults(Δ time.Duration) {
   130  	ms.mutex.Lock()
   131  	defer func() { ms.mutex.Unlock() }()
   132  
   133  	for k, v := range ms.states {
   134  		switch v.Type {
   135  			case METRIC_COUNTER:
   136  				ms.results[k + "#rps"] = float64(v.Value.(uint64)) / Δ.Seconds()
   137  				v.Value = uint64(0)
   138  			case METRIC_TIMER:
   139  				tState := v.Value.(metricTimerState)
   140  				var τ float64
   141  				if tState.Counter != 0 {
   142  					τ = (float64(tState.Sum) / float64(tState.Counter)) / float64(1000000000)
   143  				} else {
   144  					τ = 0
   145  				}
   146  				ms.results[k + "#rps"] = float64(tState.Counter) / Δ.Seconds()
   147  				ms.results[k + "_avgtime#s"] = τ
   148  				v.Value = metricTimerState{}
   149  		}
   150  	}
   151  }
   152  
   153  type CounterMetric struct {
   154  	name string
   155  	c chan metric
   156  }
   157  
   158  func (ms *MetricSet) NewCounter(name string) *CounterMetric {
   159  	m := new(CounterMetric)
   160  	m.name = name
   161  	m.c = ms.metricChan
   162  	return m
   163  }
   164  
   165  func (m *CounterMetric)Add(n uint64) {
   166  	m.c <- metric{
   167  		Type: METRIC_COUNTER,
   168  		Name: m.name,
   169  		Value: n,
   170  	}
   171  }
   172  
   173  type TimerMetric struct {
   174  	name string
   175  	c chan metric
   176  }
   177  
   178  func (ms *MetricSet) NewTimer(name string) *TimerMetric {
   179  	m := new(TimerMetric)
   180  	m.name = name
   181  	m.c = ms.metricChan
   182  	return m
   183  }
   184  
   185  func (m *TimerMetric) Add(τ time.Duration) {
   186  	m.c <- metric{
   187  		Type: METRIC_TIMER,
   188  		Name: m.name,
   189  		Value: τ,
   190  	}
   191  }
   192  
   193  type TimerMetricObservation struct {
   194  	m *TimerMetric
   195  	beginTime time.Time
   196  }
   197  
   198  func (m *TimerMetric) NewObservation() *TimerMetricObservation {
   199  	τ := new(TimerMetricObservation)
   200  	τ.m = m
   201  	τ.beginTime = time.Now()
   202  	return τ
   203  }
   204  
   205  func (τ *TimerMetricObservation) End() {
   206  	Δ := time.Since(τ.beginTime)
   207  	τ.m.c <- metric{
   208  		Type: METRIC_TIMER,
   209  		Name: τ.m.name,
   210  		Value: Δ,
   211  	}
   212  }