lib/protoparser/prometheus: properly parse OpenMetrics timestamps

OpenMetrics timestamps are floating-point numbers, that represent Unix timestamp in seconds.
This differs from Prometheus exposition format, where timestamps are integer numbers representing Unix timestamp in milliseconds.
This commit is contained in:
Aliaksandr Valialkin 2020-11-27 14:53:27 +02:00
parent eedb79ead8
commit a906b3862f
4 changed files with 37 additions and 17 deletions

View file

@ -2,6 +2,9 @@
# tip
* BUGFIX: properly parse timestamps in OpenMetrics format - they are exposed as floating-point number in seconds instead of integer milliseconds
unlike in Prometheus exposition format. See [the docs](https://github.com/OpenObservability/OpenMetrics/blob/master/specification/OpenMetrics.md#timestamps).
# [v1.48.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.48.0)

View file

@ -166,11 +166,18 @@ func (r *Row) unmarshal(s string, tagsPool []Tag, noEscapes bool) ([]Tag, error)
// There is no timestamp - just a whitespace after the value.
return tagsPool, nil
}
ts, err := fastfloat.ParseInt64(s)
ts, err := fastfloat.Parse(s)
if err != nil {
return tagsPool, fmt.Errorf("cannot parse timestamp %q: %w", s, err)
}
r.Timestamp = ts
if ts >= -1<<31 && ts < 1<<31 {
// This looks like OpenMetrics timestamp in Unix seconds.
// Convert it to milliseconds.
//
// See https://github.com/OpenObservability/OpenMetrics/blob/master/specification/OpenMetrics.md#timestamps
ts *= 1000
}
r.Timestamp = int64(ts)
return tagsPool, nil
}

View file

@ -178,14 +178,14 @@ func TestRowsUnmarshalSuccess(t *testing.T) {
Rows: []Row{{
Metric: "foobar",
Value: 123.456,
Timestamp: 789,
Timestamp: 789000,
}},
})
f("foobar{} 123.456 789\n", &Rows{
f("foobar{} 123.456 789.4354\n", &Rows{
Rows: []Row{{
Metric: "foobar",
Value: 123.456,
Timestamp: 789,
Timestamp: 789435,
}},
})
f(`# _ _
@ -225,7 +225,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
{
Metric: "abc",
Value: 123,
Timestamp: 456,
Timestamp: 456000,
},
{
Metric: "foo",
@ -274,7 +274,8 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
},
})
// Timestamp bigger than 1<<31
// Timestamp bigger than 1<<31.
// It should be parsed in milliseconds.
f("aaa 1123 429496729600", &Rows{
Rows: []Row{{
Metric: "aaa",
@ -283,6 +284,15 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
}},
})
// Floating-point timestamps in OpenMetric format.
f("aaa 1123 42949.567", &Rows{
Rows: []Row{{
Metric: "aaa",
Value: 1123,
Timestamp: 42949567,
}},
})
// Tags
f(`foo{bar="baz"} 1 2`, &Rows{
Rows: []Row{{
@ -292,7 +302,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
Value: "baz",
}},
Value: 1,
Timestamp: 2,
Timestamp: 2000,
}},
})
f(`foo{bar="b\"a\\z"} -1.2`, &Rows{
@ -324,7 +334,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
},
},
Value: 1,
Timestamp: 2,
Timestamp: 2000,
}},
})
@ -338,7 +348,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
Value: "baz",
}},
Value: 1,
Timestamp: 2,
Timestamp: 2000,
}},
})
@ -348,7 +358,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
{
Metric: "foo",
Value: 0.3,
Timestamp: 2,
Timestamp: 2000,
},
{
Metric: "aaa",
@ -357,7 +367,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
{
Metric: "bar.baz",
Value: 0.34,
Timestamp: 43,
Timestamp: 43000,
},
},
})
@ -368,12 +378,12 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
{
Metric: "foo",
Value: 0.3,
Timestamp: 2,
Timestamp: 2000,
},
{
Metric: "bar.baz",
Value: 0.34,
Timestamp: 43,
Timestamp: 43000,
},
},
})

View file

@ -82,13 +82,13 @@ func TestParseStream(t *testing.T) {
f("foo 123 456", []Row{{
Metric: "foo",
Value: 123,
Timestamp: 456,
Timestamp: 456000,
}})
f(`foo{bar="baz"} 1 2`+"\n"+`aaa{} 3 4`, []Row{
{
Metric: "aaa",
Value: 3,
Timestamp: 4,
Timestamp: 4000,
},
{
Metric: "foo",
@ -97,7 +97,7 @@ func TestParseStream(t *testing.T) {
Value: "baz",
}},
Value: 1,
Timestamp: 2,
Timestamp: 2000,
},
})
f("foo 23", []Row{{