rewrite logger and main
This commit is contained in:
parent
ac646bbad8
commit
c0c6100c17
@ -121,7 +121,7 @@ func New(luaRouter *router.LuaRouter, runner *runner.Runner, cfg *config.Config,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ListenAndServe(addr string) error {
|
func (s *Server) ListenAndServe(addr string) error {
|
||||||
logger.Info("Catch the swell at %s", color.Cyan("http://localhost"+addr))
|
logger.Infof("Catch the swell at %s", color.Cyan("http://localhost"+addr))
|
||||||
return s.fasthttpServer.ListenAndServe(addr)
|
return s.fasthttpServer.ListenAndServe(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
if s.debugMode && bytes.Equal(pathBytes, debugPath) {
|
if s.debugMode && bytes.Equal(pathBytes, debugPath) {
|
||||||
s.handleDebugStats(ctx)
|
s.handleDebugStats(ctx)
|
||||||
if s.cfg.Server.HTTPLogging {
|
if s.cfg.Server.HTTPLogging {
|
||||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
logger.Request(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
if s.staticHandler != nil && bytes.HasPrefix(pathBytes, s.staticPrefixBytes) {
|
if s.staticHandler != nil && bytes.HasPrefix(pathBytes, s.staticPrefixBytes) {
|
||||||
s.staticHandler(ctx)
|
s.staticHandler(ctx)
|
||||||
if s.cfg.Server.HTTPLogging {
|
if s.cfg.Server.HTTPLogging {
|
||||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
logger.Request(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.cfg.Server.HTTPLogging {
|
if s.cfg.Server.HTTPLogging {
|
||||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
logger.Request(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
|||||||
luaCtx.Set("form", formData)
|
luaCtx.Set("form", formData)
|
||||||
} else {
|
} else {
|
||||||
if s.debugMode {
|
if s.debugMode {
|
||||||
logger.Warning("Error parsing form: %v", err)
|
logger.Warnf("Error parsing form: %v", err)
|
||||||
}
|
}
|
||||||
luaCtx.Set("form", emptyMap)
|
luaCtx.Set("form", emptyMap)
|
||||||
}
|
}
|
||||||
@ -243,7 +243,7 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
|||||||
|
|
||||||
response, err := s.luaRunner.Run(bytecode, luaCtx, scriptPath)
|
response, err := s.luaRunner.Run(bytecode, luaCtx, scriptPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Lua execution error: %v", err)
|
logger.Errorf("Lua execution error: %v", err)
|
||||||
s.sendError(ctx, fasthttp.StatusInternalServerError, pathBytes, err)
|
s.sendError(ctx, fasthttp.StatusInternalServerError, pathBytes, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
400
main.go
400
main.go
@ -1,400 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"Moonshark/http"
|
|
||||||
"Moonshark/router"
|
|
||||||
"Moonshark/runner"
|
|
||||||
"Moonshark/sessions"
|
|
||||||
"Moonshark/utils/color"
|
|
||||||
"Moonshark/utils/config"
|
|
||||||
"Moonshark/utils/logger"
|
|
||||||
"Moonshark/utils/metadata"
|
|
||||||
"Moonshark/watchers"
|
|
||||||
|
|
||||||
fin "git.sharkk.net/Sharkk/Fin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Moonshark represents the server and all its dependencies
|
|
||||||
type Moonshark struct {
|
|
||||||
Config *config.Config
|
|
||||||
LuaRouter *router.LuaRouter
|
|
||||||
LuaRunner *runner.Runner
|
|
||||||
HTTPServer *http.Server
|
|
||||||
cleanupFuncs []func() error
|
|
||||||
scriptMode bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
configPath := flag.String("config", "config", "Path to configuration file")
|
|
||||||
debugFlag := flag.Bool("debug", false, "Enable debug mode")
|
|
||||||
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
|
|
||||||
flag.Parse()
|
|
||||||
scriptMode := *scriptPath != ""
|
|
||||||
|
|
||||||
color.SetColors(color.DetectShellColors())
|
|
||||||
banner(scriptMode)
|
|
||||||
cfg := config.New(readConfig(*configPath))
|
|
||||||
|
|
||||||
if *debugFlag || cfg.Server.Debug {
|
|
||||||
logger.EnableDebug()
|
|
||||||
logger.Debug("Debug logging enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
var moonshark *Moonshark
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if scriptMode {
|
|
||||||
moonshark, err = initScriptMode(cfg)
|
|
||||||
} else {
|
|
||||||
moonshark, err = initServerMode(cfg, *debugFlag || cfg.Server.Debug)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Initialization failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := moonshark.Shutdown(); err != nil {
|
|
||||||
logger.Error("Error during shutdown: %v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if scriptMode {
|
|
||||||
// Run the script and exit
|
|
||||||
if err := moonshark.RunScript(*scriptPath); err != nil {
|
|
||||||
logger.Fatal("Script execution failed: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the server
|
|
||||||
if err := moonshark.Start(); err != nil {
|
|
||||||
logger.Fatal("Failed to start server: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for shutdown signal
|
|
||||||
stop := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
|
||||||
<-stop
|
|
||||||
|
|
||||||
fmt.Print("\n")
|
|
||||||
logger.Info("Shutdown signal received")
|
|
||||||
}
|
|
||||||
|
|
||||||
// initScriptMode initializes minimal components needed for script execution
|
|
||||||
func initScriptMode(cfg *config.Config) (*Moonshark, error) {
|
|
||||||
moonshark := &Moonshark{
|
|
||||||
Config: cfg,
|
|
||||||
scriptMode: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only initialize the Lua runner with required paths
|
|
||||||
runnerOpts := []runner.RunnerOption{
|
|
||||||
runner.WithPoolSize(1), // Only need one state for script mode
|
|
||||||
runner.WithLibDirs(cfg.Dirs.Libs...),
|
|
||||||
runner.WithFsDir(cfg.Dirs.FS),
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debug("Script mode initialized with minimized components")
|
|
||||||
return moonshark, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// initServerMode initializes all components needed for server operation
|
|
||||||
func initServerMode(cfg *config.Config, debug bool) (*Moonshark, error) {
|
|
||||||
moonshark := &Moonshark{
|
|
||||||
Config: cfg,
|
|
||||||
scriptMode: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug {
|
|
||||||
cfg.Server.Debug = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := initLuaRouter(moonshark); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := initRunner(moonshark); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := setupWatchers(moonshark); err != nil {
|
|
||||||
logger.Warning("Watcher setup failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get static directory - empty string if it doesn't exist
|
|
||||||
staticDir := ""
|
|
||||||
if dirExists(cfg.Dirs.Static) {
|
|
||||||
staticDir = cfg.Dirs.Static
|
|
||||||
logger.Info("Static files enabled: %s", color.Yellow(staticDir))
|
|
||||||
} else {
|
|
||||||
logger.Warning("Static directory not found: %s", color.Yellow(cfg.Dirs.Static))
|
|
||||||
}
|
|
||||||
|
|
||||||
moonshark.HTTPServer = http.New(
|
|
||||||
moonshark.LuaRouter,
|
|
||||||
moonshark.LuaRunner,
|
|
||||||
cfg,
|
|
||||||
debug,
|
|
||||||
)
|
|
||||||
|
|
||||||
// For development, disable caching. For production, enable it
|
|
||||||
if cfg.Server.Debug {
|
|
||||||
moonshark.HTTPServer.SetStaticCaching(0) // No caching in debug mode
|
|
||||||
} else {
|
|
||||||
moonshark.HTTPServer.SetStaticCaching(1 * time.Hour) // Cache for 1 hour in production
|
|
||||||
}
|
|
||||||
|
|
||||||
return moonshark, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunScript executes a Lua script in the sandbox environment
|
|
||||||
func (s *Moonshark) RunScript(scriptPath string) error {
|
|
||||||
scriptPath, err := filepath.Abs(scriptPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to resolve script path: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("script file not found: %s", scriptPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info("Executing: %s", scriptPath)
|
|
||||||
|
|
||||||
resp, err := s.LuaRunner.RunScriptFile(scriptPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("execution failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp != nil && resp.Body != nil {
|
|
||||||
logger.Info("Script result: %v", resp.Body)
|
|
||||||
} else {
|
|
||||||
logger.Info("Script executed successfully (no return value)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start starts the HTTP server
|
|
||||||
func (s *Moonshark) Start() error {
|
|
||||||
if s.scriptMode {
|
|
||||||
return errors.New("cannot start server in script mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info("Surf's up on port %s!", color.Cyan(strconv.Itoa(s.Config.Server.Port)))
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil {
|
|
||||||
if err.Error() != "http: Server closed" {
|
|
||||||
logger.Error("Server error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown gracefully shuts down Moonshark
|
|
||||||
func (s *Moonshark) Shutdown() error {
|
|
||||||
logger.Info("Shutting down...")
|
|
||||||
|
|
||||||
if !s.scriptMode && s.HTTPServer != nil {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := s.HTTPServer.Shutdown(ctx); err != nil {
|
|
||||||
logger.Error("HTTP server shutdown error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cleanup := range s.cleanupFuncs {
|
|
||||||
if err := cleanup(); err != nil {
|
|
||||||
logger.Warning("Cleanup error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.LuaRunner != nil {
|
|
||||||
s.LuaRunner.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runner.CleanupEnv(); err != nil {
|
|
||||||
logger.Warning("Environment cleanup failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info("Shutdown complete")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dirExists(path string) bool {
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return info.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initLuaRouter(s *Moonshark) error {
|
|
||||||
if !dirExists(s.Config.Dirs.Routes) {
|
|
||||||
return fmt.Errorf("routes directory doesn't exist: %s", s.Config.Dirs.Routes)
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
s.LuaRouter, err = router.NewLuaRouter(s.Config.Dirs.Routes)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, router.ErrRoutesCompilationErrors) {
|
|
||||||
// Non-fatal, some routes failed
|
|
||||||
logger.Warning("Some routes failed to compile")
|
|
||||||
|
|
||||||
if failedRoutes := s.LuaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 {
|
|
||||||
for _, re := range failedRoutes {
|
|
||||||
logger.Error("Route %s %s: %v", re.Method, re.Path, re.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("lua router init failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Info("LuaRouter is g2g! %s", color.Yellow(s.Config.Dirs.Routes))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initRunner(s *Moonshark) error {
|
|
||||||
if !dirExists(s.Config.Dirs.Override) {
|
|
||||||
logger.Warning("Override directory not found... %s", color.Yellow(s.Config.Dirs.Override))
|
|
||||||
s.Config.Dirs.Override = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dir := range s.Config.Dirs.Libs {
|
|
||||||
if !dirExists(dir) {
|
|
||||||
logger.Warning("Lib directory not found... %s", color.Yellow(dir))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize environment manager
|
|
||||||
if err := runner.InitEnv(s.Config.Dirs.Data); err != nil {
|
|
||||||
logger.Warning("Environment initialization failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionManager := sessions.GlobalSessionManager
|
|
||||||
sessionManager.SetCookieOptions(
|
|
||||||
"MoonsharkSID",
|
|
||||||
"/",
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
86400,
|
|
||||||
)
|
|
||||||
|
|
||||||
poolSize := s.Config.Runner.PoolSize
|
|
||||||
if s.scriptMode {
|
|
||||||
poolSize = 1 // Only need one state for script mode
|
|
||||||
}
|
|
||||||
|
|
||||||
runnerOpts := []runner.RunnerOption{
|
|
||||||
runner.WithPoolSize(poolSize),
|
|
||||||
runner.WithLibDirs(s.Config.Dirs.Libs...),
|
|
||||||
runner.WithFsDir(s.Config.Dirs.FS),
|
|
||||||
runner.WithDataDir(s.Config.Dirs.Data),
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
s.LuaRunner, err = runner.NewRunner(runnerOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("lua runner init failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info("LuaRunner is g2g with %s states!", color.Yellow(strconv.Itoa(poolSize)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupWatchers(s *Moonshark) error {
|
|
||||||
manager := watchers.GetWatcherManager()
|
|
||||||
|
|
||||||
// Watch routes directory
|
|
||||||
routeWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.Dirs.Routes)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warning("Routes directory watch failed: %v", err)
|
|
||||||
} else {
|
|
||||||
routesDir := routeWatcher.GetDir()
|
|
||||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
|
||||||
return manager.UnwatchDirectory(routesDir)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch module directories
|
|
||||||
moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.Dirs.Libs)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warning("Module directories watch failed: %v", err)
|
|
||||||
} else {
|
|
||||||
for _, watcher := range moduleWatchers {
|
|
||||||
dirPath := watcher.GetDir()
|
|
||||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
|
||||||
return manager.UnwatchDirectory(dirPath)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
plural := ""
|
|
||||||
if len(moduleWatchers) == 1 {
|
|
||||||
plural = "directory"
|
|
||||||
} else {
|
|
||||||
plural = "directories"
|
|
||||||
}
|
|
||||||
logger.Info("Watching %s module %s.", color.Yellow(strconv.Itoa(len(moduleWatchers))), plural)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func banner(scriptMode bool) {
|
|
||||||
if scriptMode {
|
|
||||||
fmt.Println(color.Blue(fmt.Sprintf("Moonshark %s << Script Mode >>", metadata.Version)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
banner := `
|
|
||||||
_____ _________.__ __
|
|
||||||
/ \ ____ ____ ____ / _____/| |__ _____ _______| | __
|
|
||||||
/ \ / \ / _ \ / _ \ / \ \_____ \ | | \\__ \\_ __ \ |/ /
|
|
||||||
/ Y ( <_> | <_> ) | \/ \| Y \/ __ \| | \/ <
|
|
||||||
\____|__ /\____/ \____/|___| /_______ /|___| (____ /__| |__|_ \ %s
|
|
||||||
\/ \/ \/ \/ \/ \/
|
|
||||||
`
|
|
||||||
fmt.Println(color.Blue(fmt.Sprintf(banner, metadata.Version)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readConfig attempts to read config data from a Fin file
|
|
||||||
func readConfig(path string) *fin.Data {
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Failed to open config file %s", color.Yellow(path))
|
|
||||||
cfg, _ := fin.Load(nil)
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
cfg, err := fin.Load(file)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warning("Config load failed: %v, using defaults", color.Red(err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
}
|
|
334
moonshark.go
Normal file
334
moonshark.go
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"Moonshark/http"
|
||||||
|
"Moonshark/router"
|
||||||
|
"Moonshark/runner"
|
||||||
|
"Moonshark/sessions"
|
||||||
|
"Moonshark/utils/color"
|
||||||
|
"Moonshark/utils/config"
|
||||||
|
"Moonshark/utils/logger"
|
||||||
|
"Moonshark/utils/metadata"
|
||||||
|
"Moonshark/watchers"
|
||||||
|
|
||||||
|
fin "git.sharkk.net/Sharkk/Fin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Moonshark struct {
|
||||||
|
Config *config.Config
|
||||||
|
LuaRouter *router.LuaRouter
|
||||||
|
LuaRunner *runner.Runner
|
||||||
|
HTTPServer *http.Server
|
||||||
|
cleanupFuncs []func() error
|
||||||
|
scriptMode bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
configPath := flag.String("config", "config", "Path to configuration file")
|
||||||
|
debugFlag := flag.Bool("debug", false, "Enable debug mode")
|
||||||
|
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
scriptMode := *scriptPath != ""
|
||||||
|
color.SetColors(color.DetectShellColors())
|
||||||
|
banner(scriptMode)
|
||||||
|
|
||||||
|
cfg := config.New(readConfig(*configPath))
|
||||||
|
debug := *debugFlag || cfg.Server.Debug
|
||||||
|
logger.Debug(debug)
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
logger.Debugf("Debug logging enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
moonshark, err := newMoonshark(cfg, debug, scriptMode)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("Initialization failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := moonshark.Shutdown(); err != nil {
|
||||||
|
logger.Errorf("Error during shutdown: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if scriptMode {
|
||||||
|
if err := moonshark.RunScript(*scriptPath); err != nil {
|
||||||
|
logger.Fatalf("Script execution failed: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := moonshark.Start(); err != nil {
|
||||||
|
logger.Fatalf("Failed to start server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stop := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||||
|
<-stop
|
||||||
|
|
||||||
|
fmt.Print("\n")
|
||||||
|
logger.Infof("Shutdown signal received")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMoonshark(cfg *config.Config, debug, scriptMode bool) (*Moonshark, error) {
|
||||||
|
s := &Moonshark{Config: cfg, scriptMode: scriptMode}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
cfg.Server.Debug = true
|
||||||
|
}
|
||||||
|
|
||||||
|
poolSize := cfg.Runner.PoolSize
|
||||||
|
if scriptMode {
|
||||||
|
poolSize = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize runner first (needed for both modes)
|
||||||
|
if err := s.initRunner(poolSize); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if scriptMode {
|
||||||
|
logger.Debugf("Script mode initialized")
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server mode: initialize router, watchers, and HTTP server
|
||||||
|
if err := s.initRouter(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.setupWatchers()
|
||||||
|
|
||||||
|
s.HTTPServer = http.New(s.LuaRouter, s.LuaRunner, cfg, debug)
|
||||||
|
|
||||||
|
// Set caching based on debug mode
|
||||||
|
if cfg.Server.Debug {
|
||||||
|
s.HTTPServer.SetStaticCaching(0)
|
||||||
|
} else {
|
||||||
|
s.HTTPServer.SetStaticCaching(time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log static directory status
|
||||||
|
if dirExists(cfg.Dirs.Static) {
|
||||||
|
logger.Infof("Static files enabled: %s", color.Yellow(cfg.Dirs.Static))
|
||||||
|
} else {
|
||||||
|
logger.Warnf("Static directory not found: %s", color.Yellow(cfg.Dirs.Static))
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Moonshark) initRunner(poolSize int) error {
|
||||||
|
// Warn about missing directories but continue
|
||||||
|
if !dirExists(s.Config.Dirs.Override) {
|
||||||
|
logger.Warnf("Override directory not found... %s", color.Yellow(s.Config.Dirs.Override))
|
||||||
|
s.Config.Dirs.Override = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir := range s.Config.Dirs.Libs {
|
||||||
|
if !dirExists(dir) {
|
||||||
|
logger.Warnf("Lib directory not found... %s", color.Yellow(dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := runner.InitEnv(s.Config.Dirs.Data); err != nil {
|
||||||
|
logger.Warnf("Environment initialization failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sessions.GlobalSessionManager.SetCookieOptions("MoonsharkSID", "/", "", false, true, 86400)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
s.LuaRunner, err = runner.NewRunner(
|
||||||
|
runner.WithPoolSize(poolSize),
|
||||||
|
runner.WithLibDirs(s.Config.Dirs.Libs...),
|
||||||
|
runner.WithFsDir(s.Config.Dirs.FS),
|
||||||
|
runner.WithDataDir(s.Config.Dirs.Data),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("lua runner init failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("LuaRunner is g2g with %s states!", color.Yellow(strconv.Itoa(poolSize)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Moonshark) initRouter() error {
|
||||||
|
if err := os.MkdirAll(s.Config.Dirs.Routes, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create routes directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
s.LuaRouter, err = router.NewLuaRouter(s.Config.Dirs.Routes)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, router.ErrRoutesCompilationErrors) {
|
||||||
|
logger.Warnf("Some routes failed to compile")
|
||||||
|
if failedRoutes := s.LuaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 {
|
||||||
|
for _, re := range failedRoutes {
|
||||||
|
logger.Errorf("Route %s %s: %v", re.Method, re.Path, re.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("lua router init failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("LuaRouter is g2g! %s", color.Yellow(s.Config.Dirs.Routes))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Moonshark) setupWatchers() {
|
||||||
|
manager := watchers.GetWatcherManager()
|
||||||
|
|
||||||
|
// Watch routes
|
||||||
|
if routeWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.Dirs.Routes); err != nil {
|
||||||
|
logger.Warnf("Routes directory watch failed: %v", err)
|
||||||
|
} else {
|
||||||
|
routesDir := routeWatcher.GetDir()
|
||||||
|
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
||||||
|
return manager.UnwatchDirectory(routesDir)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch modules
|
||||||
|
if moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.Dirs.Libs); err != nil {
|
||||||
|
logger.Warnf("Module directories watch failed: %v", err)
|
||||||
|
} else {
|
||||||
|
for _, watcher := range moduleWatchers {
|
||||||
|
dirPath := watcher.GetDir()
|
||||||
|
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
||||||
|
return manager.UnwatchDirectory(dirPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
plural := "directory"
|
||||||
|
if len(moduleWatchers) != 1 {
|
||||||
|
plural = "directories"
|
||||||
|
}
|
||||||
|
logger.Infof("Watching %s module %s.", color.Yellow(strconv.Itoa(len(moduleWatchers))), plural)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Moonshark) RunScript(scriptPath string) error {
|
||||||
|
scriptPath, err := filepath.Abs(scriptPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve script path: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("script file not found: %s", scriptPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("Executing: %s", scriptPath)
|
||||||
|
|
||||||
|
resp, err := s.LuaRunner.RunScriptFile(scriptPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("execution failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != nil && resp.Body != nil {
|
||||||
|
logger.Infof("Script result: %v", resp.Body)
|
||||||
|
} else {
|
||||||
|
logger.Infof("Script executed successfully (no return value)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Moonshark) Start() error {
|
||||||
|
if s.scriptMode {
|
||||||
|
return errors.New("cannot start server in script mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("Surf's up on port %s!", color.Cyan(strconv.Itoa(s.Config.Server.Port)))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil {
|
||||||
|
if err.Error() != "http: Server closed" {
|
||||||
|
logger.Errorf("Server error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Moonshark) Shutdown() error {
|
||||||
|
logger.Infof("Shutting down...")
|
||||||
|
|
||||||
|
if !s.scriptMode && s.HTTPServer != nil {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := s.HTTPServer.Shutdown(ctx); err != nil {
|
||||||
|
logger.Errorf("HTTP server shutdown error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cleanup := range s.cleanupFuncs {
|
||||||
|
if err := cleanup(); err != nil {
|
||||||
|
logger.Warnf("Cleanup error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.LuaRunner != nil {
|
||||||
|
s.LuaRunner.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := runner.CleanupEnv(); err != nil {
|
||||||
|
logger.Warnf("Environment cleanup failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("Shutdown complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirExists(path string) bool {
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
return err == nil && info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
func banner(scriptMode bool) {
|
||||||
|
if scriptMode {
|
||||||
|
fmt.Println(color.Blue(fmt.Sprintf("Moonshark %s << Script Mode >>", metadata.Version)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
banner := `
|
||||||
|
_____ _________.__ __
|
||||||
|
/ \ ____ ____ ____ / _____/| |__ _____ _______| | __
|
||||||
|
/ \ / \ / _ \ / _ \ / \ \_____ \ | | \\__ \\_ __ \ |/ /
|
||||||
|
/ Y ( <_> | <_> ) | \/ \| Y \/ __ \| | \/ <
|
||||||
|
\____|__ /\____/ \____/|___| /_______ /|___| (____ /__| |__|_ \ %s
|
||||||
|
\/ \/ \/ \/ \/ \/
|
||||||
|
`
|
||||||
|
fmt.Println(color.Blue(fmt.Sprintf(banner, metadata.Version)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfig(path string) *fin.Data {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Failed to open config file %s, using defaults", color.Yellow(path))
|
||||||
|
return fin.NewData()
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
cfg, err := fin.Load(file)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Failed to load config file %s, using defaults", color.Yellow(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
@ -73,7 +73,7 @@ func precompileModule(m *ModuleInfo) {
|
|||||||
m.Once.Do(func() {
|
m.Once.Do(func() {
|
||||||
tempState := luajit.New()
|
tempState := luajit.New()
|
||||||
if tempState == nil {
|
if tempState == nil {
|
||||||
logger.Fatal("Failed to create temp Lua state for %s module compilation", m.Name)
|
logger.Fatalf("Failed to create temp Lua state for %s module compilation", m.Name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tempState.Close()
|
defer tempState.Close()
|
||||||
@ -81,7 +81,7 @@ func precompileModule(m *ModuleInfo) {
|
|||||||
|
|
||||||
code, err := tempState.CompileBytecode(m.Code, m.Name+".lua")
|
code, err := tempState.CompileBytecode(m.Code, m.Name+".lua")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to compile %s module: %v", m.Name, err)
|
logger.Errorf("Failed to compile %s module: %v", m.Name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ func precompileModule(m *ModuleInfo) {
|
|||||||
copy(bytecode, code)
|
copy(bytecode, code)
|
||||||
m.Bytecode.Store(&bytecode)
|
m.Bytecode.Store(&bytecode)
|
||||||
|
|
||||||
logger.Debug("Successfully precompiled %s.lua to bytecode (%d bytes)", m.Name, len(code))
|
logger.Debugf("Successfully precompiled %s.lua to bytecode (%d bytes)", m.Name, len(code))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ func loadModule(state *luajit.State, m *ModuleInfo, verbose bool) error {
|
|||||||
bytecode := m.Bytecode.Load()
|
bytecode := m.Bytecode.Load()
|
||||||
if bytecode != nil && len(*bytecode) > 0 {
|
if bytecode != nil && len(*bytecode) > 0 {
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Debug("Loading %s.lua from precompiled bytecode", m.Name)
|
logger.Debugf("Loading %s.lua from precompiled bytecode", m.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := state.LoadBytecode(*bytecode, m.Name+".lua"); err != nil {
|
if err := state.LoadBytecode(*bytecode, m.Name+".lua"); err != nil {
|
||||||
@ -124,7 +124,7 @@ func loadModule(state *luajit.State, m *ModuleInfo, verbose bool) error {
|
|||||||
} else {
|
} else {
|
||||||
// Fallback to interpreting the source
|
// Fallback to interpreting the source
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Warning("Using non-precompiled %s.lua", m.Name)
|
logger.Warnf("Using non-precompiled %s.lua", m.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := state.DoString(m.Code); err != nil {
|
if err := state.DoString(m.Code); err != nil {
|
||||||
@ -154,13 +154,13 @@ func loadSandboxIntoState(state *luajit.State, verbose bool) error {
|
|||||||
bytecode := sandbox.Bytecode.Load()
|
bytecode := sandbox.Bytecode.Load()
|
||||||
if bytecode != nil && len(*bytecode) > 0 {
|
if bytecode != nil && len(*bytecode) > 0 {
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Debug("Loading sandbox.lua from precompiled bytecode")
|
logger.Debugf("Loading sandbox.lua from precompiled bytecode")
|
||||||
}
|
}
|
||||||
return state.LoadAndRunBytecode(*bytecode, "sandbox.lua")
|
return state.LoadAndRunBytecode(*bytecode, "sandbox.lua")
|
||||||
}
|
}
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Warning("Using non-precompiled sandbox.lua")
|
logger.Warnf("Using non-precompiled sandbox.lua")
|
||||||
}
|
}
|
||||||
return state.DoString(sandboxLuaCode)
|
return state.DoString(sandboxLuaCode)
|
||||||
}
|
}
|
||||||
|
@ -45,16 +45,16 @@ func InitEnv(dataDir string) error {
|
|||||||
|
|
||||||
// Load existing .env file if it exists
|
// Load existing .env file if it exists
|
||||||
if err := globalEnvManager.load(); err != nil {
|
if err := globalEnvManager.load(); err != nil {
|
||||||
logger.Warning("Failed to load .env file: %v", err)
|
logger.Warnf("Failed to load .env file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
count := len(globalEnvManager.vars)
|
count := len(globalEnvManager.vars)
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
logger.Info("Environment loaded: %s vars from %s",
|
logger.Infof("Environment loaded: %s vars from %s",
|
||||||
color.Yellow(fmt.Sprintf("%d", count)),
|
color.Yellow(fmt.Sprintf("%d", count)),
|
||||||
color.Yellow(envPath))
|
color.Yellow(envPath))
|
||||||
} else {
|
} else {
|
||||||
logger.Info("Environment initialized: %s", color.Yellow(envPath))
|
logger.Infof("Environment initialized: %s", color.Yellow(envPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -95,7 +95,7 @@ func (e *EnvManager) load() error {
|
|||||||
// Parse key=value
|
// Parse key=value
|
||||||
parts := strings.SplitN(line, "=", 2)
|
parts := strings.SplitN(line, "=", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
logger.Warning("Invalid .env line %d: %s", lineNum, line)
|
logger.Warnf("Invalid .env line %d: %s", lineNum, line)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ func (e *EnvManager) Save() error {
|
|||||||
fmt.Fprintf(file, "%s=%s\n", key, strValue)
|
fmt.Fprintf(file, "%s=%s\n", key, strValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug("Environment saved: %d vars to %s", len(e.vars), e.envPath)
|
logger.Debugf("Environment saved: %d vars to %s", len(e.vars), e.envPath)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ func InitFS(basePath string) error {
|
|||||||
// Initialize file cache with 2000 entries (reasonable for most use cases)
|
// Initialize file cache with 2000 entries (reasonable for most use cases)
|
||||||
fileCache = lru.NewLRUCache(2000)
|
fileCache = lru.NewLRUCache(2000)
|
||||||
|
|
||||||
logger.Info("Filesystem is g2g! %s", color.Yellow(fsBasePath))
|
logger.Infof("Filesystem is g2g! %s", color.Yellow(fsBasePath))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ func InitFS(basePath string) error {
|
|||||||
func CleanupFS() {
|
func CleanupFS() {
|
||||||
if fileCache != nil {
|
if fileCache != nil {
|
||||||
fileCache.Clear()
|
fileCache.Clear()
|
||||||
logger.Info(
|
logger.Infof(
|
||||||
"File cache cleared - %s hits, %s misses",
|
"File cache cleared - %s hits, %s misses",
|
||||||
color.Yellow(fmt.Sprintf("%d", stats.hits)),
|
color.Yellow(fmt.Sprintf("%d", stats.hits)),
|
||||||
color.Red(fmt.Sprintf("%d", stats.misses)),
|
color.Red(fmt.Sprintf("%d", stats.misses)),
|
||||||
|
@ -315,7 +315,7 @@ func generateToken(state *luajit.State) int {
|
|||||||
// Generate secure random bytes
|
// Generate secure random bytes
|
||||||
tokenBytes := make([]byte, length)
|
tokenBytes := make([]byte, length)
|
||||||
if _, err := rand.Read(tokenBytes); err != nil {
|
if _, err := rand.Read(tokenBytes); err != nil {
|
||||||
logger.Error("Failed to generate secure token: %v", err)
|
logger.Errorf("Failed to generate secure token: %v", err)
|
||||||
state.PushString("")
|
state.PushString("")
|
||||||
return 1 // Return empty string on error
|
return 1 // Return empty string on error
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ func (l *ModuleLoader) SetScriptDir(dir string) {
|
|||||||
// debugLog logs a message if debug mode is enabled
|
// debugLog logs a message if debug mode is enabled
|
||||||
func (l *ModuleLoader) debugLog(format string, args ...interface{}) {
|
func (l *ModuleLoader) debugLog(format string, args ...interface{}) {
|
||||||
if l.debug {
|
if l.debug {
|
||||||
logger.Debug("ModuleLoader "+format, args...)
|
logger.Debugf("ModuleLoader "+format, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func NewRunner(options ...RunnerOption) (*Runner, error) {
|
|||||||
|
|
||||||
// initializeStates creates and initializes all states in the pool
|
// initializeStates creates and initializes all states in the pool
|
||||||
func (r *Runner) initializeStates() error {
|
func (r *Runner) initializeStates() error {
|
||||||
logger.Info("[LuaRunner] Creating %s states...", color.Yellow(strconv.Itoa(r.poolSize)))
|
logger.Infof("[LuaRunner] Creating %s states...", color.Yellow(strconv.Itoa(r.poolSize)))
|
||||||
|
|
||||||
for i := range r.poolSize {
|
for i := range r.poolSize {
|
||||||
state, err := r.createState(i)
|
state, err := r.createState(i)
|
||||||
@ -153,7 +153,7 @@ func (r *Runner) initializeStates() error {
|
|||||||
func (r *Runner) createState(index int) (*State, error) {
|
func (r *Runner) createState(index int) (*State, error) {
|
||||||
verbose := index == 0
|
verbose := index == 0
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Debug("Creating Lua state %d", index)
|
logger.Debugf("Creating Lua state %d", index)
|
||||||
}
|
}
|
||||||
|
|
||||||
L := luajit.New()
|
L := luajit.New()
|
||||||
@ -185,7 +185,7 @@ func (r *Runner) createState(index int) (*State, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Debug("Lua state %d initialized successfully", index)
|
logger.Debugf("Lua state %d initialized successfully", index)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &State{
|
return &State{
|
||||||
@ -291,7 +291,7 @@ waitForInUse:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(timeout) {
|
if time.Now().After(timeout) {
|
||||||
logger.Warning("Timeout waiting for states to finish during shutdown, forcing close")
|
logger.Warnf("Timeout waiting for states to finish during shutdown, forcing close")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ waitForInUse:
|
|||||||
for i, state := range r.states {
|
for i, state := range r.states {
|
||||||
if state != nil {
|
if state != nil {
|
||||||
if state.inUse.Load() {
|
if state.inUse.Load() {
|
||||||
logger.Warning("Force closing state %d that is still in use", i)
|
logger.Warnf("Force closing state %d that is still in use", i)
|
||||||
}
|
}
|
||||||
state.L.Cleanup()
|
state.L.Cleanup()
|
||||||
state.L.Close()
|
state.L.Close()
|
||||||
@ -313,7 +313,7 @@ waitForInUse:
|
|||||||
CleanupFS()
|
CleanupFS()
|
||||||
CleanupSQLite()
|
CleanupSQLite()
|
||||||
|
|
||||||
logger.Debug("Runner closed")
|
logger.Debugf("Runner closed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ func (r *Runner) RefreshStates() error {
|
|||||||
return ErrRunnerClosed
|
return ErrRunnerClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Runner is refreshing all states...")
|
logger.Infof("Runner is refreshing all states...")
|
||||||
|
|
||||||
// Drain all states from the pool
|
// Drain all states from the pool
|
||||||
for {
|
for {
|
||||||
@ -354,7 +354,7 @@ waitForInUse:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(timeout) {
|
if time.Now().After(timeout) {
|
||||||
logger.Warning("Timeout waiting for states to finish, forcing refresh")
|
logger.Warnf("Timeout waiting for states to finish, forcing refresh")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +365,7 @@ waitForInUse:
|
|||||||
for i, state := range r.states {
|
for i, state := range r.states {
|
||||||
if state != nil {
|
if state != nil {
|
||||||
if state.inUse.Load() {
|
if state.inUse.Load() {
|
||||||
logger.Warning("Force closing state %d that is still in use", i)
|
logger.Warnf("Force closing state %d that is still in use", i)
|
||||||
}
|
}
|
||||||
state.L.Cleanup()
|
state.L.Cleanup()
|
||||||
state.L.Close()
|
state.L.Close()
|
||||||
@ -378,21 +378,21 @@ waitForInUse:
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug("All states refreshed successfully")
|
logger.Debugf("All states refreshed successfully")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyFileChanged alerts the runner about file changes
|
// NotifyFileChanged alerts the runner about file changes
|
||||||
func (r *Runner) NotifyFileChanged(filePath string) bool {
|
func (r *Runner) NotifyFileChanged(filePath string) bool {
|
||||||
logger.Debug("Runner notified of file change: %s", filePath)
|
logger.Debugf("Runner notified of file change: %s", filePath)
|
||||||
|
|
||||||
module, isModule := r.moduleLoader.GetModuleByPath(filePath)
|
module, isModule := r.moduleLoader.GetModuleByPath(filePath)
|
||||||
if isModule {
|
if isModule {
|
||||||
logger.Debug("Refreshing module: %s", module)
|
logger.Debugf("Refreshing module: %s", module)
|
||||||
return r.RefreshModule(module)
|
return r.RefreshModule(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug("File change noted but no refresh needed: %s", filePath)
|
logger.Debugf("File change noted but no refresh needed: %s", filePath)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ func (r *Runner) RefreshModule(moduleName string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug("Refreshing module: %s", moduleName)
|
logger.Debugf("Refreshing module: %s", moduleName)
|
||||||
|
|
||||||
success := true
|
success := true
|
||||||
for _, state := range r.states {
|
for _, state := range r.states {
|
||||||
@ -416,12 +416,12 @@ func (r *Runner) RefreshModule(moduleName string) bool {
|
|||||||
// Use the enhanced module loader refresh
|
// Use the enhanced module loader refresh
|
||||||
if err := r.moduleLoader.RefreshModule(state.L, moduleName); err != nil {
|
if err := r.moduleLoader.RefreshModule(state.L, moduleName); err != nil {
|
||||||
success = false
|
success = false
|
||||||
logger.Debug("Failed to refresh module %s in state %d: %v", moduleName, state.index, err)
|
logger.Debugf("Failed to refresh module %s in state %d: %v", moduleName, state.index, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
logger.Debug("Successfully refreshed module: %s", moduleName)
|
logger.Debugf("Successfully refreshed module: %s", moduleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return success
|
return success
|
||||||
@ -436,7 +436,7 @@ func (r *Runner) RefreshModuleByPath(filePath string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug("Refreshing module by path: %s", filePath)
|
logger.Debugf("Refreshing module by path: %s", filePath)
|
||||||
|
|
||||||
success := true
|
success := true
|
||||||
for _, state := range r.states {
|
for _, state := range r.states {
|
||||||
@ -447,7 +447,7 @@ func (r *Runner) RefreshModuleByPath(filePath string) bool {
|
|||||||
// Use the enhanced module loader refresh by path
|
// Use the enhanced module loader refresh by path
|
||||||
if err := r.moduleLoader.RefreshModuleByPath(state.L, filePath); err != nil {
|
if err := r.moduleLoader.RefreshModuleByPath(state.L, filePath); err != nil {
|
||||||
success = false
|
success = false
|
||||||
logger.Debug("Failed to refresh module at %s in state %d: %v", filePath, state.index, err)
|
logger.Debugf("Failed to refresh module at %s in state %d: %v", filePath, state.index, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,31 +46,31 @@ func (s *Sandbox) AddModule(name string, module any) {
|
|||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
s.modules[name] = module
|
s.modules[name] = module
|
||||||
logger.Debug("Added module: %s", name)
|
logger.Debugf("Added module: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup initializes the sandbox in a Lua state
|
// Setup initializes the sandbox in a Lua state
|
||||||
func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Debug("Setting up sandbox...")
|
logger.Debugf("Setting up sandbox...")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := loadSandboxIntoState(state, verbose); err != nil {
|
if err := loadSandboxIntoState(state, verbose); err != nil {
|
||||||
logger.Error("Failed to load sandbox: %v", err)
|
logger.Errorf("Failed to load sandbox: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.registerCoreFunctions(state); err != nil {
|
if err := s.registerCoreFunctions(state); err != nil {
|
||||||
logger.Error("Failed to register core functions: %v", err)
|
logger.Errorf("Failed to register core functions: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
for name, module := range s.modules {
|
for name, module := range s.modules {
|
||||||
logger.Debug("Registering module: %s", name)
|
logger.Debugf("Registering module: %s", name)
|
||||||
if err := state.PushValue(module); err != nil {
|
if err := state.PushValue(module); err != nil {
|
||||||
s.mu.RUnlock()
|
s.mu.RUnlock()
|
||||||
logger.Error("Failed to register module %s: %v", name, err)
|
logger.Errorf("Failed to register module %s: %v", name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
state.SetGlobal(name)
|
state.SetGlobal(name)
|
||||||
@ -78,7 +78,7 @@ func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
|||||||
s.mu.RUnlock()
|
s.mu.RUnlock()
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.Debug("Sandbox setup complete")
|
logger.Debugf("Sandbox setup complete")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ var (
|
|||||||
// InitSQLite initializes the SQLite subsystem
|
// InitSQLite initializes the SQLite subsystem
|
||||||
func InitSQLite(dir string) {
|
func InitSQLite(dir string) {
|
||||||
dataDir = dir
|
dataDir = dir
|
||||||
logger.Info("SQLite is g2g! %s", color.Yellow(dir))
|
logger.Infof("SQLite is g2g! %s", color.Yellow(dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSQLitePoolSize sets the pool size to match the runner pool size
|
// SetSQLitePoolSize sets the pool size to match the runner pool size
|
||||||
@ -45,12 +45,12 @@ func CleanupSQLite() {
|
|||||||
|
|
||||||
for name, pool := range dbPools {
|
for name, pool := range dbPools {
|
||||||
if err := pool.Close(); err != nil {
|
if err := pool.Close(); err != nil {
|
||||||
logger.Error("Failed to close database %s: %v", name, err)
|
logger.Errorf("Failed to close database %s: %v", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbPools = make(map[string]*sqlitex.Pool)
|
dbPools = make(map[string]*sqlitex.Pool)
|
||||||
logger.Debug("SQLite connections closed")
|
logger.Debugf("SQLite connections closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPool returns a connection pool for the database
|
// getPool returns a connection pool for the database
|
||||||
@ -105,7 +105,7 @@ func getPool(dbName string) (*sqlitex.Pool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbPools[dbName] = pool
|
dbPools[dbName] = pool
|
||||||
logger.Debug("Created SQLite pool for %s (size: %d)", dbName, poolSize)
|
logger.Debugf("Created SQLite pool for %s (size: %d)", dbName, poolSize)
|
||||||
return pool, nil
|
return pool, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
fin "git.sharkk.net/Sharkk/Fin"
|
fin "git.sharkk.net/Sharkk/Fin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config represents the current loaded configuration for the server
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// Server settings
|
|
||||||
Server struct {
|
Server struct {
|
||||||
Port int
|
Port int
|
||||||
Debug bool
|
Debug bool
|
||||||
@ -15,12 +15,10 @@ type Config struct {
|
|||||||
StaticPrefix string
|
StaticPrefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runner settings
|
|
||||||
Runner struct {
|
Runner struct {
|
||||||
PoolSize int
|
PoolSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directory paths
|
|
||||||
Dirs struct {
|
Dirs struct {
|
||||||
Routes string
|
Routes string
|
||||||
Static string
|
Static string
|
||||||
@ -30,7 +28,6 @@ type Config struct {
|
|||||||
Libs []string
|
Libs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raw fin data struct for custom data
|
|
||||||
data *fin.Data
|
data *fin.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,345 +8,184 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"Moonshark/utils/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ANSI color codes
|
// Log levels
|
||||||
const (
|
const (
|
||||||
colorReset = "\033[0m"
|
LevelDebug = iota
|
||||||
colorRed = "\033[31m"
|
LevelInfo
|
||||||
colorGreen = "\033[32m"
|
LevelWarn
|
||||||
colorYellow = "\033[33m"
|
LevelError
|
||||||
colorBlue = "\033[34m"
|
LevelFatal
|
||||||
colorPurple = "\033[35m"
|
|
||||||
colorCyan = "\033[36m"
|
|
||||||
colorGray = "\033[90m"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Log types
|
// Level config
|
||||||
const (
|
var levels = map[int]struct {
|
||||||
TypeNone = iota
|
|
||||||
TypeDebug
|
|
||||||
TypeInfo
|
|
||||||
TypeWarning
|
|
||||||
TypeError
|
|
||||||
TypeServer
|
|
||||||
TypeFatal
|
|
||||||
)
|
|
||||||
|
|
||||||
// Type properties
|
|
||||||
var typeProps = map[int]struct {
|
|
||||||
tag string
|
tag string
|
||||||
color string
|
color func(string) string
|
||||||
}{
|
}{
|
||||||
TypeDebug: {"D", colorCyan},
|
LevelDebug: {"D", color.Cyan},
|
||||||
TypeInfo: {"I", colorBlue},
|
LevelInfo: {"I", color.Blue},
|
||||||
TypeWarning: {"W", colorYellow},
|
LevelWarn: {"W", color.Yellow},
|
||||||
TypeError: {"E", colorRed},
|
LevelError: {"E", color.Red},
|
||||||
TypeServer: {"S", colorGreen},
|
LevelFatal: {"F", color.Purple},
|
||||||
TypeFatal: {"F", colorPurple},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeFormat = "15:04:05"
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
globalLogger *Logger
|
global *Logger
|
||||||
globalLoggerOnce sync.Once
|
globalOnce sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
writer io.Writer
|
out io.Writer
|
||||||
useColors bool
|
enabled atomic.Bool
|
||||||
timeFormat string
|
timestamp atomic.Bool
|
||||||
showTimestamp bool
|
debug atomic.Bool
|
||||||
|
colors atomic.Bool
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
debugMode atomic.Bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLogger() *Logger {
|
func init() {
|
||||||
globalLoggerOnce.Do(func() {
|
globalOnce.Do(func() {
|
||||||
globalLogger = newLogger(true, true)
|
global = &Logger{out: os.Stdout}
|
||||||
|
global.enabled.Store(true)
|
||||||
|
global.timestamp.Store(true)
|
||||||
|
global.colors.Store(true)
|
||||||
})
|
})
|
||||||
return globalLogger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitGlobalLogger(useColors bool, showTimestamp bool) {
|
func applyColor(text string, colorFunc func(string) string) string {
|
||||||
globalLogger = newLogger(useColors, showTimestamp)
|
if global.colors.Load() {
|
||||||
}
|
return colorFunc(text)
|
||||||
|
|
||||||
func newLogger(useColors bool, showTimestamp bool) *Logger {
|
|
||||||
return &Logger{
|
|
||||||
writer: os.Stdout,
|
|
||||||
useColors: useColors,
|
|
||||||
timeFormat: timeFormat,
|
|
||||||
showTimestamp: showTimestamp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(useColors bool, showTimestamp bool) *Logger {
|
|
||||||
return newLogger(useColors, showTimestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) SetOutput(w io.Writer) {
|
|
||||||
l.mu.Lock()
|
|
||||||
l.writer = w
|
|
||||||
l.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) TimeFormat() string {
|
|
||||||
return l.timeFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) SetTimeFormat(format string) {
|
|
||||||
l.mu.Lock()
|
|
||||||
l.timeFormat = format
|
|
||||||
l.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) EnableTimestamp() {
|
|
||||||
l.showTimestamp = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) DisableTimestamp() {
|
|
||||||
l.showTimestamp = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) EnableColors() {
|
|
||||||
l.useColors = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) DisableColors() {
|
|
||||||
l.useColors = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) EnableDebug() {
|
|
||||||
l.debugMode.Store(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) DisableDebug() {
|
|
||||||
l.debugMode.Store(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) IsDebugEnabled() bool {
|
|
||||||
return l.debugMode.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) applyColor(text, color string) string {
|
|
||||||
if l.useColors {
|
|
||||||
return color + text + colorReset
|
|
||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
func stripAnsiColors(s string) string {
|
func write(level int, msg string) {
|
||||||
result := ""
|
if !global.enabled.Load() {
|
||||||
inEscape := false
|
|
||||||
|
|
||||||
for _, c := range s {
|
|
||||||
if inEscape {
|
|
||||||
if c == 'm' {
|
|
||||||
inEscape = false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if c == '\033' {
|
|
||||||
inEscape = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result += string(c)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) writeMessage(logType int, message string, rawMode bool) {
|
|
||||||
if rawMode {
|
|
||||||
l.mu.Lock()
|
|
||||||
_, _ = fmt.Fprint(l.writer, message+"\n")
|
|
||||||
l.mu.Unlock()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := []string{}
|
if level == LevelDebug && !global.debug.Load() {
|
||||||
|
return
|
||||||
if l.showTimestamp {
|
|
||||||
timestamp := time.Now().Format(l.timeFormat)
|
|
||||||
if l.useColors {
|
|
||||||
timestamp = l.applyColor(timestamp, colorGray)
|
|
||||||
}
|
|
||||||
parts = append(parts, timestamp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if logType != TypeNone {
|
var parts []string
|
||||||
props := typeProps[logType]
|
|
||||||
tag := "[" + props.tag + "]"
|
if global.timestamp.Load() {
|
||||||
if l.useColors {
|
ts := applyColor(time.Now().Format("3:04PM"), color.Gray)
|
||||||
tag = l.applyColor(tag, props.color)
|
parts = append(parts, ts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg, ok := levels[level]; ok {
|
||||||
|
tag := applyColor("["+cfg.tag+"]", cfg.color)
|
||||||
parts = append(parts, tag)
|
parts = append(parts, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
parts = append(parts, message)
|
parts = append(parts, msg)
|
||||||
logLine := strings.Join(parts, " ") + "\n"
|
line := strings.Join(parts, " ") + "\n"
|
||||||
|
|
||||||
l.mu.Lock()
|
global.mu.Lock()
|
||||||
_, _ = fmt.Fprint(l.writer, logLine)
|
fmt.Fprint(global.out, line)
|
||||||
if logType == TypeFatal {
|
if level == LevelFatal {
|
||||||
if f, ok := l.writer.(*os.File); ok {
|
if f, ok := global.out.(*os.File); ok {
|
||||||
_ = f.Sync()
|
f.Sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l.mu.Unlock()
|
global.mu.Unlock()
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) log(logType int, format string, args ...any) {
|
if level == LevelFatal {
|
||||||
// Only filter debug messages
|
|
||||||
if logType == TypeDebug && !l.debugMode.Load() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var message string
|
|
||||||
if len(args) > 0 {
|
|
||||||
message = fmt.Sprintf(format, args...)
|
|
||||||
} else {
|
|
||||||
message = format
|
|
||||||
}
|
|
||||||
|
|
||||||
l.writeMessage(logType, message, false)
|
|
||||||
|
|
||||||
if logType == TypeFatal {
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) LogRaw(format string, args ...any) {
|
func log(level int, format string, args ...any) {
|
||||||
var message string
|
var msg string
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
message = fmt.Sprintf(format, args...)
|
msg = fmt.Sprintf(format, args...)
|
||||||
} else {
|
} else {
|
||||||
message = format
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
if !l.useColors {
|
var msg string
|
||||||
message = stripAnsiColors(message)
|
if len(args) > 0 {
|
||||||
|
msg = fmt.Sprintf(format, args...)
|
||||||
|
} else {
|
||||||
|
msg = format
|
||||||
}
|
}
|
||||||
|
|
||||||
l.writeMessage(TypeInfo, message, true)
|
global.mu.Lock()
|
||||||
|
fmt.Fprint(global.out, msg+"\n")
|
||||||
|
global.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Debug(format string, args ...any) {
|
func Request(status int, method, path string, duration time.Duration) {
|
||||||
l.log(TypeDebug, format, args...)
|
if !global.enabled.Load() {
|
||||||
}
|
return
|
||||||
|
}
|
||||||
func (l *Logger) Info(format string, args ...any) {
|
|
||||||
l.log(TypeInfo, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) Warning(format string, args ...any) {
|
|
||||||
l.log(TypeWarning, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) Error(format string, args ...any) {
|
|
||||||
l.log(TypeError, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) Fatal(format string, args ...any) {
|
|
||||||
l.log(TypeFatal, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) Server(format string, args ...any) {
|
|
||||||
l.log(TypeServer, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) LogRequest(statusCode int, method, path string, duration time.Duration) {
|
|
||||||
var statusColor string
|
|
||||||
|
|
||||||
|
var statusColor func(string) string
|
||||||
switch {
|
switch {
|
||||||
case statusCode < 300:
|
case status < 300:
|
||||||
statusColor = colorGreen
|
statusColor = color.Green
|
||||||
case statusCode < 400:
|
case status < 400:
|
||||||
statusColor = colorCyan
|
statusColor = color.Cyan
|
||||||
case statusCode < 500:
|
case status < 500:
|
||||||
statusColor = colorYellow
|
statusColor = color.Yellow
|
||||||
default:
|
default:
|
||||||
statusColor = colorRed
|
statusColor = color.Red
|
||||||
}
|
}
|
||||||
|
|
||||||
var durationStr string
|
var dur string
|
||||||
micros := duration.Microseconds()
|
us := duration.Microseconds()
|
||||||
if micros < 1000 {
|
switch {
|
||||||
durationStr = fmt.Sprintf("%.0fµs", float64(micros))
|
case us < 1000:
|
||||||
} else if micros < 1000000 {
|
dur = fmt.Sprintf("%.0fµs", float64(us))
|
||||||
durationStr = fmt.Sprintf("%.1fms", float64(micros)/1000)
|
case us < 1000000:
|
||||||
} else {
|
dur = fmt.Sprintf("%.1fms", float64(us)/1000)
|
||||||
durationStr = fmt.Sprintf("%.2fs", duration.Seconds())
|
default:
|
||||||
|
dur = fmt.Sprintf("%.2fs", duration.Seconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
message := fmt.Sprintf("%s %s %s %s",
|
msg := fmt.Sprintf("%s %s %s %s",
|
||||||
l.applyColor("["+method+"]", colorGray),
|
applyColor("["+method+"]", color.Gray),
|
||||||
l.applyColor(fmt.Sprintf("%d", statusCode), statusColor),
|
applyColor(fmt.Sprintf("%d", status), statusColor),
|
||||||
l.applyColor(path, colorGray),
|
applyColor(path, color.Gray),
|
||||||
l.applyColor(durationStr, colorGray),
|
applyColor(dur, color.Gray),
|
||||||
)
|
)
|
||||||
|
|
||||||
l.writeMessage(TypeNone, message, false)
|
global.mu.Lock()
|
||||||
}
|
fmt.Fprint(global.out, msg+"\n")
|
||||||
|
global.mu.Unlock()
|
||||||
// Global functions
|
|
||||||
func Debug(format string, args ...any) {
|
|
||||||
GetLogger().Debug(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Info(format string, args ...any) {
|
|
||||||
GetLogger().Info(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Warning(format string, args ...any) {
|
|
||||||
GetLogger().Warning(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Error(format string, args ...any) {
|
|
||||||
GetLogger().Error(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Fatal(format string, args ...any) {
|
|
||||||
GetLogger().Fatal(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Server(format string, args ...any) {
|
|
||||||
GetLogger().Server(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogRaw(format string, args ...any) {
|
|
||||||
GetLogger().LogRaw(format, args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetOutput(w io.Writer) {
|
func SetOutput(w io.Writer) {
|
||||||
GetLogger().SetOutput(w)
|
global.mu.Lock()
|
||||||
|
global.out = w
|
||||||
|
global.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TimeFormat() string {
|
func Enable() { global.enabled.Store(true) }
|
||||||
return GetLogger().TimeFormat()
|
func Disable() { global.enabled.Store(false) }
|
||||||
}
|
func IsEnabled() bool { return global.enabled.Load() }
|
||||||
|
func EnableColors() { global.colors.Store(true) }
|
||||||
func EnableDebug() {
|
func DisableColors() { global.colors.Store(false) }
|
||||||
GetLogger().EnableDebug()
|
func ColorsEnabled() bool { return global.colors.Load() }
|
||||||
}
|
func Timestamp(enabled bool) { global.timestamp.Store(enabled) }
|
||||||
|
func Debug(enabled bool) { global.debug.Store(enabled) }
|
||||||
func DisableDebug() {
|
func IsDebug() bool { return global.debug.Load() }
|
||||||
GetLogger().DisableDebug()
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsDebugEnabled() bool {
|
|
||||||
return GetLogger().IsDebugEnabled()
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnableTimestamp() {
|
|
||||||
GetLogger().EnableTimestamp()
|
|
||||||
}
|
|
||||||
|
|
||||||
func DisableTimestamp() {
|
|
||||||
GetLogger().DisableTimestamp()
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogRequest(statusCode int, method, path string, duration time.Duration) {
|
|
||||||
GetLogger().LogRequest(statusCode, method, path, duration)
|
|
||||||
}
|
|
||||||
|
@ -48,7 +48,7 @@ func WatchLuaRouter(router *router.LuaRouter, runner *runner.Runner, routesDir s
|
|||||||
return nil, fmt.Errorf("failed to watch directory: %w", err)
|
return nil, fmt.Errorf("failed to watch directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Started watching Lua routes! %s", color.Yellow(routesDir))
|
logger.Infof("Started watching Lua routes! %s", color.Yellow(routesDir))
|
||||||
return watcher, nil
|
return watcher, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ func WatchLuaModules(luaRunner *runner.Runner, libDirs []string) ([]*DirectoryWa
|
|||||||
}
|
}
|
||||||
|
|
||||||
watchers = append(watchers, watcher)
|
watchers = append(watchers, watcher)
|
||||||
logger.Info("Started watching Lua modules! %s", color.Yellow(dir))
|
logger.Infof("Started watching Lua modules! %s", color.Yellow(dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
return watchers, nil
|
return watchers, nil
|
||||||
|
@ -198,12 +198,12 @@ func (w *DirectoryWatcher) notifyChange() {
|
|||||||
|
|
||||||
// logDebug logs a debug message with the watcher's directory prefix
|
// logDebug logs a debug message with the watcher's directory prefix
|
||||||
func (w *DirectoryWatcher) logDebug(format string, args ...any) {
|
func (w *DirectoryWatcher) logDebug(format string, args ...any) {
|
||||||
logger.Debug("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
logger.Debugf("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// logError logs an error message with the watcher's directory prefix
|
// logError logs an error message with the watcher's directory prefix
|
||||||
func (w *DirectoryWatcher) logError(format string, args ...any) {
|
func (w *DirectoryWatcher) logError(format string, args ...any) {
|
||||||
logger.Error("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
logger.Errorf("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDir gets the DirectoryWatcher's current directory
|
// GetDir gets the DirectoryWatcher's current directory
|
||||||
|
@ -86,7 +86,7 @@ func (m *WatcherManager) WatchDirectory(config DirectoryWatcherConfig) (*Directo
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.watchers[config.Dir] = watcher
|
m.watchers[config.Dir] = watcher
|
||||||
logger.Debug("WatcherManager added watcher for %s", config.Dir)
|
logger.Debugf("WatcherManager added watcher for %s", config.Dir)
|
||||||
|
|
||||||
return watcher, nil
|
return watcher, nil
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ func (m *WatcherManager) UnwatchDirectory(dir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete(m.watchers, dir)
|
delete(m.watchers, dir)
|
||||||
logger.Debug("WatcherManager removed watcher for %s", dir)
|
logger.Debugf("WatcherManager removed watcher for %s", dir)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ func (m *WatcherManager) checkAllDirectories() {
|
|||||||
for _, watcher := range watchers {
|
for _, watcher := range watchers {
|
||||||
if watcher.consecutiveErrors > 3 {
|
if watcher.consecutiveErrors > 3 {
|
||||||
if watcher.consecutiveErrors == 4 {
|
if watcher.consecutiveErrors == 4 {
|
||||||
logger.Error("Temporarily skipping directory %s due to errors: %v",
|
logger.Errorf("Temporarily skipping directory %s due to errors: %v",
|
||||||
watcher.dir, watcher.lastError)
|
watcher.dir, watcher.lastError)
|
||||||
watcher.consecutiveErrors++
|
watcher.consecutiveErrors++
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ func (m *WatcherManager) checkAllDirectories() {
|
|||||||
|
|
||||||
changed, err := watcher.checkForChanges()
|
changed, err := watcher.checkForChanges()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Error checking directory %s: %v", watcher.dir, err)
|
logger.Errorf("Error checking directory %s: %v", watcher.dir, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user