mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-24 05:04:05 -08:00
template: adding formatTime function to TemplateExpander (#10993)
Signed-off-by: Jonathan Stevens <jonathanstevens89@gmail.com> Signed-off-by: Jonathan Stevens <jon.stevens@getweave.com> Co-authored-by: Jonathan Stevens <jon.stevens@getweave.com>
This commit is contained in:
parent
97d7e09e0b
commit
ce1bf8b15a
|
@ -52,12 +52,13 @@ If functions are used in a pipeline, the pipeline value is passed as the last ar
|
|||
### Numbers
|
||||
|
||||
| Name | Arguments | Returns | Notes |
|
||||
| ------------------ | -----------------| --------| --------- |
|
||||
|---------------------| -----------------| --------| --------- |
|
||||
| humanize | number or string | string | Converts a number to a more readable format, using [metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix).
|
||||
| humanize1024 | number or string | string | Like `humanize`, but uses 1024 as the base rather than 1000. |
|
||||
| humanizeDuration | number or string | string | Converts a duration in seconds to a more readable format. |
|
||||
| humanizePercentage | number or string | string | Converts a ratio value to a fraction of 100. |
|
||||
| humanizeTimestamp | number or string | string | Converts a Unix timestamp in seconds to a more readable format. |
|
||||
| toTime | number or string | *time.Time | Converts a Unix timestamp in seconds to a time.Time. |
|
||||
|
||||
Humanizing functions are intended to produce reasonable output for consumption
|
||||
by humans, and are not guaranteed to return the same results between Prometheus
|
||||
|
|
|
@ -45,6 +45,8 @@ var (
|
|||
Name: "prometheus_template_text_expansions_total",
|
||||
Help: "The total number of template text expansions.",
|
||||
})
|
||||
|
||||
errNaNOrInf = errors.New("value is NaN or Inf")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -315,15 +317,24 @@ func NewTemplateExpander(
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
|
||||
tm, err := floatToTime(v)
|
||||
switch {
|
||||
case errors.Is(err, errNaNOrInf):
|
||||
return fmt.Sprintf("%.4g", v), nil
|
||||
case err != nil:
|
||||
return "", err
|
||||
}
|
||||
timestamp := v * 1e9
|
||||
if timestamp > math.MaxInt64 || timestamp < math.MinInt64 {
|
||||
return "", fmt.Errorf("%v cannot be represented as a nanoseconds timestamp since it overflows int64", v)
|
||||
|
||||
return fmt.Sprint(tm), nil
|
||||
},
|
||||
"toTime": func(i interface{}) (*time.Time, error) {
|
||||
v, err := convertToFloat(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := model.TimeFromUnixNano(int64(timestamp)).Time().UTC()
|
||||
return fmt.Sprint(t), nil
|
||||
|
||||
return floatToTime(v)
|
||||
},
|
||||
"pathPrefix": func() string {
|
||||
return externalURL.Path
|
||||
|
@ -446,3 +457,15 @@ func (te Expander) ParseTest() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func floatToTime(v float64) (*time.Time, error) {
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
return nil, errNaNOrInf
|
||||
}
|
||||
timestamp := v * 1e9
|
||||
if timestamp > math.MaxInt64 || timestamp < math.MinInt64 {
|
||||
return nil, fmt.Errorf("%v cannot be represented as a nanoseconds timestamp since it overflows int64", v)
|
||||
}
|
||||
t := model.TimeFromUnixNano(int64(timestamp)).Time().UTC()
|
||||
return &t, nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"context"
|
||||
"math"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -429,6 +430,16 @@ func TestTemplateExpansion(t *testing.T) {
|
|||
text: `{{ "1435065584.128" | humanizeTimestamp }}`,
|
||||
output: "2015-06-23 13:19:44.128 +0000 UTC",
|
||||
},
|
||||
{
|
||||
// ToTime - model.SampleValue input - float64.
|
||||
text: `{{ (1435065584.128 | toTime).Format "2006" }}`,
|
||||
output: "2015",
|
||||
},
|
||||
{
|
||||
// ToTime - model.SampleValue input - string.
|
||||
text: `{{ ("1435065584.128" | toTime).Format "2006" }}`,
|
||||
output: "2015",
|
||||
},
|
||||
{
|
||||
// Title.
|
||||
text: "{{ \"aa bb CC\" | title }}",
|
||||
|
@ -560,3 +571,55 @@ func testTemplateExpansion(t *testing.T, scenarios []scenario) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_floatToTime(t *testing.T) {
|
||||
type args struct {
|
||||
v float64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *time.Time
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"happy path",
|
||||
args{
|
||||
v: 1657155181,
|
||||
},
|
||||
func() *time.Time {
|
||||
tm := time.Date(2022, 7, 7, 0, 53, 1, 0, time.UTC)
|
||||
return &tm
|
||||
}(),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"more than math.MaxInt64",
|
||||
args{
|
||||
v: 1.79769313486231570814527423731704356798070e+300,
|
||||
},
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"less than math.MinInt64",
|
||||
args{
|
||||
v: -1.79769313486231570814527423731704356798070e+300,
|
||||
},
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := floatToTime(tt.args.v)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("floatToTime() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("floatToTime() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue