diff --git a/go.mod b/go.mod index f392879..8832123 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/fatih/color v1.16.0 github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 github.com/go-logfmt/logfmt v0.5.1 + github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/humanlogio/api/go v0.0.0-20241128170213-590d167300cd github.com/humanlogio/humanlog-pro v0.0.0-20241129104809-3580d74828a9 diff --git a/go.sum b/go.sum index ab015c5..80cce09 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,6 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8 github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/humanlogio/api/go v0.0.0-20241128170213-590d167300cd h1:449C6cnB4W6DblDMPfCfA4xyEkiYMpngGf7TEX9O8ro= github.com/humanlogio/api/go v0.0.0-20241128170213-590d167300cd/go.mod h1:+hU/MU1g6QvtbeknKOlUI1yEStVqkPJ8jmYIj63OV5I= -github.com/humanlogio/humanlog-pro v0.0.0-20241129104046-2603398b2ef1 h1:eDsZPPr9GBNNh8wy43wPlb/98R1uCHuxML/r99GIkP4= -github.com/humanlogio/humanlog-pro v0.0.0-20241129104046-2603398b2ef1/go.mod h1:q+Kj7V58BQVVzGgcoyhtE2RXXprEi6DrqmhV/cThKkc= github.com/humanlogio/humanlog-pro v0.0.0-20241129104809-3580d74828a9 h1:tdUCzFh8qvnWNCmxub0KSj1lIiCeWqvRjsMSSIApneE= github.com/humanlogio/humanlog-pro v0.0.0-20241129104809-3580d74828a9/go.mod h1:zq05mTZQXvKheFiAGlPx6+VSo29jw2ER8oy8DIQKW2Q= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= diff --git a/handler.go b/handler.go index 96b373b..511bfcd 100644 --- a/handler.go +++ b/handler.go @@ -16,7 +16,7 @@ type Handler interface { var DefaultOptions = func() *HandlerOptions { opts := &HandlerOptions{ - TimeFields: []string{"time", "ts", "@timestamp", "timestamp", "Timestamp"}, + TimeFields: []string{"time", "ts", "@timestamp", "timestamp", "Timestamp", "asctime"}, MessageFields: []string{"message", "msg", "Body"}, LevelFields: []string{"level", "lvl", "loglevel", "severity", "SeverityText"}, timeNow: time.Now, diff --git a/json_handler_test.go b/json_handler_test.go index cf039bc..2eea1f6 100644 --- a/json_handler_test.go +++ b/json_handler_test.go @@ -5,9 +5,12 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" typesv1 "github.com/humanlogio/api/go/types/v1" "github.com/humanlogio/humanlog" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/timestamppb" ) func TestJSONHandler_UnmarshalJSON_ParsesFields(t *testing.T) { @@ -199,3 +202,36 @@ func TestJsonHandler_TryHandle_FlattenedArrayFields_NestedArray(t *testing.T) { require.Equal(t, "world", handler.Fields["peers.2.1"]) require.Equal(t, "\"bar\"", handler.Fields["peers.3.foo"]) } + +func TestParseAsctimeFields(t *testing.T) { + tests := []struct { + name string + raw []byte + want *timestamppb.Timestamp + }{ + { + name: "asctime", + raw: []byte(`{"asctime": ["12-05-05 22:11:08,506248"]}`), + want: timestamppb.New(time.Date(2012, 5, 5, 22, 11, 8, 506248000, time.UTC)), + }, + { + name: "time", + raw: []byte(`{"time": "12-05-05 22:11:08,506248"}`), + want: timestamppb.New(time.Date(2012, 5, 5, 22, 11, 8, 506248000, time.UTC)), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + opts := humanlog.DefaultOptions() + h := humanlog.JSONHandler{Opts: opts} + ev := new(typesv1.StructuredLogEvent) + if !h.TryHandle(test.raw, ev) { + t.Fatalf("failed to handle log") + } + // timezone should be identified before parsing... we can't just treat as UTC + got := ev.Timestamp + require.Empty(t, cmp.Diff(test.want, got, protocmp.Transform())) + }) + } +} diff --git a/time_parse.go b/time_parse.go index bf908eb..a31f3b0 100644 --- a/time_parse.go +++ b/time_parse.go @@ -26,6 +26,7 @@ var TimeFormats = []string{ time.StampNano, "2006/01/02 15:04:05", "2006/01/02 15:04:05.999999999", + "06-01-02 15:04:05,999", } func parseTimeFloat64(value float64) time.Time { @@ -86,6 +87,18 @@ func tryParseTime(value interface{}) (time.Time, bool) { return parseTimeFloat64(float64(v)), true case int64: return parseTimeFloat64(float64(v)), true + case []interface{}: + if len(v) == 1 { + if timeStr, ok := v[0].(string); ok { + for _, layout := range TimeFormats { + t, err := time.Parse(layout, timeStr) + if err == nil { + t = fixTimebeforeUnixZero(t) + return t, true + } + } + } + } } return t, false }