普罗米修斯收集器失败 "collected metric was collected before with the same name and label values"

Prometheus Collector fails with "collected metric was collected before with the same name and label values"

我有一个设备将温度测量显示为以下格式的 JSON:

[
  {
    "dataPointId": 123456,
    "values": [
      {
        "t": 1589236277000,
        "v": 14.999993896484398
      },
      {
        "t": 1589236877000,
        "v": 14.700006103515648
      },
      {
        "t": 1589237477000,
        "v": 14.999993896484398
      },
[..]

如您所见,这些值包含时间戳和温度测量值。我想通过 Prometheus 指标公开这些测量结果,所以我正在使用 prometheus/client_golang 构建导出器。

我的期望是 /metrics 端点然后从上面的数据中公开类似这样的内容:

# HELP my_temperature_celsius Temperature
# TYPE my_temperature_celsius gauge
my_temperature_celsius{id="123456"} 14.999993896484398 1589236277000
my_temperature_celsius{id="123456"} 14.700006103515648 1589236877000
my_temperature_celsius{id="123456"} 14.999993896484398 1589237477000

我实现了一个简单的 prometheus.Collector 并且我正在毫无问题地添加我的静态指标。对于上面的测量,NewMetricWithTimestamp 似乎是添加带有时间戳的指标的唯一方法,因此我使用类似这样的方法迭代这些值:

for _, measurements := range dp.Values {
  ch <- prometheus.NewMetricWithTimestamp(
    time.Unix(measurements.T, 0),
    prometheus.MustNewConstMetric(
      collector.temperature,
      prometheus.GaugeValue,
      float64(measurements.V),
      device.DatapointID))
}

然而,这导致了以下我不完全理解的错误:

An error has occurred while serving metrics:

1135 error(s) occurred:
* collected metric "my_temperature_celsius" { label:<name:"id" value:"123456" > gauge:<value:14.999993896484398 > timestamp_ms:1589236877000000 } was collected before with the same name and label values
* collected metric "my_temperature_celsius" { label:<name:"id" value:"123456" > gauge:<value:14.700006103515648 > timestamp_ms:1589237477000000 } was collected before with the same name and label values
[..]

如果仔细观察,您会发现 JSON 数据格式在指标收集的上下文中略微多余,因为时间戳在每个设备内部,而不是作为父键并将值作为设备 ID 和值的数组。只有这样你才会循环实时序列数据,然后你的标签就不会像现在这样在循环中是静态的。标签唯一性是标签名称 + 标签值散列在一起。

我认为最好的方法是制作一个 Gauge Vector。使用 WithLabelValues 得到一个 Gauge 对象并在其上调用 Set 来设置值

deviceTempGaugeVector := prometheus.NewGaugeVec(
    prometheus.GaugeOpts{
        Name: "my_temperature_celsius",
    },
    []string{
        "device_id" // Using single label instead of 2 labels "id" and "value"
    },
)

prometheus.MustRegister(deviceTempGaugeVector)

for _, point := range dp.TimeStamps {
  for _, measurements := range point {
    deviceId := measurements.DatapointID
    value := measurements.V
    metric := deviceTempGaugeVector.WithLabelValues(deviceId).Set(value)
    ch <- prometheus.NewMetricWithTimestamp(time.Unix(measurements.T, 0),metric)
  }
}

参考:https://godoc.org/github.com/prometheus/client_golang/prometheus#NewGaugeVec

参考自 Prometheus

A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.

A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets. 

Gauge 用于我们关心的一个值,不关心时间戳。喜欢当前温度,而不是前一天的温度。

Gauge 不是您要查找的指标类型。或者,prometheus 可能不是您要找的。

当我们想要监控温度时,我们使用histogram。您可以在短时间内计算平均温度、最低温度或最高温度。但是,当你想使用自己的时间戳时,你需要自己实现一个直方图收集器。您可以从 prometheus/client_golang/histogram.go 查看文件。一点都不简单。

你真正需要的是一个time series database,比如influxdb。您可以将数据推送到接受自定义时间戳的 influxdb,就像 post json 到 http 一样简单,然后使用 grafana 监控数据。

希望对你有所帮助。