diff --git a/promslog/slog.go b/promslog/slog.go index 8da43aef5..02370f175 100644 --- a/promslog/slog.go +++ b/promslog/slog.go @@ -197,6 +197,13 @@ func newGoKitStyleReplaceAttrFunc(lvl *Level) func(groups []string, a slog.Attr) } default: } + + // Ensure time.Duration values are _always_ formatted as a Go + // duration string (ie, "1d2h3m"). + if v, ok := a.Value.Any().(time.Duration); ok { + a.Value = slog.StringValue(v.String()) + } + return a } } @@ -238,6 +245,13 @@ func defaultReplaceAttr(_ []string, a slog.Attr) slog.Attr { } default: } + + // Ensure time.Duration values are _always_ formatted as a Go duration + // string (ie, "1d2h3m"). + if v, ok := a.Value.Any().(time.Duration); ok { + a.Value = slog.StringValue(v.String()) + } + return a } diff --git a/promslog/slog_test.go b/promslog/slog_test.go index ea4e176c2..91e79a9fb 100644 --- a/promslog/slog_test.go +++ b/promslog/slog_test.go @@ -21,6 +21,7 @@ import ( "regexp" "strings" "testing" + "time" "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" @@ -94,6 +95,33 @@ func getLogEntryLevelCounts(s string, re *regexp.Regexp) map[string]int { return counters } +func TestDurationValues(t *testing.T) { + dur, err := time.ParseDuration("1m30s") + require.NoError(t, err) + + tests := map[string]struct { + logFormat string + want string + }{ + "logfmt_duration_testing": {want: "duration_raw=1m30s duration_string=1m30s", logFormat: "logfmt"}, + "json_duration_testing": {want: "\"duration_raw\":\"1m30s\",\"duration_string\":\"1m30s\"", logFormat: "json"}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + var buf bytes.Buffer + config := &Config{ + Writer: &buf, + Format: NewFormat(), + } + require.NoError(t, config.Format.Set(tc.logFormat)) + logger := New(config) + logger.Info("duration testing", "duration_raw", dur, "duration_string", dur.String()) + require.Contains(t, buf.String(), tc.want) + }) + } +} + func TestDynamicLevels(t *testing.T) { var buf bytes.Buffer wantedLevelCounts := map[string]int{"info": 1, "debug": 1}