201 lines
4.0 KiB
Go
201 lines
4.0 KiB
Go
package logger
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"Moonshark/color"
|
|
)
|
|
|
|
// Log levels
|
|
const (
|
|
LevelDebug = iota
|
|
LevelInfo
|
|
LevelWarn
|
|
LevelError
|
|
LevelFatal
|
|
)
|
|
|
|
// Level config
|
|
var levels = map[int]struct {
|
|
tag string
|
|
color func(string) string
|
|
}{
|
|
LevelDebug: {"D", color.Cyan},
|
|
LevelInfo: {"I", color.Blue},
|
|
LevelWarn: {"W", color.Yellow},
|
|
LevelError: {"E", color.Red},
|
|
LevelFatal: {"F", color.Purple},
|
|
}
|
|
|
|
var (
|
|
global *Logger
|
|
globalOnce sync.Once
|
|
)
|
|
|
|
type Logger struct {
|
|
out io.Writer
|
|
enabled atomic.Bool
|
|
timestamp atomic.Bool
|
|
debug atomic.Bool
|
|
colors atomic.Bool
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func init() {
|
|
globalOnce.Do(func() {
|
|
global = &Logger{out: os.Stdout}
|
|
global.enabled.Store(true)
|
|
global.timestamp.Store(true)
|
|
global.colors.Store(true)
|
|
})
|
|
}
|
|
|
|
func applyColor(text string, colorFunc func(string) string) string {
|
|
if global.colors.Load() {
|
|
return colorFunc(text)
|
|
}
|
|
return text
|
|
}
|
|
|
|
func write(level int, msg string) {
|
|
if !global.enabled.Load() {
|
|
return
|
|
}
|
|
|
|
if level == LevelDebug && !global.debug.Load() {
|
|
return
|
|
}
|
|
|
|
var parts []string
|
|
|
|
if global.timestamp.Load() {
|
|
ts := applyColor(time.Now().Format("3:04PM"), color.Gray)
|
|
parts = append(parts, ts)
|
|
}
|
|
|
|
if cfg, ok := levels[level]; ok {
|
|
tag := applyColor("["+cfg.tag+"]", cfg.color)
|
|
parts = append(parts, tag)
|
|
}
|
|
|
|
parts = append(parts, msg)
|
|
line := strings.Join(parts, " ") + "\n"
|
|
|
|
global.mu.Lock()
|
|
fmt.Fprint(global.out, line)
|
|
if level == LevelFatal {
|
|
if f, ok := global.out.(*os.File); ok {
|
|
f.Sync()
|
|
}
|
|
}
|
|
global.mu.Unlock()
|
|
|
|
if level == LevelFatal {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func log(level int, format string, args ...any) {
|
|
var msg string
|
|
if len(args) > 0 {
|
|
msg = fmt.Sprintf(format, args...)
|
|
} else {
|
|
msg = format
|
|
}
|
|
write(level, msg)
|
|
}
|
|
|
|
func Debugf(format string, args ...any) { log(LevelDebug, format, args...) }
|
|
func Infof(format string, args ...any) { log(LevelInfo, format, args...) }
|
|
func Warnf(format string, args ...any) { log(LevelWarn, format, args...) }
|
|
func Errorf(format string, args ...any) { log(LevelError, format, args...) }
|
|
func Fatalf(format string, args ...any) { log(LevelFatal, format, args...) }
|
|
|
|
func Raw(format string, args ...any) {
|
|
if !global.enabled.Load() {
|
|
return
|
|
}
|
|
|
|
var msg string
|
|
if len(args) > 0 {
|
|
msg = fmt.Sprintf(format, args...)
|
|
} else {
|
|
msg = format
|
|
}
|
|
|
|
global.mu.Lock()
|
|
fmt.Fprint(global.out, msg+"\n")
|
|
global.mu.Unlock()
|
|
}
|
|
|
|
func Request(status int, method, path string, duration time.Duration) {
|
|
if !global.enabled.Load() {
|
|
return
|
|
}
|
|
|
|
var statusColor func(string) string
|
|
switch {
|
|
case status < 300:
|
|
statusColor = color.Green
|
|
case status < 400:
|
|
statusColor = color.Cyan
|
|
case status < 500:
|
|
statusColor = color.Yellow
|
|
default:
|
|
statusColor = color.Red
|
|
}
|
|
|
|
var dur string
|
|
us := duration.Microseconds()
|
|
switch {
|
|
case us < 1000:
|
|
dur = fmt.Sprintf("%.0fµs", float64(us))
|
|
case us < 1000000:
|
|
dur = fmt.Sprintf("%.1fms", float64(us)/1000)
|
|
default:
|
|
dur = fmt.Sprintf("%.2fs", duration.Seconds())
|
|
}
|
|
|
|
var parts []string
|
|
|
|
if global.timestamp.Load() {
|
|
ts := applyColor(time.Now().Format("3:04PM"), color.Gray)
|
|
parts = append(parts, ts)
|
|
}
|
|
|
|
parts = append(parts,
|
|
applyColor("["+method+"]", color.Gray),
|
|
applyColor(fmt.Sprintf("%d", status), statusColor),
|
|
applyColor(path, color.Gray),
|
|
applyColor(dur, color.Gray),
|
|
)
|
|
|
|
msg := strings.Join(parts, " ")
|
|
|
|
global.mu.Lock()
|
|
fmt.Fprint(global.out, msg+"\n")
|
|
global.mu.Unlock()
|
|
}
|
|
|
|
func SetOutput(w io.Writer) {
|
|
global.mu.Lock()
|
|
global.out = w
|
|
global.mu.Unlock()
|
|
}
|
|
|
|
func Enable() { global.enabled.Store(true) }
|
|
func Disable() { global.enabled.Store(false) }
|
|
func IsEnabled() bool { return global.enabled.Load() }
|
|
func EnableColors() { global.colors.Store(true) }
|
|
func DisableColors() { global.colors.Store(false) }
|
|
func ColorsEnabled() bool { return global.colors.Load() }
|
|
func Timestamp(enabled bool) { global.timestamp.Store(enabled) }
|
|
func Debug(enabled bool) { global.debug.Store(enabled) }
|
|
func IsDebug() bool { return global.debug.Load() }
|