Skip to content

Commit cf84f34

Browse files
authored
feat: support timestamp reformatting (#129)
1 parent f3105f7 commit cf84f34

File tree

6 files changed

+85
-3
lines changed

6 files changed

+85
-3
lines changed

assets/example.log

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,6 @@ plain text log
5353
{"time":883612800.45,"level":"WARN","message": "If a man knows not to which port he sails, no wind is favorable","author": "Seneca"}
5454
{"time":"915148800000.45","level":"VERBOSE","message": "Begin at once to live, and count each separate day as a separate life","author": "Seneca"}
5555
{"time":946684800.45,"level":"VERBOSE","message": "Begin at once to live, and count each separate day as a separate life","author": "Seneca"}
56-
{"@timestamp":"2025-03-06T11:55:53.723682+01:00","@version":1,"host":"bad21ee895cd","message":"Message ..","level":"INFO"}
56+
{"@timestamp":"2025-03-06T11:55:53.723682+01:00","@version":1,"host":"bad21ee895cd","message":"Symphony format","level":"INFO"}
57+
{"time": "02 Jan 06 15:04 MST","level":"VERBOSE","message": "RFC822 time","author": "Seneca"}
58+
{"time": "Mon, 02 Jan 2006 15:04:05 MST","level":"VERBOSE","message": "RFC1123 time","author": "Seneca"}

example.jlv.jsonc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,31 @@
6565
"width": 0
6666
}
6767
],
68+
// Time layouts to reformat.
69+
//
70+
// If the time field has been parsed to any of the specified layouts,
71+
// it will be formatted according to "time_format" configuration.
72+
//
73+
// See: https://pkg.go.dev/time#pkg-constants.
74+
"time_layouts": [
75+
"01/02 03:04:05PM '06 -0700",
76+
"Mon Jan _2 15:04:05 2006",
77+
"Mon Jan _2 15:04:05 MST 2006",
78+
"Mon Jan 02 15:04:05 -0700 2006",
79+
"02 Jan 06 15:04 MST",
80+
"02 Jan 06 15:04 -0700",
81+
"Monday, 02-Jan-06 15:04:05 MST",
82+
"Mon, 02 Jan 2006 15:04:05 MST",
83+
"Mon, 02 Jan 2006 15:04:05 -0700",
84+
"2006-01-02T15:04:05Z07:00",
85+
"2006-01-02T15:04:05.999999999Z07:00",
86+
"Jan _2 15:04:05",
87+
"Jan _2 15:04:05.000",
88+
"Jan _2 15:04:05.000000",
89+
"Jan _2 15:04:05.000000000",
90+
"2006-01-02 15:04:05",
91+
"2006-01-02"
92+
],
6893
// Mapping of log level.
6994
// Possible values: none, trace, debug, info, warn, error, panic, fatal.
7095
"customLevelMapping": {

internal/pkg/config/config.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ type Config struct {
2525
Path string `json:"-"`
2626

2727
Fields []Field `json:"fields" validate:"min=1"`
28+
// TimeLayouts to reformat.
29+
TimeLayouts []string `json:"time_layouts"`
2830

2931
CustomLevelMapping map[string]string `json:"customLevelMapping"`
3032

@@ -65,6 +67,25 @@ func GetDefaultConfig() *Config {
6567
Path: "default",
6668
CustomLevelMapping: GetDefaultCustomLevelMapping(),
6769
MaxFileSizeBytes: 2 * units.GB,
70+
TimeLayouts: []string{
71+
time.Layout,
72+
time.ANSIC,
73+
time.UnixDate,
74+
time.RubyDate,
75+
time.RFC822,
76+
time.RFC822Z,
77+
time.RFC850,
78+
time.RFC1123,
79+
time.RFC1123Z,
80+
time.RFC3339,
81+
time.RFC3339Nano,
82+
time.Stamp,
83+
time.StampMilli,
84+
time.StampMicro,
85+
time.StampNano,
86+
time.DateTime,
87+
time.DateOnly,
88+
},
6889
Fields: []Field{{
6990
Title: "Time",
7091
Kind: FieldKindNumericTime,

internal/pkg/config/config_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,25 @@ func ExampleGetDefaultConfig() {
138138
// "width": 0
139139
// }
140140
// ],
141+
// "time_layouts": [
142+
// "01/02 03:04:05PM '06 -0700",
143+
// "Mon Jan _2 15:04:05 2006",
144+
// "Mon Jan _2 15:04:05 MST 2006",
145+
// "Mon Jan 02 15:04:05 -0700 2006",
146+
// "02 Jan 06 15:04 MST",
147+
// "02 Jan 06 15:04 -0700",
148+
// "Monday, 02-Jan-06 15:04:05 MST",
149+
// "Mon, 02 Jan 2006 15:04:05 MST",
150+
// "Mon, 02 Jan 2006 15:04:05 -0700",
151+
// "2006-01-02T15:04:05Z07:00",
152+
// "2006-01-02T15:04:05.999999999Z07:00",
153+
// "Jan _2 15:04:05",
154+
// "Jan _2 15:04:05.000",
155+
// "Jan _2 15:04:05.000000",
156+
// "Jan _2 15:04:05.000000000",
157+
// "2006-01-02 15:04:05",
158+
// "2006-01-02"
159+
// ],
141160
// "customLevelMapping": {
142161
// "10": "trace",
143162
// "20": "debug",

internal/pkg/source/entry.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func formatField(
173173
case config.FieldKindLevel:
174174
return string(ParseLevel(formatMessage(value), cfg.CustomLevelMapping))
175175
case config.FieldKindTime:
176-
return formatMessage(value)
176+
return formatMessage(reformatTime(value, cfg.TimeLayouts, timeFormat))
177177
case config.FieldKindSecondTime:
178178
return formatMessage(formatTimeValue(value, unitSeconds, timeFormat))
179179
case config.FieldKindMilliTime:
@@ -187,6 +187,17 @@ func formatField(
187187
}
188188
}
189189

190+
func reformatTime(value string, layoutsToReformat []string, timeFormat string) string {
191+
for _, laoyout := range layoutsToReformat {
192+
parsed, err := time.Parse(laoyout, value)
193+
if err == nil {
194+
return parsed.Format(timeFormat)
195+
}
196+
}
197+
198+
return value
199+
}
200+
190201
// parseLogEntry parses a single log entry from the json line.
191202
func parseLogEntry(
192203
line json.RawMessage,

internal/pkg/source/entry_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,11 @@ func TestNumericKindTimeFormatting(t *testing.T) {
469469
numericKindCases := [...]timeFormattingTestCase{{
470470
TestName: "Date passthru",
471471
JSON: `{"timestamp":"2023-10-08 20:00:00"}`,
472-
ExpectedOutput: "2023-10-08 20:00:00",
472+
ExpectedOutput: "2023-10-08T20:00:00Z",
473+
}, {
474+
TestName: "RFC1123 passthru",
475+
JSON: `{"@timestamp":"Mon, 02 Jan 2006 15:04:05 MST"}`,
476+
ExpectedOutput: "2006-01-02T15:04:05Z",
473477
}, {
474478
TestName: "Non-date string",
475479
JSON: `{"timestamp":"-"}`,

0 commit comments

Comments
 (0)