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
statsd: support sending metrics with timestamp. #262
Changes from 7 commits
97e39ef
3529331
87572b0
68308a5
6e8a1ef
0325b2a
4d27807
da9d9c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -126,6 +126,9 @@ const ( | |
writerWindowsPipe string = "pipe" | ||
) | ||
|
||
// noTimestamp is used as a value for metric without a given timestamp. | ||
const noTimestamp = int64(0) | ||
|
||
type metric struct { | ||
metricType metricType | ||
namespace string | ||
|
@@ -140,6 +143,7 @@ type metric struct { | |
tags []string | ||
stags string | ||
rate float64 | ||
timestamp int64 | ||
} | ||
|
||
type noClientErr string | ||
|
@@ -152,16 +156,40 @@ func (e noClientErr) Error() string { | |
return string(e) | ||
} | ||
|
||
type invalidTimestampErr string | ||
|
||
// InvalidTimestamp is returned if a provided timestamp is invalid. | ||
const InvalidTimestamp = invalidTimestampErr("invalid timestamp") | ||
|
||
func (e invalidTimestampErr) Error() string { | ||
return string(e) | ||
} | ||
|
||
// ClientInterface is an interface that exposes the common client functions for the | ||
// purpose of being able to provide a no-op client or even mocking. This can aid | ||
// downstream users' with their testing. | ||
type ClientInterface interface { | ||
// Gauge measures the value of a metric at a particular time. | ||
Gauge(name string, value float64, tags []string, rate float64) error | ||
|
||
// GaugeWithTimestamp measures the value of a metric at a given time. | ||
// The value will bypass any aggregation on the client side and agent side, this is | ||
// useful when sending points in the past. | ||
// Please report to the Datadog documentation for the maximum age of a metric. | ||
// | ||
// Minimum Datadog Agent version: 7.40.0 | ||
GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error | ||
|
||
// Count tracks how many times something happened per second. | ||
Count(name string, value int64, tags []string, rate float64) error | ||
|
||
// The value will bypass any aggregation on the client side and agent side, this is | ||
// useful when sending points in the past. | ||
// Please report to the Datadog documentation for the maximum age of a metric. | ||
// | ||
// Minimum Datadog Agent version: 7.40.0 | ||
CountWithTimestamp(name string, value int64, tags []string, rate float64, timestamp time.Time) error | ||
|
||
// Histogram tracks the statistical distribution of a set of values on each host. | ||
Histogram(name string, value float64, tags []string, rate float64) error | ||
|
||
|
@@ -552,6 +580,25 @@ func (c *Client) Gauge(name string, value float64, tags []string, rate float64) | |
return c.send(metric{metricType: gauge, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace}) | ||
} | ||
|
||
// GaugeWithTimestamp measures the value of a metric at a given time. | ||
// The value will bypass any aggregation on the client side and agent side, this is | ||
// useful when sending points in the past. | ||
// Please report to the Datadog documentation for the maximum age of a metric. | ||
// | ||
// Minimum Datadog Agent version: 7.40.0 | ||
func (c *Client) GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error { | ||
hush-hush marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return an error if timestamp is 0 (same for count). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure, that's not an invalid timestamp. A weird one, but not an invalid one. For instance, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's an error since go has default value. This means an non-initialized timestamp. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A gotcha... Also linked to your other question about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm trying to understand why
Dogstatsd parsing correctly handles negative timestamps. src I think the limiting factor here is the intake according to this comment:
So my suggestion here is to add a comment about why this is the minimum supported epoch. BTW - I noticed that the public metrics api says:
I assume this doesn't apply to us? Or should we be enforcing that condition here by rejecting timestamps older than 1hr? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah.. We have to draw a limit somewhere in the clients and since the intake has a "minimum timestamp" limit and we already have other places limiting to the unix epoch in the Agent, I think having
It doesn't indeed, a future PR will update the public documentation, with things more related about the "features" than the technical chunks in the client. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The idea is to provide a hard-coded limit instead of no limit, even if it has no actual feature meaning here.
|
||
if c == nil { | ||
return ErrNoClient | ||
} | ||
|
||
if timestamp.IsZero() || timestamp.Unix() <= noTimestamp { | ||
return InvalidTimestamp | ||
} | ||
|
||
atomic.AddUint64(&c.telemetry.totalMetricsGauge, 1) | ||
hush-hush marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return c.send(metric{metricType: gauge, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace, timestamp: timestamp.Unix()}) | ||
} | ||
|
||
// Count tracks how many times something happened per second. | ||
func (c *Client) Count(name string, value int64, tags []string, rate float64) error { | ||
if c == nil { | ||
|
@@ -564,6 +611,25 @@ func (c *Client) Count(name string, value int64, tags []string, rate float64) er | |
return c.send(metric{metricType: count, name: name, ivalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace}) | ||
} | ||
|
||
// CountWithTimestamp tracks how many times something happened at the given second. | ||
// The value will bypass any aggregation on the client side and agent side, this is | ||
// useful when sending points in the past. | ||
// Please report to the Datadog documentation for the maximum age of a metric. | ||
// | ||
// Minimum Datadog Agent version: 7.40.0 | ||
func (c *Client) CountWithTimestamp(name string, value int64, tags []string, rate float64, timestamp time.Time) error { | ||
if c == nil { | ||
return ErrNoClient | ||
} | ||
|
||
if timestamp.IsZero() || timestamp.Unix() <= noTimestamp { | ||
return InvalidTimestamp | ||
} | ||
|
||
atomic.AddUint64(&c.telemetry.totalMetricsCount, 1) | ||
return c.send(metric{metricType: count, name: name, ivalue: value, tags: tags, rate: rate, globalTags: c.tags, namespace: c.namespace, timestamp: timestamp.Unix()}) | ||
} | ||
|
||
// Histogram tracks the statistical distribution of a set of values on each host. | ||
func (c *Client) Histogram(name string, value float64, tags []string, rate float64) error { | ||
if c == nil { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small edge case here:
In
GaugeWithTimestamp
, we are rejecting timestamps older than epoch0
, which implies that an epoch ==0
is valid.But here we say that
0
represents a metric without a timestamp, which makes it impossible to submit with a timestamp of0
.GaugeWithTimestamp
will happily pass it along, but thenappendTimestamp
will be a no-op as it matches thenoTimestamp
value.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to:
timestamp
must be > 0 to be appended to a message or to be considered valid byGaugeWithTimestamp
andCountWithTimestamp
.