mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
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:
parent
eedb79ead8
commit
a906b3862f
4 changed files with 37 additions and 17 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
# tip
|
# 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)
|
# [v1.48.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.48.0)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
// There is no timestamp - just a whitespace after the value.
|
||||||
return tagsPool, nil
|
return tagsPool, nil
|
||||||
}
|
}
|
||||||
ts, err := fastfloat.ParseInt64(s)
|
ts, err := fastfloat.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tagsPool, fmt.Errorf("cannot parse timestamp %q: %w", s, err)
|
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
|
return tagsPool, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,14 +178,14 @@ func TestRowsUnmarshalSuccess(t *testing.T) {
|
||||||
Rows: []Row{{
|
Rows: []Row{{
|
||||||
Metric: "foobar",
|
Metric: "foobar",
|
||||||
Value: 123.456,
|
Value: 123.456,
|
||||||
Timestamp: 789,
|
Timestamp: 789000,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
f("foobar{} 123.456 789\n", &Rows{
|
f("foobar{} 123.456 789.4354\n", &Rows{
|
||||||
Rows: []Row{{
|
Rows: []Row{{
|
||||||
Metric: "foobar",
|
Metric: "foobar",
|
||||||
Value: 123.456,
|
Value: 123.456,
|
||||||
Timestamp: 789,
|
Timestamp: 789435,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
f(`# _ _
|
f(`# _ _
|
||||||
|
@ -225,7 +225,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
|
||||||
{
|
{
|
||||||
Metric: "abc",
|
Metric: "abc",
|
||||||
Value: 123,
|
Value: 123,
|
||||||
Timestamp: 456,
|
Timestamp: 456000,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Metric: "foo",
|
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{
|
f("aaa 1123 429496729600", &Rows{
|
||||||
Rows: []Row{{
|
Rows: []Row{{
|
||||||
Metric: "aaa",
|
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
|
// Tags
|
||||||
f(`foo{bar="baz"} 1 2`, &Rows{
|
f(`foo{bar="baz"} 1 2`, &Rows{
|
||||||
Rows: []Row{{
|
Rows: []Row{{
|
||||||
|
@ -292,7 +302,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
|
||||||
Value: "baz",
|
Value: "baz",
|
||||||
}},
|
}},
|
||||||
Value: 1,
|
Value: 1,
|
||||||
Timestamp: 2,
|
Timestamp: 2000,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
f(`foo{bar="b\"a\\z"} -1.2`, &Rows{
|
f(`foo{bar="b\"a\\z"} -1.2`, &Rows{
|
||||||
|
@ -324,7 +334,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Value: 1,
|
Value: 1,
|
||||||
Timestamp: 2,
|
Timestamp: 2000,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -338,7 +348,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
|
||||||
Value: "baz",
|
Value: "baz",
|
||||||
}},
|
}},
|
||||||
Value: 1,
|
Value: 1,
|
||||||
Timestamp: 2,
|
Timestamp: 2000,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -348,7 +358,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
|
||||||
{
|
{
|
||||||
Metric: "foo",
|
Metric: "foo",
|
||||||
Value: 0.3,
|
Value: 0.3,
|
||||||
Timestamp: 2,
|
Timestamp: 2000,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Metric: "aaa",
|
Metric: "aaa",
|
||||||
|
@ -357,7 +367,7 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
|
||||||
{
|
{
|
||||||
Metric: "bar.baz",
|
Metric: "bar.baz",
|
||||||
Value: 0.34,
|
Value: 0.34,
|
||||||
Timestamp: 43,
|
Timestamp: 43000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -368,12 +378,12 @@ cassandra_token_ownership_ratio 78.9`, &Rows{
|
||||||
{
|
{
|
||||||
Metric: "foo",
|
Metric: "foo",
|
||||||
Value: 0.3,
|
Value: 0.3,
|
||||||
Timestamp: 2,
|
Timestamp: 2000,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Metric: "bar.baz",
|
Metric: "bar.baz",
|
||||||
Value: 0.34,
|
Value: 0.34,
|
||||||
Timestamp: 43,
|
Timestamp: 43000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -82,13 +82,13 @@ func TestParseStream(t *testing.T) {
|
||||||
f("foo 123 456", []Row{{
|
f("foo 123 456", []Row{{
|
||||||
Metric: "foo",
|
Metric: "foo",
|
||||||
Value: 123,
|
Value: 123,
|
||||||
Timestamp: 456,
|
Timestamp: 456000,
|
||||||
}})
|
}})
|
||||||
f(`foo{bar="baz"} 1 2`+"\n"+`aaa{} 3 4`, []Row{
|
f(`foo{bar="baz"} 1 2`+"\n"+`aaa{} 3 4`, []Row{
|
||||||
{
|
{
|
||||||
Metric: "aaa",
|
Metric: "aaa",
|
||||||
Value: 3,
|
Value: 3,
|
||||||
Timestamp: 4,
|
Timestamp: 4000,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Metric: "foo",
|
Metric: "foo",
|
||||||
|
@ -97,7 +97,7 @@ func TestParseStream(t *testing.T) {
|
||||||
Value: "baz",
|
Value: "baz",
|
||||||
}},
|
}},
|
||||||
Value: 1,
|
Value: 1,
|
||||||
Timestamp: 2,
|
Timestamp: 2000,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
f("foo 23", []Row{{
|
f("foo 23", []Row{{
|
||||||
|
|
Loading…
Reference in a new issue