Skip to content

Commit

Permalink
template: adding formatTime function to TemplateExpander (#10993)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jrkt and jrkt committed Jul 14, 2022
1 parent 97d7e09 commit ce1bf8b
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 13 deletions.
15 changes: 8 additions & 7 deletions docs/configuration/template_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ 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. |
| 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
Expand Down
35 changes: 29 additions & 6 deletions template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
63 changes: 63 additions & 0 deletions template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"context"
"math"
"net/url"
"reflect"
"testing"
"time"

Expand Down Expand Up @@ -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 }}",
Expand Down Expand Up @@ -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)
}
})
}
}

0 comments on commit ce1bf8b

Please sign in to comment.