110 lines
2.8 KiB
Go

// Package metrics defines a concurrently-accessible metrics collector.
//
// A *metrics.M value exports methods to track integer counters and maximum
// values. A metric has a caller-assigned string name that is not interpreted
// by the collector except to locate its stored value.
package metrics
import "sync"
// An M collects counters and maximum value trackers. A nil *M is valid, and
// discards all metrics. The methods of an *M are safe for concurrent use by
// multiple goroutines.
type M struct {
mu sync.Mutex
counter map[string]int64
maxVal map[string]int64
label map[string]string
}
// New creates a new, empty metrics collector.
func New() *M {
return &M{
counter: make(map[string]int64),
maxVal: make(map[string]int64),
label: make(map[string]string),
}
}
// Count adds n to the current value of the counter named, defining the counter
// if it does not already exist.
func (m *M) Count(name string, n int64) {
if m != nil {
m.mu.Lock()
defer m.mu.Unlock()
m.counter[name] += n
}
}
// SetMaxValue sets the maximum value metric named to the greater of n and its
// current value, defining the value if it does not already exist.
func (m *M) SetMaxValue(name string, n int64) {
if m != nil {
m.mu.Lock()
defer m.mu.Unlock()
if old, ok := m.maxVal[name]; !ok || n > old {
m.maxVal[name] = n
}
}
}
// CountAndSetMax adds n to the current value of the counter named, and also
// updates a max value tracker with the same name in a single step.
func (m *M) CountAndSetMax(name string, n int64) {
if m != nil {
m.mu.Lock()
defer m.mu.Unlock()
if old, ok := m.maxVal[name]; !ok || n > old {
m.maxVal[name] = n
}
m.counter[name] += n
}
}
// SetLabel sets the specified label to value. If value == "" the label is
// removed from the set.
func (m *M) SetLabel(name, value string) {
if m != nil {
m.mu.Lock()
defer m.mu.Unlock()
if value == "" {
delete(m.label, name)
} else {
m.label[name] = value
}
}
}
// Snapshot copies an atomic snapshot of the collected metrics into the non-nil
// fields of the provided snapshot value. Only the fields of snap that are not
// nil are snapshotted.
func (m *M) Snapshot(snap Snapshot) {
if m != nil {
m.mu.Lock()
defer m.mu.Unlock()
if c := snap.Counter; c != nil {
for name, val := range m.counter {
c[name] = val
}
}
if v := snap.MaxValue; v != nil {
for name, val := range m.maxVal {
v[name] = val
}
}
if v := snap.Label; v != nil {
for name, val := range m.label {
v[name] = val
}
}
}
}
// A Snapshot represents a point-in-time snapshot of a metrics collector. The
// fields of this type are filled in by the Snapshot method of *M.
type Snapshot struct {
Counter map[string]int64
MaxValue map[string]int64
Label map[string]string
}