Skip to content

Commit

Permalink
Pick timestamp from metric
Browse files Browse the repository at this point in the history
based on prometheus-community#97 and prometheus-community#80 this provides the posibility to use a metric that has a unix style timestamp as the timestamp of the scraped metric
  • Loading branch information
janphkre committed Jul 11, 2022
1 parent 2c1ca88 commit 2c6f894
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 63 deletions.
95 changes: 48 additions & 47 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,82 +14,83 @@
package config

import (
"io/ioutil"
"io/ioutil"

pconfig "github.com/prometheus/common/config"
"gopkg.in/yaml.v2"
pconfig "github.com/prometheus/common/config"
"gopkg.in/yaml.v2"
)

// 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
EpochTimestamp string
Type ScrapeType
ValueType ValueType
Help string
Values map[string]string
}

type ScrapeType string

const (
ValueScrape ScrapeType = "value" // default
ObjectScrape ScrapeType = "object"
ValueScrape ScrapeType = "value" // default
ObjectScrape ScrapeType = "object"
)

type ValueType string

const (
ValueTypeGauge ValueType = "gauge"
ValueTypeCounter ValueType = "counter"
ValueTypeUntyped ValueType = "untyped"
ValueTypeGauge ValueType = "gauge"
ValueTypeCounter ValueType = "counter"
ValueTypeUntyped ValueType = "untyped"
)

// Config contains multiple modules.
type Config struct {
Modules map[string]Module `yaml:"modules"`
Modules map[string]Module `yaml:"modules"`
}

// Module contains metrics and headers defining a configuration
type Module struct {
Headers map[string]string `yaml:"headers,omitempty"`
Metrics []Metric `yaml:"metrics"`
HTTPClientConfig pconfig.HTTPClientConfig `yaml:"http_client_config,omitempty"`
Body Body `yaml:"body,omitempty"`
ValidStatusCodes []int `yaml:"valid_status_codes,omitempty"`
Headers map[string]string `yaml:"headers,omitempty"`
Metrics []Metric `yaml:"metrics"`
HTTPClientConfig pconfig.HTTPClientConfig `yaml:"http_client_config,omitempty"`
Body Body `yaml:"body,omitempty"`
ValidStatusCodes []int `yaml:"valid_status_codes,omitempty"`
}

type Body struct {
Content string `yaml:"content"`
Templatize bool `yaml:"templatize,omitempty"`
Content string `yaml:"content"`
Templatize bool `yaml:"templatize,omitempty"`
}

func LoadConfig(configPath string) (Config, error) {
var config Config
data, err := ioutil.ReadFile(configPath)
if err != nil {
return config, err
}
var config Config
data, err := ioutil.ReadFile(configPath)
if err != nil {
return config, err
}

if err := yaml.Unmarshal(data, &config); err != nil {
return config, err
}
if err := yaml.Unmarshal(data, &config); err != nil {
return config, err
}

// Complete Defaults
for _, module := range config.Modules {
for i := 0; i < len(module.Metrics); i++ {
if module.Metrics[i].Type == "" {
module.Metrics[i].Type = ValueScrape
}
if module.Metrics[i].Help == "" {
module.Metrics[i].Help = module.Metrics[i].Name
}
if module.Metrics[i].ValueType == "" {
module.Metrics[i].ValueType = ValueTypeUntyped
}
}
}
// Complete Defaults
for _, module := range config.Modules {
for i := 0; i < len(module.Metrics); i++ {
if module.Metrics[i].Type == "" {
module.Metrics[i].Type = ValueScrape
}
if module.Metrics[i].Help == "" {
module.Metrics[i].Help = module.Metrics[i].Name
}
if module.Metrics[i].ValueType == "" {
module.Metrics[i].ValueType = ValueTypeUntyped
}
}
}

return config, nil
}
return config, nil
}
39 changes: 30 additions & 9 deletions exporter/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package exporter

import (
"time"
"bytes"
"encoding/json"

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, m, 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, m, 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(mc JSONMetricCollector, m JSONMetric, pm prometheus.Metric) prometheus.Metric {
if m.EpochTimestampJSONPath == "" {
return pm
}
ts, err := extractValue(mc.Logger, mc.Data, m.EpochTimestampJSONPath, false)
if err != nil {
level.Error(mc.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(mc.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

0 comments on commit 2c6f894

Please sign in to comment.