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

Grab Timestamps from Metric #167

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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ $ docker run --rm -it -p 9090:9090 -v $PWD/examples/prometheus.yml:/etc/promethe
```
Then head over to http://localhost:9090/graph?g0.range_input=1h&g0.expr=example_value_active&g0.tab=1 or http://localhost:9090/targets to check the scraped metrics or the targets.

## Using custom timestamps

This exporter allows you to use a field of the metric as the (unix/epoch) timestamp for the data as an int64. However, this may lead to unexpected behaviour, as the prometheus implements a [Staleness](https://prometheus.io/docs/prometheus/latest/querying/basics/#staleness) mechanism. Including timestamps in metrics disabled this staleness handling and can make data visible for longer than expected.

## Exposing metrics through HTTPS

TLS configuration supported by this exporter can be found at [exporter-toolkit/web](https://github.com/prometheus/exporter-toolkit/blob/v0.5.1/docs/web-configuration.md)
Expand Down
15 changes: 8 additions & 7 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import (

// Metric contains values that define a metric
type Metric struct {
Name string
Path string
Labels map[string]string
Type ScrapeType
ValueType ValueType
Help string
Values map[string]string
Name string
Path string
Labels map[string]string
Type ScrapeType
ValueType ValueType
EpochTimestamp string
Help string
Values map[string]string
}

type ScrapeType string
Expand Down
7 changes: 6 additions & 1 deletion examples/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ modules:
labels:
environment: beta # static label
location: "planet-{.location}" # dynamic label

- name: example_timestamped_value
path: "{ .values[?(@.state == "INACTIVE")] }"
epochTimestamp: "{ .timestamp }"
help: Example of a timestamped value scrape in the json
labels:
environment: beta # static label
- name: example_value
type: object
help: Example of sub-level value scrapes from a json
Expand Down
1 change: 1 addition & 0 deletions examples/data.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"counter": 1234,
"timestamp": 1657568506,
"values": [
{
"id": "id-A",
Expand Down
39 changes: 30 additions & 9 deletions exporter/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package exporter
import (
"bytes"
"encoding/json"
"time"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
Expand All @@ -31,12 +32,13 @@ type JSONMetricCollector struct {
}

type JSONMetric struct {
Desc *prometheus.Desc
Type config.ScrapeType
KeyJSONPath string
ValueJSONPath string
LabelsJSONPaths []string
ValueType prometheus.ValueType
Desc *prometheus.Desc
Type config.ScrapeType
KeyJSONPath string
ValueJSONPath string
LabelsJSONPaths []string
ValueType prometheus.ValueType
EpochTimestampJSONPath string
}

func (mc JSONMetricCollector) Describe(ch chan<- *prometheus.Desc) {
Expand All @@ -56,13 +58,13 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
}

if floatValue, err := SanitizeValue(value); err == nil {

ch <- prometheus.MustNewConstMetric(
metric := prometheus.MustNewConstMetric(
m.Desc,
m.ValueType,
floatValue,
extractLabels(mc.Logger, mc.Data, m.LabelsJSONPaths)...,
)
ch <- timestampMetric(mc.Logger, m, mc.Data, metric)
} else {
level.Error(mc.Logger).Log("msg", "Failed to convert extracted value to float64", "path", m.KeyJSONPath, "value", value, "err", err, "metric", m.Desc)
continue
Expand Down Expand Up @@ -90,12 +92,13 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
}

if floatValue, err := SanitizeValue(value); err == nil {
ch <- prometheus.MustNewConstMetric(
metric := prometheus.MustNewConstMetric(
m.Desc,
m.ValueType,
floatValue,
extractLabels(mc.Logger, jdata, m.LabelsJSONPaths)...,
)
ch <- timestampMetric(mc.Logger, m, jdata, metric)
} else {
level.Error(mc.Logger).Log("msg", "Failed to convert extracted value to float64", "path", m.ValueJSONPath, "value", value, "err", err, "metric", m.Desc)
continue
Expand Down Expand Up @@ -157,3 +160,21 @@ func extractLabels(logger log.Logger, data []byte, paths []string) []string {
}
return labels
}

func timestampMetric(logger log.Logger, m JSONMetric, data []byte, pm prometheus.Metric) prometheus.Metric {
if m.EpochTimestampJSONPath == "" {
return pm
}
ts, err := extractValue(logger, data, m.EpochTimestampJSONPath, false)
if err != nil {
level.Error(logger).Log("msg", "Failed to extract timestamp for metric", "path", m.KeyJSONPath, "err", err, "metric", m.Desc)
return pm
}
epochTime, err := SanitizeIntValue(ts)
if err != nil {
level.Error(logger).Log("msg", "Failed to parse timestamp for metric", "path", m.KeyJSONPath, "err", err, "metric", m.Desc)
return pm
}
timestamp := time.UnixMilli(epochTime)
return prometheus.NewMetricWithTimestamp(timestamp, pm)
}
29 changes: 22 additions & 7 deletions exporter/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ func SanitizeValue(s string) (float64, error) {
return value, fmt.Errorf(resultErr)
}

func SanitizeIntValue(s string) (int64, error) {
var err error
var value int64
var resultErr string

if value, err = strconv.ParseInt(s, 10, 64); err == nil {
return value, nil
}
resultErr = fmt.Sprintf("%s", err)

return value, fmt.Errorf(resultErr)
}

func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
var (
metrics []JSONMetric
Expand Down Expand Up @@ -91,9 +104,10 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
variableLabels,
nil,
),
KeyJSONPath: metric.Path,
LabelsJSONPaths: variableLabelsValues,
ValueType: valueType,
KeyJSONPath: metric.Path,
LabelsJSONPaths: variableLabelsValues,
ValueType: valueType,
EpochTimestampJSONPath: metric.EpochTimestamp,
}
metrics = append(metrics, jsonMetric)
case config.ObjectScrape:
Expand All @@ -112,10 +126,11 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
variableLabels,
nil,
),
KeyJSONPath: metric.Path,
ValueJSONPath: valuePath,
LabelsJSONPaths: variableLabelsValues,
ValueType: valueType,
KeyJSONPath: metric.Path,
ValueJSONPath: valuePath,
LabelsJSONPaths: variableLabelsValues,
ValueType: valueType,
EpochTimestampJSONPath: metric.EpochTimestamp,
}
metrics = append(metrics, jsonMetric)
}
Expand Down