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

add Age Gauge and Monotonic Counter meter types #75

Merged
merged 1 commit into from
May 7, 2024
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
43 changes: 43 additions & 0 deletions spectator/meter/age_gauge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package meter

import (
"fmt"
"github.com/Netflix/spectator-go/spectator/writer"
)

// AgeGauge represents a value that is the time in seconds since the epoch at which an event
// has successfully occurred, or 0 to use the current time in epoch seconds. After an Age Gauge
// has been set, it will continue reporting the number of seconds since the last time recorded,
// for as long as the spectatord process runs. The purpose of this metric type is to enable users
// to more easily implement the Time Since Last Success alerting pattern.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no atlas-docs on this meter type, so I just inlined the spectatord description.

//
// To set `now()` as the last success, set a value of 0.
type AgeGauge struct {
id *Id
writer writer.Writer
meterTypeSymbol string
}

// NewAgeGauge generates a new gauge, using the provided meter identifier.
func NewAgeGauge(id *Id, writer writer.Writer) *AgeGauge {
return &AgeGauge{id, writer, "A"}
}

// MeterId returns the meter identifier.
func (g *AgeGauge) MeterId() *Id {
return g.id
}

// Set records the current value.
func (g *AgeGauge) Set(value int64) {
if value >= 0 {
var line = fmt.Sprintf("%s:%s:%d", g.meterTypeSymbol, g.id.spectatordId, value)
g.writer.Write(line)
}
}

// Now records the current time in epoch seconds in spectatord.
func (g *AgeGauge) Now() {
var line = fmt.Sprintf("%s:%s:0", g.meterTypeSymbol, g.id.spectatordId)
g.writer.Write(line)
}
69 changes: 69 additions & 0 deletions spectator/meter/age_gauge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package meter

import (
"github.com/Netflix/spectator-go/spectator/writer"
"testing"
)

func TestAgeGauge_Set(t *testing.T) {
id := NewId("set", nil)
w := writer.MemoryWriter{}
g := NewAgeGauge(id, &w)
g.Set(100)

expected := "A:set:100"
if w.Lines[0] != expected {
t.Errorf("Expected line to be %s, got %s", expected, w.Lines[0])
}
}

func TestAgeGauge_SetZero(t *testing.T) {
id := NewId("setZero", nil)
w := writer.MemoryWriter{}
g := NewAgeGauge(id, &w)
g.Set(0)

expected := "A:setZero:0"
if w.Lines[0] != expected {
t.Errorf("Expected line to be %s, got %s", expected, w.Lines[0])
}
}

func TestAgeGauge_Now(t *testing.T) {
id := NewId("now", nil)
w := writer.MemoryWriter{}
g := NewAgeGauge(id, &w)
g.Now()

expected := "A:now:0"
if w.Lines[0] != expected {
t.Errorf("Expected line to be %s, got %s", expected, w.Lines[0])
}
}

func TestAgeGauge_SetNegative(t *testing.T) {
id := NewId("setNegative", nil)
w := writer.MemoryWriter{}
g := NewAgeGauge(id, &w)
g.Set(-100)

if len(w.Lines) != 0 {
t.Error("Negative values should be ignored")
}
}

func TestAgeGauge_SetMultipleValues(t *testing.T) {
id := NewId("setMultiple", nil)
w := writer.MemoryWriter{}
g := NewAgeGauge(id, &w)
g.Set(100)
g.Set(200)
g.Set(300)

expectedLines := []string{"A:setMultiple:100", "A:setMultiple:200", "A:setMultiple:300"}
for i, line := range w.Lines {
if line != expectedLines[i] {
t.Errorf("Expected line to be %s, got %s", expectedLines[i], line)
}
}
}
10 changes: 5 additions & 5 deletions spectator/meter/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import (
type Counter struct {
id *Id
writer writer.Writer
meterTypeSymbol rune
meterTypeSymbol string
}

// NewCounter generates a new counter, using the provided meter identifier.
func NewCounter(id *Id, writer writer.Writer) *Counter {
return &Counter{id, writer, 'c'}
return &Counter{id, writer, "c"}
}

// MeterId returns the meter identifier.
Expand All @@ -30,22 +30,22 @@ func (c *Counter) MeterId() *Id {

// Increment increments the counter.
func (c *Counter) Increment() {
var line = fmt.Sprintf("%c:%s:%d", c.meterTypeSymbol, c.id.spectatordId, 1)
var line = fmt.Sprintf("%s:%s:%d", c.meterTypeSymbol, c.id.spectatordId, 1)
c.writer.Write(line)
}

// AddFloat adds a specific float64 delta to the current measurement.
func (c *Counter) AddFloat(delta float64) {
if delta > 0.0 {
var line = fmt.Sprintf("%c:%s:%f", c.meterTypeSymbol, c.id.spectatordId, delta)
var line = fmt.Sprintf("%s:%s:%f", c.meterTypeSymbol, c.id.spectatordId, delta)
c.writer.Write(line)
}
}

// Add is to add a specific int64 delta to the current measurement.
func (c *Counter) Add(delta int64) {
if delta > 0 {
var line = fmt.Sprintf("%c:%s:%d", c.meterTypeSymbol, c.id.spectatordId, delta)
var line = fmt.Sprintf("%s:%s:%d", c.meterTypeSymbol, c.id.spectatordId, delta)
c.writer.Write(line)
}
}
6 changes: 3 additions & 3 deletions spectator/meter/dist_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import (
type DistributionSummary struct {
id *Id
writer writer.Writer
meterTypeSymbol rune
meterTypeSymbol string
}

// NewDistributionSummary generates a new distribution summary, using the
// provided meter identifier.
func NewDistributionSummary(id *Id, writer writer.Writer) *DistributionSummary {
return &DistributionSummary{id, writer, 'd'}
return &DistributionSummary{id, writer, "d"}
}

// MeterId returns the meter identifier.
Expand All @@ -32,7 +32,7 @@ func (d *DistributionSummary) MeterId() *Id {
// Record records a new value to track within the distribution.
func (d *DistributionSummary) Record(amount int64) {
if amount >= 0 {
var line = fmt.Sprintf("%c:%s:%d", d.meterTypeSymbol, d.id.spectatordId, amount)
var line = fmt.Sprintf("%s:%s:%d", d.meterTypeSymbol, d.id.spectatordId, amount)
d.writer.Write(line)
}
}
6 changes: 3 additions & 3 deletions spectator/meter/max_gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import (
type MaxGauge struct {
id *Id
writer writer.Writer
meterTypeSymbol rune
meterTypeSymbol string
}

// NewMaxGauge generates a new gauge, using the provided meter identifier.
func NewMaxGauge(id *Id, writer writer.Writer) *MaxGauge {
return &MaxGauge{id, writer, 'm'}
return &MaxGauge{id, writer, "m"}
}

// MeterId returns the meter identifier.
Expand All @@ -31,6 +31,6 @@ func (g *MaxGauge) MeterId() *Id {

// Set records the current value.
func (g *MaxGauge) Set(value float64) {
var line = fmt.Sprintf("%c:%s:%f", g.meterTypeSymbol, g.id.spectatordId, value)
var line = fmt.Sprintf("%s:%s:%f", g.meterTypeSymbol, g.id.spectatordId, value)
g.writer.Write(line)
}
38 changes: 38 additions & 0 deletions spectator/meter/monotonic_counter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package meter

import (
"fmt"
"github.com/Netflix/spectator-go/spectator/writer"
)

// MonotonicCounter is used to measure the rate at which some event is occurring. This
// type is safe for concurrent use.
//
// The value is a monotonically increasing number. A minimum of two samples must be received
// in order for spectatord to calculate a delta value and report it to the backend.
//
// A variety of networking metrics may be reported monotonically and this metric type provides a
// convenient means of recording these values, at the expense of a slower time-to-first metric.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no atlas-docs on this meter type, so I just inlined the spectatord description.

type MonotonicCounter struct {
id *Id
writer writer.Writer
meterTypeSymbol string
}

// NewMonotonicCounter generates a new counter, using the provided meter identifier.
func NewMonotonicCounter(id *Id, writer writer.Writer) *MonotonicCounter {
return &MonotonicCounter{id, writer, "C"}
}

// MeterId returns the meter identifier.
func (c *MonotonicCounter) MeterId() *Id {
return c.id
}

// Set is to set a specific int64 delta as the current measurement.
func (c *MonotonicCounter) Set(delta int64) {
if delta > 0 {
var line = fmt.Sprintf("%s:%s:%d", c.meterTypeSymbol, c.id.spectatordId, delta)
c.writer.Write(line)
}
}
24 changes: 24 additions & 0 deletions spectator/meter/monotonic_counter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package meter

import (
"github.com/Netflix/spectator-go/spectator/writer"
"testing"
)

func TestMonotonicCounter_Set(t *testing.T) {
w := writer.MemoryWriter{}
id := NewId("add", nil)
c := NewMonotonicCounter(id, &w)

c.Set(4)

expected := "C:add:4"
if w.Lines[0] != expected {
t.Error("Expected ", expected, " got ", w.Lines[0])
}

c.Set(-1)
if len(w.Lines) != 1 {
t.Error("Negative deltas should be ignored")
}
}
6 changes: 3 additions & 3 deletions spectator/meter/percentile_distsummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
type PercentileDistributionSummary struct {
id *Id
writer writer.Writer
meterTypeSymbol rune
meterTypeSymbol string
}

func (p *PercentileDistributionSummary) MeterId() *Id {
Expand All @@ -19,13 +19,13 @@ func (p *PercentileDistributionSummary) MeterId() *Id {

// NewPercentileDistributionSummary creates a new *PercentileDistributionSummary using the meter identifier.
func NewPercentileDistributionSummary(id *Id, writer writer.Writer) *PercentileDistributionSummary {
return &PercentileDistributionSummary{id, writer, 'D'}
return &PercentileDistributionSummary{id, writer, "D"}
}

// Record records a new value to track within the distribution.
func (p *PercentileDistributionSummary) Record(amount int64) {
if amount >= 0 {
var line = fmt.Sprintf("%c:%s:%d", p.meterTypeSymbol, p.id.spectatordId, amount)
var line = fmt.Sprintf("%s:%s:%d", p.meterTypeSymbol, p.id.spectatordId, amount)
p.writer.Write(line)
}
}
6 changes: 3 additions & 3 deletions spectator/meter/percentile_timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import (
type PercentileTimer struct {
id *Id
writer writer.Writer
meterTypeSymbol rune
meterTypeSymbol string
}

func NewPercentileTimer(
id *Id,
writer writer.Writer,
) *PercentileTimer {
return &PercentileTimer{id, writer, 'T'}
return &PercentileTimer{id, writer, "T"}
}

func (t *PercentileTimer) MeterId() *Id {
Expand All @@ -28,7 +28,7 @@ func (t *PercentileTimer) MeterId() *Id {
// Record records the value for a single event.
func (t *PercentileTimer) Record(amount time.Duration) {
if amount >= 0 {
var line = fmt.Sprintf("%c:%s:%f", t.meterTypeSymbol, t.id.spectatordId, amount.Seconds())
var line = fmt.Sprintf("%s:%s:%f", t.meterTypeSymbol, t.id.spectatordId, amount.Seconds())
t.writer.Write(line)
}
}
6 changes: 3 additions & 3 deletions spectator/meter/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
type Timer struct {
id *Id
writer writer.Writer
meterTypeSymbol rune
meterTypeSymbol string
}

// NewTimer generates a new timer, using the provided meter identifier.
func NewTimer(id *Id, writer writer.Writer) *Timer {
return &Timer{id, writer, 't'}
return &Timer{id, writer, "t"}
}

// MeterId returns the meter identifier.
Expand All @@ -27,7 +27,7 @@ func (t *Timer) MeterId() *Id {
// Record records the duration this specific event took.
func (t *Timer) Record(amount time.Duration) {
if amount >= 0 {
var line = fmt.Sprintf("%c:%s:%f", t.meterTypeSymbol, t.id.spectatordId, amount.Seconds())
var line = fmt.Sprintf("%s:%s:%f", t.meterTypeSymbol, t.id.spectatordId, amount.Seconds())
t.writer.Write(line)
}
}
Loading