diff --git a/logger/logger.go b/logger/logger.go index 4643a3b..fe038b2 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -31,11 +31,12 @@ type Payload struct { // Line contains properties related to an individual log message. type Line struct { - App string `json:"app,omitempty"` - Body string `json:"line,omitempty"` - Level string `json:"level,omitempty"` - Env string `json:"env,omitempty"` - Meta metaEnvelope `json:"meta,omitempty"` + Body string `json:"line"` + Timestamp int64 `json:"timestamp"` + App string `json:"app,omitempty"` + Level string `json:"level,omitempty"` + Env string `json:"env,omitempty"` + Meta metaEnvelope `json:"meta,omitempty"` } type ingestAPIResponse struct { diff --git a/logger/logger_test.go b/logger/logger_test.go index e51815b..0002ec8 100644 --- a/logger/logger_test.go +++ b/logger/logger_test.go @@ -81,6 +81,7 @@ func TestLogger_Log(t *testing.T) { assert.Equal(t, "testing", line["line"]) assert.Equal(t, "info", line["level"]) assert.Equal(t, "test", line["app"]) + assert.NotEmpty(t, line["timestamp"]) assert.Equal(t, "application/json", head["Content-Type"][0]) assert.Equal(t, "abc123", head["Apikey"][0]) @@ -95,11 +96,14 @@ func TestLogger_LogWithOptions(t *testing.T) { })) defer ts.Close() + now := time.Now() + o := Options{ IngestURL: ts.URL, App: "app", Env: "development", Level: "info", + Timestamp: now, } l, err := NewLogger(o, "abc123") @@ -121,6 +125,7 @@ func TestLogger_LogWithOptions(t *testing.T) { assert.Equal(t, "anotherapp", line["app"]) assert.Equal(t, "production", line["env"]) assert.Equal(t, "error", line["level"]) + assert.Equal(t, now.UnixNano()/int64(time.Millisecond), int64(line["timestamp"].(float64))) }) t.Run("Invalid options", func(t *testing.T) { @@ -167,6 +172,7 @@ func TestLogger_LogWithLevel(t *testing.T) { line := ls[0].(map[string]interface{}) assert.Equal(t, "testing", line["line"]) assert.Equal(t, "error", line["level"]) + assert.NotEmpty(t, line["timestamp"]) } func TestLogger_LogWithMeta(t *testing.T) { @@ -196,6 +202,7 @@ func TestLogger_LogWithMeta(t *testing.T) { ls := body["lines"].([]interface{}) line := ls[0].(map[string]interface{}) assert.Equal(t, meta, line["meta"]) + assert.NotEmpty(t, line["timestamp"]) } func TestLogger_LogWithMetaIndexed(t *testing.T) { @@ -224,6 +231,7 @@ func TestLogger_LogWithMetaIndexed(t *testing.T) { ls := body["lines"].([]interface{}) line := ls[0].(map[string]interface{}) assert.NotEmpty(t, line["meta"]) + assert.NotEmpty(t, line["timestamp"]) meta := line["meta"].(map[string](interface{})) assert.Equal(t, "value", meta["key"]) diff --git a/logger/options.go b/logger/options.go index b5d363d..9c8a55f 100644 --- a/logger/options.go +++ b/logger/options.go @@ -37,6 +37,7 @@ type Options struct { MaxBufferLen int Meta string Tags string + Timestamp time.Time } func (e InvalidOptionMessage) String() string { @@ -104,10 +105,10 @@ func (options *Options) setDefaults() { if options.FlushInterval == 0 { options.FlushInterval = defaultFlushInterval } - if options.MaxBufferLen == 0 { - options.MaxBufferLen = defaultMaxBufferLen - } if options.IngestURL == "" { options.IngestURL = defaultIngestURL } + if options.MaxBufferLen == 0 { + options.MaxBufferLen = defaultMaxBufferLen + } } diff --git a/logger/transport.go b/logger/transport.go index 52449ef..5b04713 100644 --- a/logger/transport.go +++ b/logger/transport.go @@ -98,6 +98,12 @@ func (t *transport) send(msgs []Message) error { Level: msg.Options.Level, } + timestamp := msg.Options.Timestamp + if timestamp.IsZero() { + timestamp = time.Now() + } + line.Timestamp = timestamp.UnixNano() / int64(time.Millisecond) + if msg.Options.Meta != "" { line.Meta = metaEnvelope{ indexed: msg.Options.IndexMeta,