big ol logger refactor
This commit is contained in:
parent
d1ec8ade9c
commit
bf5a841716
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
137
main.go
@ -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))
|
||||
}
|
||||
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user