Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 678a228fb8 | |||
| 02db40de59 |
@@ -24,6 +24,7 @@ import (
|
|||||||
"go-simpler.org/env"
|
"go-simpler.org/env"
|
||||||
lol "lol.mleku.dev"
|
lol "lol.mleku.dev"
|
||||||
"lol.mleku.dev/chk"
|
"lol.mleku.dev/chk"
|
||||||
|
"lol.mleku.dev/log"
|
||||||
"next.orly.dev/pkg/logbuffer"
|
"next.orly.dev/pkg/logbuffer"
|
||||||
"next.orly.dev/pkg/version"
|
"next.orly.dev/pkg/version"
|
||||||
)
|
)
|
||||||
@@ -185,6 +186,15 @@ func New() (cfg *C, err error) {
|
|||||||
logbuffer.Init(cfg.LogBufferSize)
|
logbuffer.Init(cfg.LogBufferSize)
|
||||||
logbuffer.SetCurrentLevel(cfg.LogLevel)
|
logbuffer.SetCurrentLevel(cfg.LogLevel)
|
||||||
lol.Writer = logbuffer.NewBufferedWriter(lol.Writer, logbuffer.GlobalBuffer)
|
lol.Writer = logbuffer.NewBufferedWriter(lol.Writer, logbuffer.GlobalBuffer)
|
||||||
|
// Reinitialize the loggers to use the new wrapped Writer
|
||||||
|
// The lol.Main logger is initialized in init() with os.Stderr directly,
|
||||||
|
// so we need to recreate it with the new Writer
|
||||||
|
l, c, e := lol.New(lol.Writer, 2)
|
||||||
|
lol.Main.Log = l
|
||||||
|
lol.Main.Check = c
|
||||||
|
lol.Main.Errorf = e
|
||||||
|
// Also update the log package convenience variables
|
||||||
|
log.F, log.E, log.W, log.I, log.D, log.T = l.F, l.E, l.W, l.I, l.D, l.T
|
||||||
}
|
}
|
||||||
lol.SetLogLevel(cfg.LogLevel)
|
lol.SetLogLevel(cfg.LogLevel)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -17,12 +17,15 @@ type BufferedWriter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log format regex patterns
|
// Log format regex patterns
|
||||||
// lol library format: "2024/01/15 10:30:45 file.go:123 [INF] message"
|
// lol library format: "1703500000000000ℹ️ message /path/to/file.go:123"
|
||||||
// or similar variations
|
// - Unix microseconds timestamp
|
||||||
var logPattern = regexp.MustCompile(`^(\d{4}/\d{2}/\d{2}\s+\d{2}:\d{2}:\d{2}(?:\.\d+)?)\s+([^\s:]+):(\d+)\s+\[([A-Z]{3})\]\s+(.*)$`)
|
// - Level emoji (☠️, 🚨, ⚠️, ℹ️, 🔎, 👻)
|
||||||
|
// - Message
|
||||||
|
// - File:line location
|
||||||
|
var lolPattern = regexp.MustCompile(`^(\d{16})([☠️🚨⚠️ℹ️🔎👻]+)\s*(.*?)\s+([^\s]+:\d+)$`)
|
||||||
|
|
||||||
// Simple format: "[level] message"
|
// Simpler pattern for when emoji detection fails - just capture timestamp and rest
|
||||||
var simplePattern = regexp.MustCompile(`^\[([A-Z]{3})\]\s+(.*)$`)
|
var simplePattern = regexp.MustCompile(`^(\d{13,16})\s*(.*)$`)
|
||||||
|
|
||||||
// NewBufferedWriter creates a new BufferedWriter
|
// NewBufferedWriter creates a new BufferedWriter
|
||||||
func NewBufferedWriter(original io.Writer, buffer *Buffer) *BufferedWriter {
|
func NewBufferedWriter(original io.Writer, buffer *Buffer) *BufferedWriter {
|
||||||
@@ -64,6 +67,16 @@ func (w *BufferedWriter) Write(p []byte) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// emojiToLevel maps lol library level emojis to level strings
|
||||||
|
var emojiToLevel = map[string]string{
|
||||||
|
"☠️": "FTL",
|
||||||
|
"🚨": "ERR",
|
||||||
|
"⚠️": "WRN",
|
||||||
|
"ℹ️": "INF",
|
||||||
|
"🔎": "DBG",
|
||||||
|
"👻": "TRC",
|
||||||
|
}
|
||||||
|
|
||||||
// parseLine parses a log line into a LogEntry
|
// parseLine parses a log line into a LogEntry
|
||||||
func (w *BufferedWriter) parseLine(line string) LogEntry {
|
func (w *BufferedWriter) parseLine(line string) LogEntry {
|
||||||
entry := LogEntry{
|
entry := LogEntry{
|
||||||
@@ -72,47 +85,64 @@ func (w *BufferedWriter) parseLine(line string) LogEntry {
|
|||||||
Level: "INF",
|
Level: "INF",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try full pattern first
|
|
||||||
if matches := logPattern.FindStringSubmatch(line); matches != nil {
|
|
||||||
// Parse timestamp
|
|
||||||
if t, err := time.Parse("2006/01/02 15:04:05", matches[1]); err == nil {
|
|
||||||
entry.Timestamp = t
|
|
||||||
} else if t, err := time.Parse("2006/01/02 15:04:05.000", matches[1]); err == nil {
|
|
||||||
entry.Timestamp = t
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.File = matches[2]
|
|
||||||
if lineNum, err := strconv.Atoi(matches[3]); err == nil {
|
|
||||||
entry.Line = lineNum
|
|
||||||
}
|
|
||||||
entry.Level = matches[4]
|
|
||||||
entry.Message = matches[5]
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try simple pattern
|
|
||||||
if matches := simplePattern.FindStringSubmatch(line); matches != nil {
|
|
||||||
entry.Level = matches[1]
|
|
||||||
entry.Message = matches[2]
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect level from common prefixes
|
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
if strings.HasPrefix(line, "TRC") || strings.HasPrefix(line, "[TRC]") {
|
if line == "" {
|
||||||
entry.Level = "TRC"
|
return entry
|
||||||
} else if strings.HasPrefix(line, "DBG") || strings.HasPrefix(line, "[DBG]") {
|
|
||||||
entry.Level = "DBG"
|
|
||||||
} else if strings.HasPrefix(line, "INF") || strings.HasPrefix(line, "[INF]") {
|
|
||||||
entry.Level = "INF"
|
|
||||||
} else if strings.HasPrefix(line, "WRN") || strings.HasPrefix(line, "[WRN]") {
|
|
||||||
entry.Level = "WRN"
|
|
||||||
} else if strings.HasPrefix(line, "ERR") || strings.HasPrefix(line, "[ERR]") {
|
|
||||||
entry.Level = "ERR"
|
|
||||||
} else if strings.HasPrefix(line, "FTL") || strings.HasPrefix(line, "[FTL]") {
|
|
||||||
entry.Level = "FTL"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try lol pattern first: "1703500000000000ℹ️ message /path/to/file.go:123"
|
||||||
|
if matches := lolPattern.FindStringSubmatch(line); matches != nil {
|
||||||
|
// Parse Unix microseconds timestamp
|
||||||
|
if usec, err := strconv.ParseInt(matches[1], 10, 64); err == nil {
|
||||||
|
entry.Timestamp = time.UnixMicro(usec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map emoji to level
|
||||||
|
if level, ok := emojiToLevel[matches[2]]; ok {
|
||||||
|
entry.Level = level
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Message = strings.TrimSpace(matches[3])
|
||||||
|
|
||||||
|
// Parse file:line
|
||||||
|
loc := matches[4]
|
||||||
|
if idx := strings.LastIndex(loc, ":"); idx > 0 {
|
||||||
|
entry.File = loc[:idx]
|
||||||
|
if lineNum, err := strconv.Atoi(loc[idx+1:]); err == nil {
|
||||||
|
entry.Line = lineNum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try simple pattern - just grab timestamp and rest as message
|
||||||
|
if matches := simplePattern.FindStringSubmatch(line); matches != nil {
|
||||||
|
if usec, err := strconv.ParseInt(matches[1], 10, 64); err == nil {
|
||||||
|
// Could be microseconds or milliseconds
|
||||||
|
if len(matches[1]) >= 16 {
|
||||||
|
entry.Timestamp = time.UnixMicro(usec)
|
||||||
|
} else {
|
||||||
|
entry.Timestamp = time.UnixMilli(usec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rest := strings.TrimSpace(matches[2])
|
||||||
|
|
||||||
|
// Try to detect level from emoji in the rest
|
||||||
|
for emoji, level := range emojiToLevel {
|
||||||
|
if strings.HasPrefix(rest, emoji) {
|
||||||
|
entry.Level = level
|
||||||
|
rest = strings.TrimPrefix(rest, emoji)
|
||||||
|
rest = strings.TrimSpace(rest)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Message = rest
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: just store the whole line as message
|
||||||
|
entry.Message = line
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,3 +166,4 @@ func SetCurrentLevel(level string) string {
|
|||||||
}
|
}
|
||||||
return currentLevel
|
return currentLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v0.37.3
|
v0.38.1
|
||||||
|
|||||||
Reference in New Issue
Block a user