Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use set of meters and update documentation #1

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,22 @@ t.Update(47)
Register() is not threadsafe. For threadsafe metric registration use
GetOrRegister:

```
```go
t := metrics.GetOrRegisterTimer("account.create.latency", nil)
t.Time(func() {})
t.Update(47)
```

**NOTE:** Be sure to either unregister meters and timers otherwise and they will
leak memory:

```go
// Will call Stop() on the Meter to allow for garbage collection
metrics.Unregister("quux")
// Or similarly for a Timer that embeds a Meter
metrics.Unregister("bang")
```

Periodically log every metric in human-readable form to standard error:

```go
Expand Down
28 changes: 13 additions & 15 deletions meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type Meter interface {

// GetOrRegisterMeter returns an existing Meter or constructs and registers a
// new StandardMeter.
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
func GetOrRegisterMeter(name string, r Registry) Meter {
if nil == r {
r = DefaultRegistry
Expand All @@ -28,15 +30,15 @@ func GetOrRegisterMeter(name string, r Registry) Meter {
}

// NewMeter constructs a new StandardMeter and launches a goroutine.
// Be sure to call Stop() once the meter is of no use to allow for garbage collection.
func NewMeter() Meter {
if UseNilMetrics {
return NilMeter{}
}
m := newStandardMeter()
arbiter.Lock()
defer arbiter.Unlock()
m.id = arbiter.newID()
arbiter.meters[m.id] = m
arbiter.meters[m] = struct{}{}
if !arbiter.started {
arbiter.started = true
go arbiter.tick()
Expand All @@ -46,6 +48,8 @@ func NewMeter() Meter {

// NewMeter constructs and registers a new StandardMeter and launches a
// goroutine.
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
func NewRegisteredMeter(name string, r Registry) Meter {
c := NewMeter()
if nil == r {
Expand Down Expand Up @@ -125,7 +129,6 @@ type StandardMeter struct {
a1, a5, a15 EWMA
startTime time.Time
stopped bool
id int64
}

func newStandardMeter() *StandardMeter {
Expand All @@ -138,15 +141,15 @@ func newStandardMeter() *StandardMeter {
}
}

// Stop stops the meter, Mark() will panic if you use it after being stopped.
// Stop stops the meter, Mark() will be a no-op if you use it after being stopped.
func (m *StandardMeter) Stop() {
m.lock.Lock()
stopped := m.stopped
m.stopped = true
m.lock.Unlock()
if !stopped {
arbiter.Lock()
delete(arbiter.meters, m.id)
delete(arbiter.meters, m)
arbiter.Unlock()
}
}
Expand Down Expand Up @@ -231,15 +234,16 @@ func (m *StandardMeter) tick() {
m.updateSnapshot()
}

// meterArbiter ticks meters every 5s from a single goroutine.
// meters are references in a set for future stopping.
type meterArbiter struct {
sync.RWMutex
started bool
meters map[int64]*StandardMeter
meters map[*StandardMeter]struct{}
ticker *time.Ticker
id int64
}

var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[int64]*StandardMeter)}
var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[*StandardMeter]struct{})}

// Ticks meters on the scheduled interval
func (ma *meterArbiter) tick() {
Expand All @@ -251,16 +255,10 @@ func (ma *meterArbiter) tick() {
}
}

// should only be called with Lock() held
func (ma *meterArbiter) newID() int64 {
ma.id++
return ma.id
}

func (ma *meterArbiter) tickMeters() {
ma.RLock()
defer ma.RUnlock()
for _, meter := range ma.meters {
for meter := range ma.meters {
meter.tick()
}
}
5 changes: 2 additions & 3 deletions meter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ func TestGetOrRegisterMeter(t *testing.T) {
func TestMeterDecay(t *testing.T) {
ma := meterArbiter{
ticker: time.NewTicker(time.Millisecond),
meters: make(map[int64]*StandardMeter),
meters: make(map[*StandardMeter]struct{}),
}
m := newStandardMeter()
m.id = ma.newID()
ma.meters[m.id] = m
ma.meters[m] = struct{}{}
go ma.tick()
m.Mark(1)
rateMean := m.RateMean()
Expand Down
6 changes: 6 additions & 0 deletions timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type Timer interface {

// GetOrRegisterTimer returns an existing Timer or constructs and registers a
// new StandardTimer.
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
func GetOrRegisterTimer(name string, r Registry) Timer {
if nil == r {
r = DefaultRegistry
Expand All @@ -37,6 +39,7 @@ func GetOrRegisterTimer(name string, r Registry) Timer {
}

// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter.
// Be sure to call Stop() once the timer is of no use to allow for garbage collection.
func NewCustomTimer(h Histogram, m Meter) Timer {
if UseNilMetrics {
return NilTimer{}
Expand All @@ -48,6 +51,8 @@ func NewCustomTimer(h Histogram, m Meter) Timer {
}

// NewRegisteredTimer constructs and registers a new StandardTimer.
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
func NewRegisteredTimer(name string, r Registry) Timer {
c := NewTimer()
if nil == r {
Expand All @@ -59,6 +64,7 @@ func NewRegisteredTimer(name string, r Registry) Timer {

// NewTimer constructs a new StandardTimer using an exponentially-decaying
// sample with the same reservoir size and alpha as UNIX load averages.
// Be sure to call Stop() once the timer is of no use to allow for garbage collection.
func NewTimer() Timer {
if UseNilMetrics {
return NilTimer{}
Expand Down