big ol logger refactor

This commit is contained in:
Sky Johnson 2025-05-24 11:06:22 -05:00
parent d1ec8ade9c
commit bf5a841716
11 changed files with 224 additions and 322 deletions

View File

@ -9,6 +9,7 @@ import (
"Moonshark/runner"
"Moonshark/sessions"
"Moonshark/utils"
"Moonshark/utils/color"
"Moonshark/utils/config"
"Moonshark/utils/logger"
"Moonshark/utils/metadata"
@ -73,7 +74,7 @@ func New(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter,
// ListenAndServe starts the server on the given address
func (s *Server) ListenAndServe(addr string) error {
logger.Server("Catch the swell at http://localhost%s", addr)
logger.Info("Catch the swell at %s", color.Apply("http://localhost"+addr, color.Cyan))
return s.fasthttpServer.ListenAndServe(addr)
}
@ -95,7 +96,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
if s.debugMode && path == "/debug/stats" {
s.handleDebugStats(ctx)
if s.loggingEnabled {
LogRequest(ctx.Response.StatusCode(), method, path, time.Since(start))
logger.LogRequest(ctx.Response.StatusCode(), method, path, time.Since(start))
}
return
}
@ -103,7 +104,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
s.processRequest(ctx, method, path)
if s.loggingEnabled {
LogRequest(ctx.Response.StatusCode(), method, path, time.Since(start))
logger.LogRequest(ctx.Response.StatusCode(), method, path, time.Since(start))
}
}

View File

@ -3,13 +3,9 @@ package http
import (
"crypto/rand"
"encoding/base64"
"fmt"
"mime/multipart"
"strings"
"sync"
"time"
"Moonshark/utils/logger"
"github.com/valyala/fasthttp"
)
@ -29,52 +25,6 @@ var (
}
)
// LogRequest logs an HTTP request with its status code and duration
func LogRequest(statusCode int, method, path string, duration time.Duration) {
var statusColor, methodColor string
// Simplified color assignment
switch {
case statusCode < 300:
statusColor = "\u001b[32m" // Green for 2xx
case statusCode < 400:
statusColor = "\u001b[36m" // Cyan for 3xx
case statusCode < 500:
statusColor = "\u001b[33m" // Yellow for 4xx
default:
statusColor = "\u001b[31m" // Red for 5xx+
}
switch method {
case "GET":
methodColor = "\u001b[32m"
case "POST":
methodColor = "\u001b[34m"
case "PUT":
methodColor = "\u001b[33m"
case "DELETE":
methodColor = "\u001b[31m"
default:
methodColor = "\u001b[35m"
}
// Optimized duration formatting
var durationStr string
micros := duration.Microseconds()
if micros < 1000 {
durationStr = fmt.Sprintf("%.0fµs", float64(micros))
} else if micros < 1000000 {
durationStr = fmt.Sprintf("%.1fms", float64(micros)/1000)
} else {
durationStr = fmt.Sprintf("%.2fs", duration.Seconds())
}
logger.Server("%s%d\u001b[0m %s%s\u001b[0m %s %s",
statusColor, statusCode,
methodColor, method,
path, durationStr)
}
// QueryToLua converts HTTP query args to a Lua-friendly map
func QueryToLua(ctx *fasthttp.RequestCtx) map[string]any {
args := ctx.QueryArgs()

137
main.go
View File

@ -8,6 +8,7 @@ import (
"os"
"os/signal"
"path/filepath"
"strconv"
"syscall"
"time"
@ -15,8 +16,10 @@ import (
"Moonshark/routers"
"Moonshark/runner"
"Moonshark/sessions"
"Moonshark/utils/color"
"Moonshark/utils/config"
"Moonshark/utils/logger"
"Moonshark/utils/metadata"
"Moonshark/watchers"
)
@ -33,23 +36,42 @@ type Moonshark struct {
func main() {
configPath := flag.String("config", "config.lua", "Path to configuration file")
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
debugFlag := flag.Bool("debug", false, "Enable debug mode")
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
scriptMode := *scriptPath != ""
flag.Parse()
// Initialize logger with basic settings first
logger.InitGlobalLogger(logger.LevelInfo, true, false)
banner()
mode := ""
if scriptMode {
mode = "[Script Mode]"
} else {
mode = "[Server Mode]"
}
fmt.Printf("%s %s\n\n", color.Apply(mode, color.Gray), color.Apply("v"+metadata.Version, color.Blue))
// Initialize logger
logger.InitGlobalLogger(true, false)
// Load config
cfg, err := config.Load(*configPath)
if err != nil {
logger.Warning("Config load failed: %v, using defaults", color.Apply(err.Error(), color.Red))
cfg = config.New()
}
// Setup logging with debug mode
if *debugFlag || cfg.Server.Debug {
logger.EnableDebug()
logger.Debug("Debug logging enabled")
}
scriptMode := *scriptPath != ""
var moonshark *Moonshark
var err error
if scriptMode {
logger.Server("Moonshark script execution mode 🦈")
moonshark, err = initScriptMode(*configPath, *debugFlag)
moonshark, err = initScriptMode(cfg)
} else {
logger.Server("Moonshark server mode 🦈")
moonshark, err = initServerMode(*configPath, *debugFlag)
moonshark, err = initServerMode(cfg, *debugFlag)
}
if err != nil {
@ -82,23 +104,15 @@ func main() {
<-stop
fmt.Print("\n")
logger.Server("Shutdown signal received")
logger.Info("Shutdown signal received")
}
// initScriptMode initializes minimal components needed for script execution
func initScriptMode(configPath string, debug bool) (*Moonshark, error) {
moonshark := &Moonshark{scriptMode: true}
// Load config (minimal validation for script mode)
cfg, err := config.Load(configPath)
if err != nil {
logger.Warning("Config load failed: %v, using defaults", err)
cfg = config.New()
func initScriptMode(cfg *config.Config) (*Moonshark, error) {
moonshark := &Moonshark{
Config: cfg,
scriptMode: true,
}
moonshark.Config = cfg
// Setup logging
setupLogging(cfg, debug)
// Only initialize the Lua runner with required paths
runnerOpts := []runner.RunnerOption{
@ -108,6 +122,7 @@ func initScriptMode(configPath string, debug bool) (*Moonshark, error) {
runner.WithDataDir(cfg.Dirs.Data),
}
var err error
moonshark.LuaRunner, err = runner.NewRunner(runnerOpts...)
if err != nil {
return nil, fmt.Errorf("failed to initialize Lua runner: %v", err)
@ -118,22 +133,16 @@ func initScriptMode(configPath string, debug bool) (*Moonshark, error) {
}
// initServerMode initializes all components needed for server operation
func initServerMode(configPath string, debug bool) (*Moonshark, error) {
moonshark := &Moonshark{scriptMode: false}
cfg, err := config.Load(configPath)
if err != nil {
logger.Warning("Config load failed: %v, using defaults", err)
cfg = config.New()
func initServerMode(cfg *config.Config, debug bool) (*Moonshark, error) {
moonshark := &Moonshark{
Config: cfg,
scriptMode: false,
}
moonshark.Config = cfg
if debug {
cfg.Server.Debug = true
}
setupLogging(cfg, debug)
if err := initRouters(moonshark); err != nil {
return nil, err
}
@ -170,7 +179,7 @@ func (s *Moonshark) RunScript(scriptPath string) error {
return fmt.Errorf("script file not found: %s", scriptPath)
}
logger.Server("Executing: %s", scriptPath)
logger.Info("Executing: %s", scriptPath)
resp, err := s.LuaRunner.RunScriptFile(scriptPath)
if err != nil {
@ -178,9 +187,9 @@ func (s *Moonshark) RunScript(scriptPath string) error {
}
if resp != nil && resp.Body != nil {
logger.Server("Script result: %v", resp.Body)
logger.Info("Script result: %v", resp.Body)
} else {
logger.Server("Script executed successfully (no return value)")
logger.Info("Script executed successfully (no return value)")
}
return nil
@ -192,7 +201,7 @@ func (s *Moonshark) Start() error {
return errors.New("cannot start server in script mode")
}
logger.Server("Surf's up on port %d!", s.Config.Server.Port)
logger.Info("Surf's up on port %s!", color.Apply(strconv.Itoa(s.Config.Server.Port), color.Cyan))
go func() {
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil {
@ -207,7 +216,7 @@ func (s *Moonshark) Start() error {
// Shutdown gracefully shuts down Moonshark
func (s *Moonshark) Shutdown() error {
logger.Server("Shutting down...")
logger.Info("Shutting down...")
if !s.scriptMode && s.HTTPServer != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
@ -228,30 +237,10 @@ func (s *Moonshark) Shutdown() error {
s.LuaRunner.Close()
}
logger.Server("Shutdown complete")
logger.Info("Shutdown complete")
return nil
}
func setupLogging(cfg *config.Config, debug bool) {
switch cfg.Server.LogLevel {
case "warn":
logger.SetLevel(logger.LevelWarning)
case "error":
logger.SetLevel(logger.LevelError)
case "server":
logger.SetLevel(logger.LevelServer)
case "fatal":
logger.SetLevel(logger.LevelFatal)
default:
logger.SetLevel(logger.LevelInfo)
}
if debug || cfg.Server.Debug {
logger.EnableDebug()
logger.Debug("Debug logging enabled")
}
}
func dirExists(path string) bool {
info, err := os.Stat(path)
if err != nil {
@ -281,16 +270,16 @@ func initRouters(s *Moonshark) error {
return fmt.Errorf("lua router init failed: %v", err)
}
}
logger.Info("Lua router initialized: %s", s.Config.Dirs.Routes)
logger.Info("LuaRouter is g2g! %s", color.Set(s.Config.Dirs.Routes, color.Yellow))
if dirExists(s.Config.Dirs.Static) {
s.StaticRouter, err = routers.NewStaticRouter(s.Config.Dirs.Static)
if err != nil {
return fmt.Errorf("static router init failed: %v", err)
}
logger.Info("Static router initialized: %s", s.Config.Dirs.Static)
logger.Info("StaticRouter is g2g! %s", color.Apply(s.Config.Dirs.Static, color.Yellow))
} else {
logger.Warning("Static directory not found: %s", s.Config.Dirs.Static)
logger.Warning("Static directory not found... %s", color.Apply(s.Config.Dirs.Static, color.Yellow))
}
return nil
@ -298,13 +287,13 @@ func initRouters(s *Moonshark) error {
func initRunner(s *Moonshark) error {
if !dirExists(s.Config.Dirs.Override) {
logger.Warning("Override directory not found: %s", s.Config.Dirs.Override)
logger.Warning("Override directory not found... %s", color.Apply(s.Config.Dirs.Override, color.Yellow))
s.Config.Dirs.Override = ""
}
for _, dir := range s.Config.Dirs.Libs {
if !dirExists(dir) {
logger.Warning("Lib directory not found: %s", dir)
logger.Warning("Lib directory not found... %s", color.Apply(dir, color.Yellow))
}
}
@ -336,7 +325,7 @@ func initRunner(s *Moonshark) error {
return fmt.Errorf("lua runner init failed: %v", err)
}
logger.Server("Lua runner initialized with pool size: %d", poolSize)
logger.Info("LuaRunner is g2g with %s states!", color.Apply(strconv.Itoa(poolSize), color.Yellow))
return nil
}
@ -365,8 +354,26 @@ func setupWatchers(s *Moonshark) error {
return manager.UnwatchDirectory(dirPath)
})
}
logger.Info("Watching %d module directories", len(moduleWatchers))
plural := ""
if len(moduleWatchers) == 1 {
plural = "directory"
} else {
plural = "directories"
}
logger.Info("Watching %s module %s.", color.Apply(strconv.Itoa(len(moduleWatchers)), color.Yellow), plural)
}
return nil
}
func banner() {
banner := `
_____ _________.__ __
/ \ ____ ____ ____ / _____/| |__ _____ _______| | __
/ \ / \ / _ \ / _ \ / \ \_____ \ | | \\__ \\_ __ \ |/ /
/ Y ( <_> | <_> ) | \/ \| Y \/ __ \| | \/ <
\____|__ /\____/ \____/|___| /_______ /|___| (____ /__| |__|_ \
\/ \/ \/ \/ \/ \/
`
fmt.Println(color.Apply(banner, color.Blue))
}

View File

@ -8,6 +8,7 @@ import (
"strings"
"time"
"Moonshark/utils/color"
"Moonshark/utils/logger"
lru "git.sharkk.net/Go/LRU"
@ -51,7 +52,7 @@ func InitFS(basePath string) error {
// Initialize file cache with 2000 entries (reasonable for most use cases)
fileCache = lru.NewLRUCache(2000)
logger.Server("Virtual filesystem initialized at: %s", fsBasePath)
logger.Info("Filesystem is g2g! %s", color.Apply(fsBasePath, color.Yellow))
return nil
}
@ -59,7 +60,11 @@ func InitFS(basePath string) error {
func CleanupFS() {
if fileCache != nil {
fileCache.Clear()
logger.Server("File cache cleared - Stats: hits=%d, misses=%d", stats.hits, stats.misses)
logger.Info(
"File cache cleared - %s hits, %s misses",
color.Apply(fmt.Sprintf("%d", stats.hits), color.Yellow),
color.Apply(fmt.Sprintf("%d", stats.misses), color.Red),
)
}
}

View File

@ -1,6 +1,7 @@
package runner
import (
"Moonshark/utils/color"
"Moonshark/utils/logger"
"context"
"errors"
@ -8,6 +9,7 @@ import (
"os"
"path/filepath"
"runtime"
"strconv"
"sync"
"sync/atomic"
"time"
@ -130,7 +132,7 @@ func NewRunner(options ...RunnerOption) (*Runner, error) {
// initializeStates creates and initializes all states in the pool
func (r *Runner) initializeStates() error {
logger.Server("Initializing %d states...", r.poolSize)
logger.Info("[LuaRunner] Creating %s states...", color.Apply(strconv.Itoa(r.poolSize), color.Yellow))
for i := range r.poolSize {
state, err := r.createState(i)
@ -302,7 +304,7 @@ func (r *Runner) RefreshStates() error {
return ErrRunnerClosed
}
logger.Server("Runner is refreshing all states...")
logger.Info("Runner is refreshing all states...")
// Drain all states from the pool
for {

View File

@ -13,6 +13,7 @@ import (
sqlite "zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
"Moonshark/utils/color"
"Moonshark/utils/logger"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
@ -48,7 +49,7 @@ func generateConnToken() string {
// InitSQLite initializes the SQLite subsystem
func InitSQLite(dir string) {
dataDir = dir
logger.Server("SQLite initialized with data directory: %s", dir)
logger.Info("SQLite is g2g! %s", color.Apply(dir, color.Yellow))
// Start connection cleanup goroutine
go cleanupIdleConnections()

View File

@ -16,7 +16,7 @@ import (
// setupTestEnv initializes test components and returns cleanup function
func setupTestEnv(b *testing.B) (*routers.LuaRouter, *runner.Runner, func()) {
// Completely silence logging during benchmarks
logger.InitGlobalLogger(logger.LevelFatal, false, false)
logger.InitGlobalLogger(false, false)
// Redirect standard logger output to discard
log.SetOutput(io.Discard)

View File

@ -13,14 +13,39 @@ const (
Gray = "\033[90m"
)
// Apply adds color to text if useColors is true
func Apply(text, color string, useColors bool) string {
var useColors = true
// SetColors enables or disables colors globally
func SetColors(enabled bool) {
useColors = enabled
}
// ColorsEnabled returns current global color setting
func ColorsEnabled() bool {
return useColors
}
// Apply adds color to text using global color setting
func Apply(text, color string) string {
if useColors {
return color + text + Reset
}
return text
}
// ApplyIf adds color to text if useColors is true (for backward compatibility)
func ApplyIf(text, color string, enabled bool) string {
if enabled {
return color + text + Reset
}
return text
}
// Set adds color to text (always applies color, ignores global setting)
func Set(text, color string) string {
return color + text + Reset
}
// Strip removes ANSI color codes from a string
func Strip(s string) string {
result := ""
@ -33,14 +58,11 @@ func Strip(s string) string {
}
continue
}
if c == '\033' {
inEscape = true
continue
}
result += string(c)
}
return result
}

View File

@ -16,7 +16,6 @@ type Config struct {
Server struct {
Port int
Debug bool
LogLevel string
HTTPLogging bool
}
@ -49,7 +48,6 @@ func New() *Config {
// Server defaults
config.Server.Port = 3117
config.Server.Debug = false
config.Server.LogLevel = "info"
config.Server.HTTPLogging = false
// Runner defaults
@ -116,9 +114,6 @@ func applyConfig(config *Config, values map[string]any) {
if v, ok := serverTable["debug"].(bool); ok {
config.Server.Debug = v
}
if v, ok := serverTable["log_level"].(string); ok {
config.Server.LogLevel = v
}
if v, ok := serverTable["http_logging"].(bool); ok {
config.Server.HTTPLogging = v
}

View File

@ -19,145 +19,117 @@ const (
colorBlue = "\033[34m"
colorPurple = "\033[35m"
colorCyan = "\033[36m"
colorWhite = "\033[37m"
colorGray = "\033[90m"
)
// Log levels
// Log types
const (
LevelDebug = iota
LevelInfo
LevelWarning
LevelError
LevelServer
LevelFatal
TypeNone = iota
TypeDebug
TypeInfo
TypeWarning
TypeError
TypeServer
TypeFatal
)
// Level names and colors
var levelProps = map[int]struct {
// Type properties
var typeProps = map[int]struct {
tag string
color string
}{
LevelDebug: {"D", colorCyan},
LevelInfo: {"I", colorBlue},
LevelWarning: {"W", colorYellow},
LevelError: {"E", colorRed},
LevelServer: {"S", colorGreen},
LevelFatal: {"F", colorPurple},
TypeDebug: {"D", colorCyan},
TypeInfo: {"I", colorBlue},
TypeWarning: {"W", colorYellow},
TypeError: {"E", colorRed},
TypeServer: {"S", colorGreen},
TypeFatal: {"F", colorPurple},
}
// Time format for log messages
const timeFormat = "15:04:05"
// Single global logger instance with mutex for safe initialization
var (
globalLogger *Logger
globalLoggerOnce sync.Once
)
// Logger handles logging operations
type Logger struct {
writer io.Writer
level int
useColors bool
timeFormat string
showTimestamp bool // Whether to show timestamp
mu sync.Mutex // Mutex for thread-safe writing
debugMode atomic.Bool // Force debug logging regardless of level
showTimestamp bool
mu sync.Mutex
debugMode atomic.Bool
}
// GetLogger returns the global logger instance, creating it if needed
func GetLogger() *Logger {
globalLoggerOnce.Do(func() {
globalLogger = newLogger(LevelInfo, true, true)
globalLogger = newLogger(true, true)
})
return globalLogger
}
// InitGlobalLogger initializes the global logger with custom settings
func InitGlobalLogger(minLevel int, useColors bool, showTimestamp bool) {
// Reset the global logger instance
globalLogger = newLogger(minLevel, useColors, showTimestamp)
func InitGlobalLogger(useColors bool, showTimestamp bool) {
globalLogger = newLogger(useColors, showTimestamp)
}
// newLogger creates a new logger instance (internal use)
func newLogger(minLevel int, useColors bool, showTimestamp bool) *Logger {
logger := &Logger{
func newLogger(useColors bool, showTimestamp bool) *Logger {
return &Logger{
writer: os.Stdout,
level: minLevel,
useColors: useColors,
timeFormat: timeFormat,
showTimestamp: showTimestamp,
}
return logger
}
// New creates a new logger (deprecated - use GetLogger() instead)
func New(minLevel int, useColors bool, showTimestamp bool) *Logger {
return newLogger(minLevel, useColors, showTimestamp)
func New(useColors bool, showTimestamp bool) *Logger {
return newLogger(useColors, showTimestamp)
}
// SetOutput changes the output destination
func (l *Logger) SetOutput(w io.Writer) {
l.mu.Lock()
defer l.mu.Unlock()
l.writer = w
l.mu.Unlock()
}
// TimeFormat returns the current time format
func (l *Logger) TimeFormat() string {
return l.timeFormat
}
// SetTimeFormat changes the time format string
func (l *Logger) SetTimeFormat(format string) {
l.mu.Lock()
defer l.mu.Unlock()
l.timeFormat = format
l.mu.Unlock()
}
// EnableTimestamp enables timestamp display
func (l *Logger) EnableTimestamp() {
l.showTimestamp = true
}
// DisableTimestamp disables timestamp display
func (l *Logger) DisableTimestamp() {
l.showTimestamp = false
}
// SetLevel changes the minimum log level
func (l *Logger) SetLevel(level int) {
l.level = level
}
// EnableColors enables ANSI color codes in the output
func (l *Logger) EnableColors() {
l.useColors = true
}
// DisableColors disables ANSI color codes in the output
func (l *Logger) DisableColors() {
l.useColors = false
}
// EnableDebug forces debug logs to be shown regardless of level
func (l *Logger) EnableDebug() {
l.debugMode.Store(true)
}
// DisableDebug stops forcing debug logs
func (l *Logger) DisableDebug() {
l.debugMode.Store(false)
}
// IsDebugEnabled returns true if debug mode is enabled
func (l *Logger) IsDebugEnabled() bool {
return l.debugMode.Load()
}
// applyColor applies color to text if colors are enabled
func (l *Logger) applyColor(text, color string) string {
if l.useColors {
return color + text + colorReset
@ -165,7 +137,6 @@ func (l *Logger) applyColor(text, color string) string {
return text
}
// stripAnsiColors removes ANSI color codes from a string
func stripAnsiColors(s string) string {
result := ""
inEscape := false
@ -177,56 +148,48 @@ func stripAnsiColors(s string) string {
}
continue
}
if c == '\033' {
inEscape = true
continue
}
result += string(c)
}
return result
}
// writeMessage writes a formatted log message directly to the writer
func (l *Logger) writeMessage(level int, message string, rawMode bool) {
var logLine string
func (l *Logger) writeMessage(logType int, message string, rawMode bool) {
if rawMode {
// Raw mode - message is already formatted, just append newline
logLine = message + "\n"
} else {
// Standard format with level tag and optional timestamp
props := levelProps[level]
if l.showTimestamp {
now := time.Now().Format(l.timeFormat)
if l.useColors {
timestamp := l.applyColor(now, colorGray)
tag := l.applyColor("["+props.tag+"]", props.color)
logLine = fmt.Sprintf("%s %s %s\n", timestamp, tag, message)
} else {
logLine = fmt.Sprintf("%s [%s] %s\n", now, props.tag, message)
}
} else {
// No timestamp, just level tag and message
if l.useColors {
tag := l.applyColor("["+props.tag+"]", props.color)
logLine = fmt.Sprintf("%s %s\n", tag, message)
} else {
logLine = fmt.Sprintf("[%s] %s\n", props.tag, message)
}
}
l.mu.Lock()
_, _ = fmt.Fprint(l.writer, message+"\n")
l.mu.Unlock()
return
}
// Synchronously write the log message
parts := []string{}
if l.showTimestamp {
timestamp := time.Now().Format(l.timeFormat)
if l.useColors {
timestamp = l.applyColor(timestamp, colorGray)
}
parts = append(parts, timestamp)
}
if logType != TypeNone {
props := typeProps[logType]
tag := "[" + props.tag + "]"
if l.useColors {
tag = l.applyColor(tag, props.color)
}
parts = append(parts, tag)
}
parts = append(parts, message)
logLine := strings.Join(parts, " ") + "\n"
l.mu.Lock()
_, _ = fmt.Fprint(l.writer, logLine)
// For fatal errors, ensure we sync immediately
if level == LevelFatal {
if logType == TypeFatal {
if f, ok := l.writer.(*os.File); ok {
_ = f.Sync()
}
@ -234,15 +197,12 @@ func (l *Logger) writeMessage(level int, message string, rawMode bool) {
l.mu.Unlock()
}
// log handles the core logging logic with level filtering
func (l *Logger) log(level int, format string, args ...any) {
// Check if we should log this message
// Either level is high enough OR (it's a debug message AND debug mode is enabled)
if level < l.level && !(level == LevelDebug && l.debugMode.Load()) {
func (l *Logger) log(logType int, format string, args ...any) {
// Only filter debug messages
if logType == TypeDebug && !l.debugMode.Load() {
return
}
// Format message
var message string
if len(args) > 0 {
message = fmt.Sprintf(format, args...)
@ -250,21 +210,14 @@ func (l *Logger) log(level int, format string, args ...any) {
message = format
}
l.writeMessage(level, message, false)
l.writeMessage(logType, message, false)
// Exit on fatal errors
if level == LevelFatal {
if logType == TypeFatal {
os.Exit(1)
}
}
// LogRaw logs a message with raw formatting, bypassing the standard format
func (l *Logger) LogRaw(format string, args ...any) {
// Use info level for filtering
if LevelInfo < l.level {
return
}
var message string
if len(args) > 0 {
message = fmt.Sprintf(format, args...)
@ -272,163 +225,128 @@ func (l *Logger) LogRaw(format string, args ...any) {
message = format
}
// Don't apply colors if disabled
if !l.useColors {
// Strip ANSI color codes if colors are disabled
message = stripAnsiColors(message)
}
l.writeMessage(LevelInfo, message, true)
l.writeMessage(TypeInfo, message, true)
}
// Debug logs a debug message
func (l *Logger) Debug(format string, args ...any) {
l.log(LevelDebug, format, args...)
l.log(TypeDebug, format, args...)
}
// Info logs an informational message
func (l *Logger) Info(format string, args ...any) {
l.log(LevelInfo, format, args...)
l.log(TypeInfo, format, args...)
}
// Warning logs a warning message
func (l *Logger) Warning(format string, args ...any) {
l.log(LevelWarning, format, args...)
l.log(TypeWarning, format, args...)
}
// Error logs an error message
func (l *Logger) Error(format string, args ...any) {
l.log(LevelError, format, args...)
l.log(TypeError, format, args...)
}
// Fatal logs a fatal error message and exits
func (l *Logger) Fatal(format string, args ...any) {
l.log(LevelFatal, format, args...)
// No need for os.Exit here as it's handled in log()
l.log(TypeFatal, format, args...)
}
// Server logs a server message
func (l *Logger) Server(format string, args ...any) {
l.log(LevelServer, format, args...)
l.log(TypeServer, format, args...)
}
// Global helper functions that use the global logger
func (l *Logger) LogRequest(statusCode int, method, path string, duration time.Duration) {
var statusColor string
// Debug logs a debug message to the global logger
switch {
case statusCode < 300:
statusColor = colorGreen
case statusCode < 400:
statusColor = colorCyan
case statusCode < 500:
statusColor = colorYellow
default:
statusColor = colorRed
}
var durationStr string
micros := duration.Microseconds()
if micros < 1000 {
durationStr = fmt.Sprintf("%.0fµs", float64(micros))
} else if micros < 1000000 {
durationStr = fmt.Sprintf("%.1fms", float64(micros)/1000)
} else {
durationStr = fmt.Sprintf("%.2fs", duration.Seconds())
}
message := fmt.Sprintf("%s %s %s %s",
l.applyColor("["+method+"]", colorGray),
l.applyColor(fmt.Sprintf("%d", statusCode), statusColor),
l.applyColor(path, colorGray),
l.applyColor(durationStr, colorGray),
)
l.writeMessage(TypeNone, message, false)
}
// Global functions
func Debug(format string, args ...any) {
GetLogger().Debug(format, args...)
}
// Info logs an informational message to the global logger
func Info(format string, args ...any) {
GetLogger().Info(format, args...)
}
// Warning logs a warning message to the global logger
func Warning(format string, args ...any) {
GetLogger().Warning(format, args...)
}
// Error logs an error message to the global logger
func Error(format string, args ...any) {
GetLogger().Error(format, args...)
}
// Fatal logs a fatal error message to the global logger and exits
func Fatal(format string, args ...any) {
GetLogger().Fatal(format, args...)
}
// Server logs a server message to the global logger
func Server(format string, args ...any) {
GetLogger().Server(format, args...)
}
// LogRaw logs a raw message to the global logger
func LogRaw(format string, args ...any) {
GetLogger().LogRaw(format, args...)
}
// SetLevel changes the minimum log level of the global logger
func SetLevel(level int) {
GetLogger().SetLevel(level)
}
// SetOutput changes the output destination of the global logger
func SetOutput(w io.Writer) {
GetLogger().SetOutput(w)
}
// TimeFormat returns the current time format of the global logger
func TimeFormat() string {
return GetLogger().TimeFormat()
}
// EnableDebug enables debug messages regardless of log level
func EnableDebug() {
GetLogger().EnableDebug()
}
// DisableDebug disables forced debug messages
func DisableDebug() {
GetLogger().DisableDebug()
}
// IsDebugEnabled returns true if debug mode is enabled
func IsDebugEnabled() bool {
return GetLogger().IsDebugEnabled()
}
// EnableTimestamp enables timestamp display
func EnableTimestamp() {
GetLogger().EnableTimestamp()
}
// DisableTimestamp disables timestamp display
func DisableTimestamp() {
GetLogger().DisableTimestamp()
}
// LogSpacer adds a horizontal line separator to the log output
func (l *Logger) LogSpacer() {
l.mu.Lock()
defer l.mu.Unlock()
// Calculate spacer width
tagWidth := 7 // Standard width of tag area "[DEBUG]"
var spacer string
if l.showTimestamp {
// Format: "15:04:05 [DEBUG] ----"
timeWidth := len(time.Now().Format(l.timeFormat))
tagSpacer := strings.Repeat("-", tagWidth)
restSpacer := strings.Repeat("-", 20) // Fixed width for the rest
if l.useColors {
timeStr := l.applyColor(strings.Repeat("-", timeWidth), colorGray)
tagStr := l.applyColor(tagSpacer, colorCyan)
spacer = fmt.Sprintf("%s %s %s\n", timeStr, tagStr, restSpacer)
} else {
spacer = fmt.Sprintf("%s %s %s\n",
strings.Repeat("-", timeWidth), tagSpacer, restSpacer)
}
} else {
// No timestamp: "[DEBUG] ----"
tagSpacer := strings.Repeat("-", tagWidth)
restSpacer := strings.Repeat("-", 20) // Fixed width for the rest
if l.useColors {
tagStr := l.applyColor(tagSpacer, colorCyan)
spacer = fmt.Sprintf("%s %s\n", tagStr, restSpacer)
} else {
spacer = fmt.Sprintf("%s %s\n", tagSpacer, restSpacer)
}
}
_, _ = fmt.Fprint(l.writer, spacer)
}
// LogSpacer adds a horizontal line separator to the global logger
func LogSpacer() {
GetLogger().LogSpacer()
func LogRequest(statusCode int, method, path string, duration time.Duration) {
GetLogger().LogRequest(statusCode, method, path, duration)
}

View File

@ -7,6 +7,7 @@ import (
"Moonshark/routers"
"Moonshark/runner"
"Moonshark/utils/color"
"Moonshark/utils/logger"
)
@ -47,7 +48,7 @@ func WatchLuaRouter(router *routers.LuaRouter, runner *runner.Runner, routesDir
return nil, fmt.Errorf("failed to watch directory: %w", err)
}
logger.Info("Started watching Lua routes directory: %s", routesDir)
logger.Info("Started watching Lua routes! %s", color.Apply(routesDir, color.Yellow))
return watcher, nil
}
@ -76,7 +77,7 @@ func WatchLuaModules(luaRunner *runner.Runner, libDirs []string) ([]*DirectoryWa
}
watchers = append(watchers, watcher)
logger.Info("Started watching Lua modules directory: %s", dir)
logger.Info("Started watching Lua modules! %s", color.Apply(dir, color.Yellow))
}
return watchers, nil