Something went wrong. Try again.
dockscope is a TUI for viewing Docker containers, logs, metrics and other info, inspired by lazydocker and dtop.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132package parser
import ( "encoding/json" "maps" "time")
var ( timeKeys = []string{"time", "timestamp", "ts", "@timestamp"} levelKeys = []string{"level", "lvl", "severity"} msgKeys = []string{"msg", "message"} traceKeys = []string{"trace_id", "traceID", "traceId", "trace.id", "traceparent"} spanKeys = []string{"span_id", "spanID", "spanId", "span.id"})
type JSONParser struct{}
func (JSONParser) Parse(raw string) (LogEntry, bool) { var m map[string]any if err := json.Unmarshal([]byte(raw), &m); err != nil { return LogEntry{}, false }
if m == nil { return LogEntry{}, false }
entry := LogEntry{Raw: raw, Fields: make(map[string]any)}
entry.Timestamp = extractTime(m) entry.Level = extractString(m, levelKeys) entry.Message = extractString(m, msgKeys) entry.TraceID = normalizeTraceID(extractString(m, traceKeys)) entry.SpanID = extractString(m, spanKeys)
maps.Copy(entry.Fields, m)
return entry, true}
func extractString(m map[string]any, keys []string) string { for _, k := range keys { if v, ok := m[k]; ok { delete(m, k)
if s, ok := v.(string); ok { return s }
return "" } }
return ""}
func extractTime(m map[string]any) time.Time { for _, k := range timeKeys { v, ok := m[k] if !ok { continue }
delete(m, k)
return parseTimeValue(v) }
return time.Now()}
func parseTimeValue(v any) time.Time { switch t := v.(type) { case string: return parseTimeString(t) case float64: return parseTimeNumber(t) }
return time.Now()}
func parseTimeString(s string) time.Time { for _, layout := range []string{time.RFC3339Nano, time.RFC3339, "2006-01-02T15:04:05.000Z"} { if t, err := time.Parse(layout, s); err == nil { return t } }
return time.Now()}
func parseTimeNumber(n float64) time.Time { const ( millisThreshold = 1e12 microsThreshold = 1e15 nanosThreshold = 1e18 nanosPerSecond = int64(time.Second) )
switch { case n >= nanosThreshold: return time.Unix(0, int64(n)) case n >= microsThreshold: return time.Unix(0, int64(n)*int64(time.Microsecond)) case n >= millisThreshold: return time.Unix(0, int64(n)*int64(time.Millisecond)) default: secs := int64(n) nanos := int64((n - float64(secs)) * float64(nanosPerSecond))
return time.Unix(secs, nanos) }}
func normalizeTraceID(s string) string { const ( traceparentLen = 55 traceparentPrefixLen = 3 traceparentTraceIDLen = 32 dashAfterTrace = 35 dashBeforeFlags = 52 )
if len(s) == traceparentLen && s[2] == '-' && s[dashAfterTrace] == '-' && s[dashBeforeFlags] == '-' { return s[traceparentPrefixLen : traceparentPrefixLen+traceparentTraceIDLen] }
return s}