Skip to content
This repository has been archived by the owner on Dec 8, 2020. It is now read-only.

Commit

Permalink
metrics: removes delegate field from prometheus timer metric
Browse files Browse the repository at this point in the history
We often need the ability to update label values on a timer that's in
flight and originally we would create a delegate, that was a prometheus
Observer when the timer is started. This isn't needed as we can just
create the observer on the fly when we want to stop the timer and
observe the duration, allowing us to set the labels that can be passed
in.
  • Loading branch information
kyleterry committed Nov 12, 2019
1 parent baa4132 commit afda647
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 27 deletions.
9 changes: 7 additions & 2 deletions instrumentation/metrics/collectors/timer.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package collectors

// TimerHandle is a type used to map running timers for defered observations.
type TimerHandle struct{}

// TimerOptions is used to configure a timer with labels and histogram boundaries.
type TimerOptions struct {
Description string
Labels []string
HistogramBoundaries []float64
}

// Timer is a named metric with labels. It allows concurrent use by returning a handle
// when the timer is started. This handle is used to lookup a running timer to record the duration.
type Timer interface {
WithLabels([]Label) (Timer, error)
// WithLabels returns a new Timer with labels attached.
WithLabels(...Label) Timer
Start() *TimerHandle
ObserveDuration(*TimerHandle)
ObserveDuration(*TimerHandle, ...Label)
}
6 changes: 3 additions & 3 deletions instrumentation/metrics/internal/noop/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import "github.com/puppetlabs/horsehead/v2/instrumentation/metrics/collectors"

type Timer struct{}

func (n Timer) WithLabels([]collectors.Label) (collectors.Timer, error) { return n, nil }
func (n Timer) Start() *collectors.TimerHandle { return &collectors.TimerHandle{} }
func (n Timer) ObserveDuration(*collectors.TimerHandle) {}
func (n Timer) WithLabels(...collectors.Label) collectors.Timer { return n }
func (n Timer) Start() *collectors.TimerHandle { return &collectors.TimerHandle{} }
func (n Timer) ObserveDuration(*collectors.TimerHandle, ...collectors.Label) {}
31 changes: 15 additions & 16 deletions instrumentation/metrics/internal/prometheus/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,48 @@ import (
"sync"

prom "github.com/prometheus/client_golang/prometheus"
"github.com/puppetlabs/horsehead/v2/instrumentation/errors"
"github.com/puppetlabs/horsehead/v2/instrumentation/metrics/collectors"
)

type Timer struct {
vector prom.ObserverVec
delegate prom.Observer
timers map[*collectors.TimerHandle]*prom.Timer
vector prom.ObserverVec
timers map[*collectors.TimerHandle]*prom.Timer
labels []collectors.Label

sync.RWMutex
}

func (t *Timer) WithLabels(labels []collectors.Label) (collectors.Timer, error) {
delegate, err := t.vector.GetMetricWith(convertLabels(labels))
if err != nil {
return nil, errors.NewMetricsUnknownError("prometheus").WithCause(err)
}

func (t *Timer) WithLabels(labels ...collectors.Label) collectors.Timer {
return &Timer{
vector: t.vector,
delegate: delegate,
timers: make(map[*collectors.TimerHandle]*prom.Timer),
}, nil
vector: t.vector,
labels: labels,
timers: make(map[*collectors.TimerHandle]*prom.Timer),
}
}

func (t *Timer) Start() *collectors.TimerHandle {
t.Lock()
defer t.Unlock()

h := &collectors.TimerHandle{}

promt := prom.NewTimer(prom.ObserverFunc(func(v float64) {
t.delegate.Observe(v)
// we can change the label values while a timer is in flight. this allows us to capture
// context about what happened inside a callback.
t.vector.With(convertLabels(t.labels)).Observe(v)
}))

t.timers[h] = promt

return h
}

func (t *Timer) ObserveDuration(h *collectors.TimerHandle) {
func (t *Timer) ObserveDuration(h *collectors.TimerHandle, labels ...collectors.Label) {
t.RLock()
defer t.RUnlock()

t.labels = labels

if promt, ok := t.timers[h]; ok {
promt.ObserveDuration()
}
Expand Down
7 changes: 1 addition & 6 deletions instrumentation/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,7 @@ func (m *Metrics) MustTimer(name string, labels ...collectors.Label) collectors.
return noop.Timer{}
}

t, err = t.WithLabels(labels)
if err != nil {
m.handleError(err)

return noop.Timer{}
}
t = t.WithLabels(labels...)

return t
}
Expand Down

0 comments on commit afda647

Please sign in to comment.