Skip to content
This repository has been archived by the owner on Oct 2, 2022. It is now read-only.

Commit

Permalink
0.9.5: Added extended metrics (#9)
Browse files Browse the repository at this point in the history
This release adds a `WithLabel` method to create metrics primed with certain labels. This can be used when passing labels between modules.
  • Loading branch information
Janos Pasztor authored Dec 29, 2020
1 parent befe233 commit e923fd7
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.9.5: Added extended metrics

This release adds a `WithLabel` method to create metrics primed with certain labels. This can be used when passing labels between modules.

## 0.9.4: Add `Must*` methods

This release adds methods starting with `Must` that panic instead of throwing an error.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ The following rules apply and will cause a `panic` if violated:
- Label names and values cannot be empty.
- The `country` label name is reserved for GeoIP usage.

The metrics also have a `WithLabels()` method that allow for creating a copy of a metric already primed with a set of labels. This can be used when passing metrics to other modules that need to be scoped.

## Using the metrics server

The metrics server exposes the collected metrics on an HTTP webserver in the Prometheus / OpenMetrics format. It requires the [service library](https://github.com/containerssh/service) and a logger from the [log library](https://github.com/containerssh/log) to work properly:
Expand Down
52 changes: 44 additions & 8 deletions collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,34 +151,34 @@ var CounterCannotBeIncrementedByNegative = errors.New("a counter cannot be incre
// Collector is the main interface for interacting with the metrics collector.
type Collector interface {
// CreateCounter creates a monotonic (increasing) counter with the specified name and help text.
CreateCounter(name string, unit string, help string) (SimpleCounter, error)
CreateCounter(name string, unit string, help string) (Counter, error)

// MustCreateCounter creates a monotonic (increasing) counter with the specified name and help text. Panics if an
// error occurs.
MustCreateCounter(name string, unit string, help string) SimpleCounter
MustCreateCounter(name string, unit string, help string) Counter

// CreateCounterGeo creates a monotonic (increasing) counter that is labeled with the country from the GeoIP lookup
// with the specified name and help text.
CreateCounterGeo(name string, unit string, help string) (SimpleGeoCounter, error)
CreateCounterGeo(name string, unit string, help string) (GeoCounter, error)

// MustCreateCounterGeo creates a monotonic (increasing) counter that is labeled with the country from the GeoIP
// lookup with the specified name and help text. Panics if an error occurs.
MustCreateCounterGeo(name string, unit string, help string) SimpleGeoCounter
MustCreateCounterGeo(name string, unit string, help string) GeoCounter

// CreateGauge creates a freely modifiable numeric gauge with the specified name and help text.
CreateGauge(name string, unit string, help string) (SimpleGauge, error)
CreateGauge(name string, unit string, help string) (Gauge, error)

// MustCreateGauge creates a freely modifiable numeric gauge with the specified name and help text. Panics if an
// error occurs.
MustCreateGauge(name string, unit string, help string) SimpleGauge
MustCreateGauge(name string, unit string, help string) Gauge

// CreateGaugeGeo creates a freely modifiable numeric gauge that is labeled with the country from the GeoIP lookup
// with the specified name and help text.
CreateGaugeGeo(name string, unit string, help string) (SimpleGeoGauge, error)
CreateGaugeGeo(name string, unit string, help string) (GeoGauge, error)

// MustCreateGaugeGeo creates a freely modifiable numeric gauge that is labeled with the country from the GeoIP
// lookup with the specified name and help text. Panics if an error occurs.
MustCreateGaugeGeo(name string, unit string, help string) SimpleGeoGauge
MustCreateGaugeGeo(name string, unit string, help string) GeoGauge

// ListMetrics returns a list of metrics metadata stored in the collector.
ListMetrics() []Metric
Expand All @@ -204,6 +204,15 @@ type SimpleCounter interface {
IncrementBy(by float64, labels ...MetricLabel) error
}

// Counter extends the SimpleCounter interface by adding a WithLabels function to create a copy of the counter that
// is primed with a set of labels.
type Counter interface {
SimpleCounter

// WithLabels adds labels to the counter
WithLabels(labels ...MetricLabel) Counter
}

// SimpleGeoCounter is a simple counter that can only be incremented and is labeled with the country from a GeoIP
// lookup.
type SimpleGeoCounter interface {
Expand All @@ -219,6 +228,15 @@ type SimpleGeoCounter interface {
IncrementBy(ip net.IP, by float64, labels ...MetricLabel) error
}

// GeoCounter extends the SimpleGeoCounter interface by adding a WithLabels function to create a copy of the counter
// that is primed with a set of labels.
type GeoCounter interface {
SimpleGeoCounter

// WithLabels adds labels to the counter
WithLabels(labels ...MetricLabel) GeoCounter
}

// SimpleGauge is a metric that can be incremented and decremented.
type SimpleGauge interface {
// Increment increments the counter by 1.
Expand Down Expand Up @@ -247,6 +265,15 @@ type SimpleGauge interface {
Set(value float64, labels ...MetricLabel)
}

// Gauge extends the SimpleGauge interface by adding a WithLabels function to create a copy of the counter
// that is primed with a set of labels.
type Gauge interface {
SimpleGauge

// WithLabels adds labels to the counter
WithLabels(labels ...MetricLabel) Gauge
}

// SimpleGeoGauge is a metric that can be incremented and decremented and is labeled by the country from a GeoIP lookup.
type SimpleGeoGauge interface {
// Increment increments the counter for the country from the specified ip by 1.
Expand Down Expand Up @@ -274,3 +301,12 @@ type SimpleGeoGauge interface {
// - labels is a set of labels to apply. Can be created using the Label function.
Set(ip net.IP, value float64, labels ...MetricLabel)
}

// GeoGauge extends the SimpleGeoGauge interface by adding a WithLabels function to create a copy of the counter
// that is primed with a set of labels.
type GeoGauge interface {
SimpleGeoGauge

// WithLabels adds labels to the counter
WithLabels(labels ...MetricLabel) GeoGauge
}
16 changes: 8 additions & 8 deletions collector_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,31 @@ type collector struct {
values map[string]*metricValue
}

func (c *collector) MustCreateCounter(name string, unit string, help string) SimpleCounter {
func (c *collector) MustCreateCounter(name string, unit string, help string) Counter {
counter, err := c.CreateCounter(name, unit, help)
if err != nil {
panic(err)
}
return counter
}

func (c *collector) MustCreateCounterGeo(name string, unit string, help string) SimpleGeoCounter {
func (c *collector) MustCreateCounterGeo(name string, unit string, help string) GeoCounter {
counter, err := c.CreateCounterGeo(name, unit, help)
if err != nil {
panic(err)
}
return counter
}

func (c *collector) MustCreateGauge(name string, unit string, help string) SimpleGauge {
func (c *collector) MustCreateGauge(name string, unit string, help string) Gauge {
gauge, err := c.CreateGauge(name, unit, help)
if err != nil {
panic(err)
}
return gauge
}

func (c *collector) MustCreateGaugeGeo(name string, unit string, help string) SimpleGeoGauge {
func (c *collector) MustCreateGaugeGeo(name string, unit string, help string) GeoGauge {
gauge, err := c.CreateGaugeGeo(name, unit, help)
if err != nil {
panic(err)
Expand Down Expand Up @@ -72,7 +72,7 @@ func (c *collector) createMetric(name string, unit string, help string, metricTy
return nil
}

func (c *collector) CreateCounter(name string, unit string, help string) (SimpleCounter, error) {
func (c *collector) CreateCounter(name string, unit string, help string) (Counter, error) {
if err := c.createMetric(name, unit, help, MetricTypeCounter); err != nil {
return nil, err
}
Expand All @@ -82,7 +82,7 @@ func (c *collector) CreateCounter(name string, unit string, help string) (Simple
}, nil
}

func (c *collector) CreateCounterGeo(name string, unit string, help string) (SimpleGeoCounter, error) {
func (c *collector) CreateCounterGeo(name string, unit string, help string) (GeoCounter, error) {
if err := c.createMetric(name, unit, help, MetricTypeCounter); err != nil {
return nil, err
}
Expand All @@ -92,7 +92,7 @@ func (c *collector) CreateCounterGeo(name string, unit string, help string) (Sim
}, nil
}

func (c *collector) CreateGauge(name string, unit string, help string) (SimpleGauge, error) {
func (c *collector) CreateGauge(name string, unit string, help string) (Gauge, error) {
if err := c.createMetric(name, unit, help, MetricTypeGauge); err != nil {
return nil, err
}
Expand All @@ -102,7 +102,7 @@ func (c *collector) CreateGauge(name string, unit string, help string) (SimpleGa
}, nil
}

func (c *collector) CreateGaugeGeo(name string, unit string, help string) (SimpleGeoGauge, error) {
func (c *collector) CreateGaugeGeo(name string, unit string, help string) (GeoGauge, error) {
if err := c.createMetric(name, unit, help, MetricTypeGauge); err != nil {
return nil, err
}
Expand Down
13 changes: 12 additions & 1 deletion counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package metrics
type counterImpl struct {
name string
collector *collector
labels []MetricLabel
}

func (c *counterImpl) Increment(labels ...MetricLabel) {
Expand All @@ -17,8 +18,18 @@ func (c *counterImpl) IncrementBy(by float64, labels ...MetricLabel) error {
return CounterCannotBeIncrementedByNegative
}

realLabels := metricLabels(labels).toMap()
realLabels := metricLabels(
append(c.labels, labels...),
).toMap()
value := c.collector.get(c.name, realLabels)
c.collector.set(c.name, realLabels, value+by)
return nil
}

func (c *counterImpl) WithLabels(labels ...MetricLabel) Counter {
return &counterImpl{
name: c.name,
collector: c.collector,
labels: append(c.labels, labels...),
}
}
21 changes: 21 additions & 0 deletions counter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,24 @@ func TestCounter(t *testing.T) {
}
}
}

// TestCounter tests the functionality of counters
func TestCounterLabel(t *testing.T) {
collector := metrics.New(&geoIpLookupProvider{})
counter, err := collector.CreateCounter("test", "seconds", "Hello world!")
assert.Nil(t, err, "creating counter returned an error")
counter.Increment()
newCounter := counter.WithLabels(metrics.Label("foo", "bar"))
newCounter.Increment(metrics.Label("baz", "bar"))

metric := collector.GetMetric("test")
assert.Equal(t, 2, len(metric))

assert.Equal(t, "test", metric[0].Name)
assert.Equal(t, float64(1), metric[0].Value)
assert.Equal(t, 0, len(metric[0].Labels))

assert.Equal(t, "test", metric[1].Name)
assert.Equal(t, float64(1), metric[1].Value)
assert.Equal(t, 2, len(metric[1].Labels))
}
13 changes: 12 additions & 1 deletion countergeo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
type counterGeoImpl struct {
name string
collector *collector
labels []MetricLabel
}

func (c *counterGeoImpl) Increment(ip net.IP, labels ...MetricLabel) {
Expand All @@ -21,10 +22,20 @@ func (c *counterGeoImpl) IncrementBy(ip net.IP, by float64, labels ...MetricLabe
return CounterCannotBeIncrementedByNegative
}

realLabels := metricLabels(labels).toMap()
realLabels := metricLabels(
append(c.labels, labels...),
).toMap()
realLabels["country"] = c.collector.geoIpLookupProvider.Lookup(ip)

value := c.collector.get(c.name, realLabels)
c.collector.set(c.name, realLabels, value+by)
return nil
}

func (c *counterGeoImpl) WithLabels(labels ...MetricLabel) GeoCounter {
return &counterGeoImpl{
name: c.name,
collector: c.collector,
labels: append(c.labels, labels...),
}
}
21 changes: 21 additions & 0 deletions countergeo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,24 @@ func TestCounterGeo(t *testing.T) {
}
}
}

// TestCounter tests the functionality of counters
func TestGeoCounterLabel(t *testing.T) {
collector := metrics.New(&geoIpLookupProvider{})
counter, err := collector.CreateCounterGeo("test", "seconds", "Hello world!")
assert.Nil(t, err, "creating counter returned an error")
counter.Increment(net.ParseIP("127.0.0.2"))
newCounter := counter.WithLabels(metrics.Label("foo", "bar"))
newCounter.Increment(net.ParseIP("127.0.0.2"), metrics.Label("baz", "bar"))

metric := collector.GetMetric("test")
assert.Equal(t, 2, len(metric))

assert.Equal(t, "test", metric[0].Name)
assert.Equal(t, float64(1), metric[0].Value)
assert.Equal(t, 1, len(metric[0].Labels))

assert.Equal(t, "test", metric[1].Name)
assert.Equal(t, float64(1), metric[1].Value)
assert.Equal(t, 3, len(metric[1].Labels))
}
21 changes: 18 additions & 3 deletions gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package metrics
type gaugeImpl struct {
name string
collector *collector
labels []MetricLabel
}

func (g *gaugeImpl) Increment(labels ...MetricLabel) {
Expand All @@ -13,7 +14,9 @@ func (g *gaugeImpl) IncrementBy(by float64, labels ...MetricLabel) {
g.collector.mutex.Lock()
defer g.collector.mutex.Unlock()

realLabels := metricLabels(labels).toMap()
realLabels := metricLabels(
append(g.labels, labels...),
).toMap()
value := g.collector.get(g.name, realLabels)
g.collector.set(g.name, realLabels, value+by)
}
Expand All @@ -26,7 +29,9 @@ func (g *gaugeImpl) DecrementBy(by float64, labels ...MetricLabel) {
g.collector.mutex.Lock()
defer g.collector.mutex.Unlock()

realLabels := metricLabels(labels).toMap()
realLabels := metricLabels(
append(g.labels, labels...),
).toMap()
value := g.collector.get(g.name, realLabels)
g.collector.set(g.name, realLabels, value-by)
}
Expand All @@ -35,6 +40,16 @@ func (g *gaugeImpl) Set(value float64, labels ...MetricLabel) {
g.collector.mutex.Lock()
defer g.collector.mutex.Unlock()

realLabels := metricLabels(labels).toMap()
realLabels := metricLabels(
append(g.labels, labels...),
).toMap()
g.collector.set(g.name, realLabels, value)
}

func (g *gaugeImpl) WithLabels(labels ...MetricLabel) Gauge {
return &gaugeImpl{
name: g.name,
collector: g.collector,
labels: append(g.labels, labels...),
}
}
32 changes: 32 additions & 0 deletions gauge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package metrics_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/containerssh/metrics"
)

func TestGauge(t *testing.T) {
collector := metrics.New(&geoIpLookupProvider{})
gauge, err := collector.CreateGauge("test", "seconds", "Hello world!")
assert.Nil(t, err, "creating counter returned an error")

gauge.Set(42)

testMetrics := collector.GetMetric("test")
assert.Equal(t, 1, len(testMetrics))
assert.Equal(t, 0, len(testMetrics[0].Labels))
assert.Equal(t, float64(42), testMetrics[0].Value)

newGauge := gauge.WithLabels(metrics.Label("foo", "bar"))
newGauge.Set(43)

testMetrics = collector.GetMetric("test")
assert.Equal(t, 2, len(testMetrics))
assert.Equal(t, 0, len(testMetrics[0].Labels))
assert.Equal(t, 1, len(testMetrics[1].Labels))
assert.Equal(t, float64(42), testMetrics[0].Value)
assert.Equal(t, float64(43), testMetrics[1].Value)
}
Loading

0 comments on commit e923fd7

Please sign in to comment.