refactor
This commit is contained in:
parent
50f4cb91f6
commit
87eaddd8e4
@ -4,13 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"Moonshark/core/metadata"
|
|
||||||
"Moonshark/core/routers"
|
"Moonshark/core/routers"
|
||||||
"Moonshark/core/runner"
|
"Moonshark/core/runner"
|
||||||
"Moonshark/core/sessions"
|
"Moonshark/core/sessions"
|
||||||
"Moonshark/core/utils"
|
"Moonshark/core/utils"
|
||||||
"Moonshark/core/utils/config"
|
"Moonshark/core/utils/config"
|
||||||
"Moonshark/core/utils/logger"
|
"Moonshark/core/utils/logger"
|
||||||
|
"Moonshark/core/utils/metadata"
|
||||||
|
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
)
|
)
|
||||||
@ -28,7 +28,7 @@ type Server struct {
|
|||||||
errorConfig utils.ErrorPageConfig
|
errorConfig utils.ErrorPageConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new HTTP server with optimized connection settings
|
// New creates a new HTTP server
|
||||||
func New(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter,
|
func New(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter,
|
||||||
runner *runner.Runner, loggingEnabled bool, debugMode bool,
|
runner *runner.Runner, loggingEnabled bool, debugMode bool,
|
||||||
overrideDir string, config *config.Config) *Server {
|
overrideDir string, config *config.Config) *Server {
|
||||||
@ -47,7 +47,6 @@ func New(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure fasthttp server
|
|
||||||
server.fasthttpServer = &fasthttp.Server{
|
server.fasthttpServer = &fasthttp.Server{
|
||||||
Handler: server.handleRequest,
|
Handler: server.handleRequest,
|
||||||
Name: "Moonshark/" + metadata.Version,
|
Name: "Moonshark/" + metadata.Version,
|
||||||
@ -59,7 +58,7 @@ func New(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter,
|
|||||||
TCPKeepalivePeriod: 60 * time.Second,
|
TCPKeepalivePeriod: 60 * time.Second,
|
||||||
ReduceMemoryUsage: true,
|
ReduceMemoryUsage: true,
|
||||||
GetOnly: false,
|
GetOnly: false,
|
||||||
DisablePreParseMultipartForm: true, // We'll handle parsing manually
|
DisablePreParseMultipartForm: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return server
|
return server
|
||||||
@ -67,7 +66,7 @@ func New(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter,
|
|||||||
|
|
||||||
// ListenAndServe starts the server on the given address
|
// ListenAndServe starts the server on the given address
|
||||||
func (s *Server) ListenAndServe(addr string) error {
|
func (s *Server) ListenAndServe(addr string) error {
|
||||||
logger.ServerCont("Catch the swell at http://localhost%s", addr)
|
logger.Server("Catch the swell at http://localhost%s", addr)
|
||||||
return s.fasthttpServer.ListenAndServe(addr)
|
return s.fasthttpServer.ListenAndServe(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,11 +81,9 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
method := string(ctx.Method())
|
method := string(ctx.Method())
|
||||||
path := string(ctx.Path())
|
path := string(ctx.Path())
|
||||||
|
|
||||||
// Special case for debug stats when debug mode is enabled
|
|
||||||
if s.debugMode && path == "/debug/stats" {
|
if s.debugMode && path == "/debug/stats" {
|
||||||
s.handleDebugStats(ctx)
|
s.handleDebugStats(ctx)
|
||||||
|
|
||||||
// Log request
|
|
||||||
if s.loggingEnabled {
|
if s.loggingEnabled {
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
LogRequest(ctx.Response.StatusCode(), method, path, duration)
|
LogRequest(ctx.Response.StatusCode(), method, path, duration)
|
||||||
@ -94,10 +91,8 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the request
|
|
||||||
s.processRequest(ctx)
|
s.processRequest(ctx)
|
||||||
|
|
||||||
// Log the request
|
|
||||||
if s.loggingEnabled {
|
if s.loggingEnabled {
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
LogRequest(ctx.Response.StatusCode(), method, path, duration)
|
LogRequest(ctx.Response.StatusCode(), method, path, duration)
|
||||||
@ -111,23 +106,19 @@ func (s *Server) processRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
|
|
||||||
logger.Debug("Processing request %s %s", method, path)
|
logger.Debug("Processing request %s %s", method, path)
|
||||||
|
|
||||||
// Try Lua routes first
|
|
||||||
params := &routers.Params{}
|
params := &routers.Params{}
|
||||||
bytecode, scriptPath, found := s.luaRouter.GetBytecode(method, path, params)
|
bytecode, scriptPath, found := s.luaRouter.GetBytecode(method, path, params)
|
||||||
|
|
||||||
// Check if we found a route but it has no valid bytecode (compile error)
|
// Check if we found a route but it has no valid bytecode (compile error)
|
||||||
if found && len(bytecode) == 0 {
|
if found && len(bytecode) == 0 {
|
||||||
// Get the actual error from the router
|
|
||||||
errorMsg := "Route exists but failed to compile. Check server logs for details."
|
errorMsg := "Route exists but failed to compile. Check server logs for details."
|
||||||
|
|
||||||
// Get the actual node to access its error
|
|
||||||
if node, _ := s.luaRouter.GetNodeWithError(method, path, params); node != nil && node.Error != nil {
|
if node, _ := s.luaRouter.GetNodeWithError(method, path, params); node != nil && node.Error != nil {
|
||||||
errorMsg = node.Error.Error()
|
errorMsg = node.Error.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error("%s %s - %s", method, path, errorMsg)
|
logger.Error("%s %s - %s", method, path, errorMsg)
|
||||||
|
|
||||||
// Show error page with the actual error message
|
|
||||||
ctx.SetContentType("text/html; charset=utf-8")
|
ctx.SetContentType("text/html; charset=utf-8")
|
||||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
errorHTML := utils.InternalErrorPage(s.errorConfig, path, errorMsg)
|
errorHTML := utils.InternalErrorPage(s.errorConfig, path, errorMsg)
|
||||||
@ -139,13 +130,11 @@ func (s *Server) processRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then try static files
|
|
||||||
if _, found := s.staticRouter.Match(path); found {
|
if _, found := s.staticRouter.Match(path); found {
|
||||||
s.staticRouter.ServeHTTP(ctx)
|
s.staticRouter.ServeHTTP(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// No route found - 404 Not Found
|
|
||||||
ctx.SetContentType("text/html; charset=utf-8")
|
ctx.SetContentType("text/html; charset=utf-8")
|
||||||
ctx.SetStatusCode(fasthttp.StatusNotFound)
|
ctx.SetStatusCode(fasthttp.StatusNotFound)
|
||||||
ctx.SetBody([]byte(utils.NotFoundPage(s.errorConfig, path)))
|
ctx.SetBody([]byte(utils.NotFoundPage(s.errorConfig, path)))
|
||||||
@ -159,19 +148,16 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
|||||||
|
|
||||||
host := string(ctx.Host())
|
host := string(ctx.Host())
|
||||||
|
|
||||||
// Set up additional context values
|
|
||||||
luaCtx.Set("method", method)
|
luaCtx.Set("method", method)
|
||||||
luaCtx.Set("path", path)
|
luaCtx.Set("path", path)
|
||||||
luaCtx.Set("host", host)
|
luaCtx.Set("host", host)
|
||||||
|
|
||||||
// Add session data
|
|
||||||
session := s.sessionManager.GetSessionFromRequest(ctx)
|
session := s.sessionManager.GetSessionFromRequest(ctx)
|
||||||
luaCtx.Set("session", map[string]any{
|
luaCtx.Set("session", map[string]any{
|
||||||
"id": session.ID,
|
"id": session.ID,
|
||||||
"data": session.Data,
|
"data": session.Data,
|
||||||
})
|
})
|
||||||
|
|
||||||
// URL parameters
|
|
||||||
if params.Count > 0 {
|
if params.Count > 0 {
|
||||||
paramMap := make(map[string]any, params.Count)
|
paramMap := make(map[string]any, params.Count)
|
||||||
for i, key := range params.Keys {
|
for i, key := range params.Keys {
|
||||||
@ -182,7 +168,6 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
|||||||
luaCtx.Set("params", make(map[string]any))
|
luaCtx.Set("params", make(map[string]any))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse form data for POST/PUT/PATCH requests
|
|
||||||
if method == "POST" || method == "PUT" || method == "PATCH" {
|
if method == "POST" || method == "PUT" || method == "PATCH" {
|
||||||
formData, err := ParseForm(ctx)
|
formData, err := ParseForm(ctx)
|
||||||
if err == nil && len(formData) > 0 {
|
if err == nil && len(formData) > 0 {
|
||||||
@ -197,12 +182,10 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
|||||||
luaCtx.Set("form", make(map[string]any))
|
luaCtx.Set("form", make(map[string]any))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute Lua script
|
|
||||||
response, err := s.luaRunner.Run(bytecode, luaCtx, scriptPath)
|
response, err := s.luaRunner.Run(bytecode, luaCtx, scriptPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Error executing Lua route: %v", err)
|
logger.Error("Error executing Lua route: %v", err)
|
||||||
|
|
||||||
// General error handling
|
|
||||||
ctx.SetContentType("text/html; charset=utf-8")
|
ctx.SetContentType("text/html; charset=utf-8")
|
||||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
errorHTML := utils.InternalErrorPage(s.errorConfig, path, err.Error())
|
errorHTML := utils.InternalErrorPage(s.errorConfig, path, err.Error())
|
||||||
@ -210,25 +193,19 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle persisting session data
|
|
||||||
for k, v := range response.SessionData {
|
for k, v := range response.SessionData {
|
||||||
session.Set(k, v)
|
session.Set(k, v)
|
||||||
}
|
}
|
||||||
s.sessionManager.ApplySessionCookie(ctx, session)
|
s.sessionManager.ApplySessionCookie(ctx, session)
|
||||||
|
|
||||||
// Apply response to HTTP context
|
|
||||||
runner.ApplyResponse(response, ctx)
|
runner.ApplyResponse(response, ctx)
|
||||||
|
|
||||||
// Release the response when done
|
|
||||||
runner.ReleaseResponse(response)
|
runner.ReleaseResponse(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleDebugStats displays debug statistics
|
// handleDebugStats displays debug statistics
|
||||||
func (s *Server) handleDebugStats(ctx *fasthttp.RequestCtx) {
|
func (s *Server) handleDebugStats(ctx *fasthttp.RequestCtx) {
|
||||||
// Collect system stats
|
|
||||||
stats := utils.CollectSystemStats(s.config)
|
stats := utils.CollectSystemStats(s.config)
|
||||||
|
|
||||||
// Add component stats
|
|
||||||
routeCount, bytecodeBytes := s.luaRouter.GetRouteStats()
|
routeCount, bytecodeBytes := s.luaRouter.GetRouteStats()
|
||||||
//stateCount := s.luaRunner.GetStateCount()
|
//stateCount := s.luaRunner.GetStateCount()
|
||||||
//activeStates := s.luaRunner.GetActiveStateCount()
|
//activeStates := s.luaRunner.GetActiveStateCount()
|
||||||
@ -240,11 +217,7 @@ func (s *Server) handleDebugStats(ctx *fasthttp.RequestCtx) {
|
|||||||
//ActiveStates: activeStates,
|
//ActiveStates: activeStates,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate HTML page
|
|
||||||
html := utils.DebugStatsPage(stats)
|
|
||||||
|
|
||||||
// Send the response
|
|
||||||
ctx.SetContentType("text/html; charset=utf-8")
|
ctx.SetContentType("text/html; charset=utf-8")
|
||||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||||
ctx.SetBody([]byte(html))
|
ctx.SetBody([]byte(utils.DebugStatsPage(stats)))
|
||||||
}
|
}
|
@ -78,7 +78,7 @@ func (s *Moonshark) loadConfig(configPath string) error {
|
|||||||
s.Config, err = config.Load(configPath)
|
s.Config, err = config.Load(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Wipeout! Couldn't load config file: %v", err)
|
logger.Warning("Wipeout! Couldn't load config file: %v", err)
|
||||||
logger.WarningCont("Rolling with the default setup")
|
logger.Warning("Rolling with the default setup")
|
||||||
s.Config = config.New()
|
s.Config = config.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,14 +226,6 @@ func (s *Moonshark) setupWatchers() error {
|
|||||||
func (s *Moonshark) Start() error {
|
func (s *Moonshark) Start() error {
|
||||||
logger.Server("Surf's up on port %d!", s.Config.Server.Port)
|
logger.Server("Surf's up on port %d!", s.Config.Server.Port)
|
||||||
|
|
||||||
// Log HTTP logging status
|
|
||||||
if s.Config.Server.HTTPLogging {
|
|
||||||
logger.ServerCont("HTTP logging is turned on - watch those waves")
|
|
||||||
} else {
|
|
||||||
logger.ServerCont("HTTP logging is turned off - waves are flat bro")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the server in a non-blocking way
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil {
|
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil {
|
||||||
if err.Error() != "http: Server closed" {
|
if err.Error() != "http: Server closed" {
|
||||||
@ -265,6 +257,6 @@ func (s *Moonshark) Shutdown() error {
|
|||||||
|
|
||||||
s.LuaRunner.Close()
|
s.LuaRunner.Close()
|
||||||
|
|
||||||
logger.ServerCont("Server stopped")
|
logger.Server("Server stopped")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@ -47,7 +47,6 @@ type LuaRouter struct {
|
|||||||
failedRoutes map[string]*RouteError // Track failed routes
|
failedRoutes map[string]*RouteError // Track failed routes
|
||||||
mu sync.RWMutex // Lock for concurrent access to routes
|
mu sync.RWMutex // Lock for concurrent access to routes
|
||||||
|
|
||||||
// Cache for route matches and bytecode
|
|
||||||
routeCache *fastcache.Cache // Cache for route lookups
|
routeCache *fastcache.Cache // Cache for route lookups
|
||||||
bytecodeCache *fastcache.Cache // Cache for compiled bytecode
|
bytecodeCache *fastcache.Cache // Cache for compiled bytecode
|
||||||
}
|
}
|
||||||
@ -64,7 +63,6 @@ type node struct {
|
|||||||
|
|
||||||
// NewLuaRouter creates a new LuaRouter instance
|
// NewLuaRouter creates a new LuaRouter instance
|
||||||
func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
||||||
// Verify routes directory exists
|
|
||||||
info, err := os.Stat(routesDir)
|
info, err := os.Stat(routesDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -81,7 +79,6 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
|||||||
bytecodeCache: fastcache.New(defaultBytecodeMaxBytes),
|
bytecodeCache: fastcache.New(defaultBytecodeMaxBytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize method trees
|
|
||||||
methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"}
|
methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"}
|
||||||
for _, method := range methods {
|
for _, method := range methods {
|
||||||
r.routes[method] = &node{
|
r.routes[method] = &node{
|
||||||
@ -89,7 +86,6 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build routes
|
|
||||||
err = r.buildRoutes()
|
err = r.buildRoutes()
|
||||||
|
|
||||||
// If some routes failed to compile, return the router with a warning error
|
// If some routes failed to compile, return the router with a warning error
|
||||||
@ -102,7 +98,6 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
|||||||
|
|
||||||
// buildRoutes scans the routes directory and builds the routing tree
|
// buildRoutes scans the routes directory and builds the routing tree
|
||||||
func (r *LuaRouter) buildRoutes() error {
|
func (r *LuaRouter) buildRoutes() error {
|
||||||
// Clear failed routes map
|
|
||||||
r.failedRoutes = make(map[string]*RouteError)
|
r.failedRoutes = make(map[string]*RouteError)
|
||||||
|
|
||||||
return filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error {
|
return filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error {
|
||||||
@ -110,38 +105,31 @@ func (r *LuaRouter) buildRoutes() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip directories
|
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only process .lua files
|
|
||||||
if !strings.HasSuffix(info.Name(), ".lua") {
|
if !strings.HasSuffix(info.Name(), ".lua") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract method from filename
|
|
||||||
method := strings.ToUpper(strings.TrimSuffix(info.Name(), ".lua"))
|
method := strings.ToUpper(strings.TrimSuffix(info.Name(), ".lua"))
|
||||||
|
|
||||||
// Check if valid method
|
|
||||||
root, exists := r.routes[method]
|
root, exists := r.routes[method]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil // Skip invalid methods
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get relative path for URL
|
|
||||||
relDir, err := filepath.Rel(r.routesDir, filepath.Dir(path))
|
relDir, err := filepath.Rel(r.routesDir, filepath.Dir(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build URL path
|
|
||||||
urlPath := "/"
|
urlPath := "/"
|
||||||
if relDir != "." {
|
if relDir != "." {
|
||||||
urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/")
|
urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add route to tree - continue even if there are errors
|
|
||||||
r.addRoute(root, urlPath, path, info.ModTime())
|
r.addRoute(root, urlPath, path, info.ModTime())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -162,7 +150,6 @@ func (r *LuaRouter) addRoute(root *node, urlPath, handlerPath string, modTime ti
|
|||||||
}
|
}
|
||||||
current = current.paramChild
|
current = current.paramChild
|
||||||
} else {
|
} else {
|
||||||
// Create or get static child
|
|
||||||
child, exists := current.staticChild[segment]
|
child, exists := current.staticChild[segment]
|
||||||
if !exists {
|
if !exists {
|
||||||
child = &node{
|
child = &node{
|
@ -21,18 +21,16 @@ var (
|
|||||||
|
|
||||||
// precompileSandboxCode compiles the sandbox.lua code to bytecode once
|
// precompileSandboxCode compiles the sandbox.lua code to bytecode once
|
||||||
func precompileSandboxCode() {
|
func precompileSandboxCode() {
|
||||||
// Create temporary state for compilation
|
|
||||||
tempState := luajit.New()
|
tempState := luajit.New()
|
||||||
if tempState == nil {
|
if tempState == nil {
|
||||||
logger.ErrorCont("Failed to create temp Lua state for bytecode compilation")
|
logger.Fatal("Failed to create temp Lua state for bytecode compilation")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer tempState.Close()
|
defer tempState.Close()
|
||||||
defer tempState.Cleanup()
|
defer tempState.Cleanup()
|
||||||
|
|
||||||
code, err := tempState.CompileBytecode(sandboxLuaCode, "sandbox.lua")
|
code, err := tempState.CompileBytecode(sandboxLuaCode, "sandbox.lua")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCont("Failed to compile sandbox code: %v", err)
|
logger.Error("Failed to compile sandbox code: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +38,7 @@ func precompileSandboxCode() {
|
|||||||
copy(bytecode, code)
|
copy(bytecode, code)
|
||||||
sandboxBytecode.Store(&bytecode)
|
sandboxBytecode.Store(&bytecode)
|
||||||
|
|
||||||
logger.ServerCont("Successfully precompiled sandbox.lua to bytecode (%d bytes)", len(code))
|
logger.Debug("Successfully precompiled sandbox.lua to bytecode (%d bytes)", len(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadSandboxIntoState loads the sandbox code into a Lua state
|
// loadSandboxIntoState loads the sandbox code into a Lua state
|
||||||
@ -50,14 +48,14 @@ func loadSandboxIntoState(state *luajit.State, verbose bool) error {
|
|||||||
bytecode := sandboxBytecode.Load()
|
bytecode := sandboxBytecode.Load()
|
||||||
if bytecode != nil && len(*bytecode) > 0 {
|
if bytecode != nil && len(*bytecode) > 0 {
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.ServerCont("Loading sandbox.lua from precompiled bytecode") // piggyback off Sandbox.go's Setup()
|
logger.Debug("Loading sandbox.lua from precompiled bytecode")
|
||||||
}
|
}
|
||||||
return state.LoadAndRunBytecode(*bytecode, "sandbox.lua")
|
return state.LoadAndRunBytecode(*bytecode, "sandbox.lua")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to direct execution
|
// Fallback to direct execution
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.WarningCont("Using non-precompiled sandbox.lua (bytecode compilation failed)")
|
logger.Warning("Using non-precompiled sandbox.lua (bytecode compilation failed)")
|
||||||
}
|
}
|
||||||
return state.DoString(sandboxLuaCode)
|
return state.DoString(sandboxLuaCode)
|
||||||
}
|
}
|
@ -121,7 +121,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.ServerCont("Creating Lua state %d", index)
|
logger.Debug("Creating Lua state %d", index)
|
||||||
}
|
}
|
||||||
|
|
||||||
L := luajit.New()
|
L := luajit.New()
|
||||||
@ -153,7 +153,7 @@ func (r *Runner) createState(index int) (*State, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.ServerCont("Lua state %d initialized successfully", index)
|
logger.Debug("Lua state %d initialized successfully", index)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &State{
|
return &State{
|
||||||
@ -289,7 +289,7 @@ cleanup:
|
|||||||
for i, state := range r.states {
|
for i, state := range r.states {
|
||||||
if state != nil {
|
if state != nil {
|
||||||
if state.inUse {
|
if state.inUse {
|
||||||
logger.WarningCont("Attempting to refresh state %d that is in use", i)
|
logger.Warning("Attempting to refresh state %d that is in use", i)
|
||||||
}
|
}
|
||||||
state.L.Cleanup()
|
state.L.Cleanup()
|
||||||
state.L.Close()
|
state.L.Close()
|
||||||
@ -302,7 +302,7 @@ cleanup:
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.ServerCont("All states refreshed successfully")
|
logger.Debug("All states refreshed successfully")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,13 +314,13 @@ func (r *Runner) NotifyFileChanged(filePath string) bool {
|
|||||||
// Check if it's a module file
|
// Check if it's a module file
|
||||||
module, isModule := r.moduleLoader.GetModuleByPath(filePath)
|
module, isModule := r.moduleLoader.GetModuleByPath(filePath)
|
||||||
if isModule {
|
if isModule {
|
||||||
logger.DebugCont("File is a module: %s", module)
|
logger.Debug("File is a module: %s", module)
|
||||||
return r.RefreshModule(module)
|
return r.RefreshModule(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-module files, refresh all states
|
// For non-module files, refresh all states
|
||||||
if err := r.RefreshStates(); err != nil {
|
if err := r.RefreshStates(); err != nil {
|
||||||
logger.DebugCont("Failed to refresh states: %v", err)
|
logger.Debug("Failed to refresh states: %v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ func (r *Runner) RefreshModule(moduleName string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.DebugCont("Refreshing module: %s", moduleName)
|
logger.Debug("Refreshing module: %s", moduleName)
|
||||||
|
|
||||||
success := true
|
success := true
|
||||||
for _, state := range r.states {
|
for _, state := range r.states {
|
||||||
@ -347,7 +347,7 @@ func (r *Runner) RefreshModule(moduleName string) bool {
|
|||||||
// Invalidate module in Lua
|
// Invalidate module in Lua
|
||||||
if err := state.L.DoString(`package.loaded["` + moduleName + `"] = nil`); err != nil {
|
if err := state.L.DoString(`package.loaded["` + moduleName + `"] = nil`); err != nil {
|
||||||
success = false
|
success = false
|
||||||
logger.DebugCont("Failed to invalidate module %s: %v", moduleName, err)
|
logger.Debug("Failed to invalidate module %s: %v", moduleName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,21 +55,21 @@ func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := loadSandboxIntoState(state, verbose); err != nil {
|
if err := loadSandboxIntoState(state, verbose); err != nil {
|
||||||
logger.ErrorCont("Failed to load sandbox: %v", err)
|
logger.Error("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.ErrorCont("Failed to register core functions: %v", err)
|
logger.Error("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.DebugCont("Registering module: %s", name)
|
logger.Debug("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.ErrorCont("Failed to register module %s: %v", name, err)
|
logger.Error("Failed to register module %s: %v", name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
state.SetGlobal(name)
|
state.SetGlobal(name)
|
||||||
@ -77,7 +77,7 @@ func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
|||||||
s.mu.RUnlock()
|
s.mu.RUnlock()
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
logger.ServerCont("Sandbox setup complete")
|
logger.Server("Sandbox setup complete")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@ -300,6 +300,16 @@ local session = {
|
|||||||
return env.ctx.session.id
|
return env.ctx.session.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end,
|
||||||
|
|
||||||
|
get_all = function()
|
||||||
|
local env = getfenv(2)
|
||||||
|
|
||||||
|
if env.ctx and env.ctx.session then
|
||||||
|
return env.ctx.session.data
|
||||||
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
46
core/utils/color/color.go
Normal file
46
core/utils/color/color.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package color
|
||||||
|
|
||||||
|
// ANSI color codes
|
||||||
|
const (
|
||||||
|
Reset = "\033[0m"
|
||||||
|
Red = "\033[31m"
|
||||||
|
Green = "\033[32m"
|
||||||
|
Yellow = "\033[33m"
|
||||||
|
Blue = "\033[34m"
|
||||||
|
Purple = "\033[35m"
|
||||||
|
Cyan = "\033[36m"
|
||||||
|
White = "\033[37m"
|
||||||
|
Gray = "\033[90m"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Apply adds color to text if useColors is true
|
||||||
|
func Apply(text, color string, useColors bool) string {
|
||||||
|
if useColors {
|
||||||
|
return color + text + Reset
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip removes ANSI color codes from a string
|
||||||
|
func Strip(s string) string {
|
||||||
|
result := ""
|
||||||
|
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
|
||||||
|
}
|
@ -7,8 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"Moonshark/core/metadata"
|
|
||||||
"Moonshark/core/utils/config"
|
"Moonshark/core/utils/config"
|
||||||
|
"Moonshark/core/utils/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ComponentStats holds stats from various system components
|
// ComponentStats holds stats from various system components
|
@ -38,23 +38,17 @@ var levelProps = map[int]struct {
|
|||||||
tag string
|
tag string
|
||||||
color string
|
color string
|
||||||
}{
|
}{
|
||||||
LevelDebug: {"DEBUG", colorCyan},
|
LevelDebug: {"D", colorCyan},
|
||||||
LevelInfo: {" INFO", colorBlue},
|
LevelInfo: {"I", colorBlue},
|
||||||
LevelWarning: {" WARN", colorYellow},
|
LevelWarning: {"W", colorYellow},
|
||||||
LevelError: {"ERROR", colorRed},
|
LevelError: {"E", colorRed},
|
||||||
LevelServer: {" SYS", colorGreen},
|
LevelServer: {"S", colorGreen},
|
||||||
LevelFatal: {"FATAL", colorPurple},
|
LevelFatal: {"F", colorPurple},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time format for log messages
|
// Time format for log messages
|
||||||
const timeFormat = "15:04:05"
|
const timeFormat = "15:04:05"
|
||||||
|
|
||||||
// Default rate limiting settings
|
|
||||||
const (
|
|
||||||
defaultMaxLogs = 1000 // Max logs per second before rate limiting
|
|
||||||
defaultRateLimitTime = 10 * time.Second // How long to pause during rate limiting
|
|
||||||
)
|
|
||||||
|
|
||||||
// Single global logger instance with mutex for safe initialization
|
// Single global logger instance with mutex for safe initialization
|
||||||
var (
|
var (
|
||||||
globalLogger *Logger
|
globalLogger *Logger
|
||||||
@ -70,17 +64,6 @@ type Logger struct {
|
|||||||
showTimestamp bool // Whether to show timestamp
|
showTimestamp bool // Whether to show timestamp
|
||||||
mu sync.Mutex // Mutex for thread-safe writing
|
mu sync.Mutex // Mutex for thread-safe writing
|
||||||
debugMode atomic.Bool // Force debug logging regardless of level
|
debugMode atomic.Bool // Force debug logging regardless of level
|
||||||
indentCache string // Cached indent string for continuations
|
|
||||||
indentSize int // Size of the indent for continuations
|
|
||||||
lastLevel int // Last log level used, for continuations
|
|
||||||
|
|
||||||
// Simple rate limiting
|
|
||||||
logCount atomic.Int64 // Number of logs in current window
|
|
||||||
logCountStart atomic.Int64 // Start time of current counting window
|
|
||||||
rateLimited atomic.Bool // Whether we're currently rate limited
|
|
||||||
rateLimitUntil atomic.Int64 // Timestamp when rate limiting ends
|
|
||||||
maxLogsPerSec int64 // Maximum logs per second before limiting
|
|
||||||
limitDuration time.Duration // How long to pause logging when rate limited
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLogger returns the global logger instance, creating it if needed
|
// GetLogger returns the global logger instance, creating it if needed
|
||||||
@ -105,17 +88,8 @@ func newLogger(minLevel int, useColors bool, showTimestamp bool) *Logger {
|
|||||||
useColors: useColors,
|
useColors: useColors,
|
||||||
timeFormat: timeFormat,
|
timeFormat: timeFormat,
|
||||||
showTimestamp: showTimestamp,
|
showTimestamp: showTimestamp,
|
||||||
maxLogsPerSec: defaultMaxLogs,
|
|
||||||
limitDuration: defaultRateLimitTime,
|
|
||||||
lastLevel: -1, // Initialize to invalid level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize counters
|
|
||||||
logger.resetCounters()
|
|
||||||
|
|
||||||
// Calculate the base indent size
|
|
||||||
logger.updateIndentCache()
|
|
||||||
|
|
||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,14 +98,6 @@ func New(minLevel int, useColors bool, showTimestamp bool) *Logger {
|
|||||||
return newLogger(minLevel, useColors, showTimestamp)
|
return newLogger(minLevel, useColors, showTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resetCounters resets the rate limiting counters
|
|
||||||
func (l *Logger) resetCounters() {
|
|
||||||
l.logCount.Store(0)
|
|
||||||
l.logCountStart.Store(time.Now().Unix())
|
|
||||||
l.rateLimited.Store(false)
|
|
||||||
l.rateLimitUntil.Store(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutput changes the output destination
|
// SetOutput changes the output destination
|
||||||
func (l *Logger) SetOutput(w io.Writer) {
|
func (l *Logger) SetOutput(w io.Writer) {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
@ -144,47 +110,21 @@ func (l *Logger) TimeFormat() string {
|
|||||||
return l.timeFormat
|
return l.timeFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateIndentCache recalculates and updates the indent cache
|
// SetTimeFormat changes the time format string
|
||||||
func (l *Logger) updateIndentCache() {
|
|
||||||
tagWidth := 7
|
|
||||||
|
|
||||||
if l.showTimestamp {
|
|
||||||
// Format: "15:04:05 DEBUG "
|
|
||||||
timeWidth := len(time.Now().Format(l.timeFormat))
|
|
||||||
l.indentSize = timeWidth + 1 + tagWidth + 1
|
|
||||||
} else {
|
|
||||||
// Format: "DEBUG "
|
|
||||||
l.indentSize = tagWidth + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
l.indentCache = strings.Repeat(" ", l.indentSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTimeFormat changes the time format string and updates the indent cache
|
|
||||||
func (l *Logger) SetTimeFormat(format string) {
|
func (l *Logger) SetTimeFormat(format string) {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.timeFormat = format
|
l.timeFormat = format
|
||||||
l.updateIndentCache()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableTimestamp enables timestamp display
|
// EnableTimestamp enables timestamp display
|
||||||
func (l *Logger) EnableTimestamp() {
|
func (l *Logger) EnableTimestamp() {
|
||||||
l.mu.Lock()
|
|
||||||
defer l.mu.Unlock()
|
|
||||||
|
|
||||||
l.showTimestamp = true
|
l.showTimestamp = true
|
||||||
l.updateIndentCache()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableTimestamp disables timestamp display
|
// DisableTimestamp disables timestamp display
|
||||||
func (l *Logger) DisableTimestamp() {
|
func (l *Logger) DisableTimestamp() {
|
||||||
l.mu.Lock()
|
|
||||||
defer l.mu.Unlock()
|
|
||||||
|
|
||||||
l.showTimestamp = false
|
l.showTimestamp = false
|
||||||
l.updateIndentCache()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLevel changes the minimum log level
|
// SetLevel changes the minimum log level
|
||||||
@ -217,202 +157,16 @@ func (l *Logger) IsDebugEnabled() bool {
|
|||||||
return l.debugMode.Load()
|
return l.debugMode.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeMessage writes a formatted log message directly to the writer
|
// applyColor applies color to text if colors are enabled
|
||||||
func (l *Logger) writeMessage(level int, message string, rawMode bool, continuation bool) {
|
func (l *Logger) applyColor(text, color string) string {
|
||||||
var logLine string
|
|
||||||
|
|
||||||
if rawMode {
|
|
||||||
// Raw mode - message is already formatted, just append newline
|
|
||||||
logLine = message + "\n"
|
|
||||||
} else if continuation {
|
|
||||||
// Continuation format - just indent and message
|
|
||||||
if l.useColors {
|
if l.useColors {
|
||||||
// For colored output, use the color of the last level
|
return color + text + colorReset
|
||||||
props := levelProps[l.lastLevel]
|
|
||||||
logLine = fmt.Sprintf("%s%s%s\n",
|
|
||||||
l.indentCache, props.color, message+colorReset)
|
|
||||||
} else {
|
|
||||||
logLine = fmt.Sprintf("%s%s\n", l.indentCache, message)
|
|
||||||
}
|
}
|
||||||
} else {
|
return text
|
||||||
// Standard format with level tag and optional timestamp
|
|
||||||
props := levelProps[level]
|
|
||||||
|
|
||||||
if l.showTimestamp {
|
|
||||||
now := time.Now().Format(l.timeFormat)
|
|
||||||
|
|
||||||
if l.useColors {
|
|
||||||
logLine = fmt.Sprintf("%s%s%s %s[%s]%s %s\n",
|
|
||||||
colorGray, now, colorReset, props.color, props.tag, colorReset, message)
|
|
||||||
} else {
|
|
||||||
logLine = fmt.Sprintf("%s [%s] %s\n",
|
|
||||||
now, props.tag, message)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No timestamp, just level tag and message
|
|
||||||
if l.useColors {
|
|
||||||
logLine = fmt.Sprintf("%s[%s]%s %s\n",
|
|
||||||
props.color, props.tag, colorReset, message)
|
|
||||||
} else {
|
|
||||||
logLine = fmt.Sprintf("[%s] %s\n",
|
|
||||||
props.tag, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the level for continuations
|
|
||||||
l.lastLevel = level
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synchronously write the log message
|
|
||||||
l.mu.Lock()
|
|
||||||
_, _ = fmt.Fprint(l.writer, logLine)
|
|
||||||
|
|
||||||
// For fatal errors, ensure we sync immediately
|
|
||||||
if level == LevelFatal {
|
|
||||||
if f, ok := l.writer.(*os.File); ok {
|
|
||||||
_ = f.Sync()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkRateLimit checks if we should rate limit logging
|
// stripAnsiColors removes ANSI color codes from a string
|
||||||
// Returns true if the message should be logged, false if it should be dropped
|
func stripAnsiColors(s string) string {
|
||||||
func (l *Logger) checkRateLimit(level int) bool {
|
|
||||||
// High priority messages are never rate limited
|
|
||||||
if level >= LevelWarning {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we're currently in a rate-limited period
|
|
||||||
if l.rateLimited.Load() {
|
|
||||||
now := time.Now().Unix()
|
|
||||||
limitUntil := l.rateLimitUntil.Load()
|
|
||||||
|
|
||||||
if now >= limitUntil {
|
|
||||||
// Rate limiting period is over
|
|
||||||
l.rateLimited.Store(false)
|
|
||||||
l.resetCounters()
|
|
||||||
} else {
|
|
||||||
// Still in rate limiting period, drop the message
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not rate limited, check if we should start rate limiting
|
|
||||||
count := l.logCount.Add(1)
|
|
||||||
|
|
||||||
// Check if we need to reset the counter for a new second
|
|
||||||
now := time.Now().Unix()
|
|
||||||
start := l.logCountStart.Load()
|
|
||||||
if now > start {
|
|
||||||
// New second, reset counter
|
|
||||||
l.logCount.Store(1) // Count this message
|
|
||||||
l.logCountStart.Store(now)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we've exceeded our threshold
|
|
||||||
if count > l.maxLogsPerSec {
|
|
||||||
// Start rate limiting
|
|
||||||
l.rateLimited.Store(true)
|
|
||||||
l.rateLimitUntil.Store(now + int64(l.limitDuration.Seconds()))
|
|
||||||
|
|
||||||
// Log a warning about rate limiting
|
|
||||||
l.writeMessage(LevelServer,
|
|
||||||
fmt.Sprintf("Rate limiting logger temporarily due to high demand (%d logs/sec exceeded)", count),
|
|
||||||
false, false)
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check rate limiting - always log high priority messages
|
|
||||||
if !l.checkRateLimit(level) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format message
|
|
||||||
var message string
|
|
||||||
if len(args) > 0 {
|
|
||||||
message = fmt.Sprintf(format, args...)
|
|
||||||
} else {
|
|
||||||
message = format
|
|
||||||
}
|
|
||||||
|
|
||||||
l.writeMessage(level, message, false, false)
|
|
||||||
|
|
||||||
// Exit on fatal errors
|
|
||||||
if level == LevelFatal {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// continuation handles continuation log messages (messages that continue from a previous log)
|
|
||||||
func (l *Logger) continuation(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()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check rate limiting
|
|
||||||
if !l.checkRateLimit(level) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format message
|
|
||||||
var message string
|
|
||||||
if len(args) > 0 {
|
|
||||||
message = fmt.Sprintf(format, args...)
|
|
||||||
} else {
|
|
||||||
message = format
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the continuation format
|
|
||||||
l.writeMessage(level, message, false, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check rate limiting
|
|
||||||
if !l.checkRateLimit(LevelInfo) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var message string
|
|
||||||
if len(args) > 0 {
|
|
||||||
message = fmt.Sprintf(format, args...)
|
|
||||||
} else {
|
|
||||||
message = format
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't apply colors if disabled
|
|
||||||
if !l.useColors {
|
|
||||||
// Strip ANSI color codes if colors are disabled
|
|
||||||
message = removeAnsiColors(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.writeMessage(LevelInfo, message, true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple helper to remove ANSI color codes
|
|
||||||
func removeAnsiColors(s string) string {
|
|
||||||
result := ""
|
result := ""
|
||||||
inEscape := false
|
inEscape := false
|
||||||
|
|
||||||
@ -435,68 +189,129 @@ func removeAnsiColors(s string) string {
|
|||||||
return result
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronously write the log message
|
||||||
|
l.mu.Lock()
|
||||||
|
_, _ = fmt.Fprint(l.writer, logLine)
|
||||||
|
|
||||||
|
// For fatal errors, ensure we sync immediately
|
||||||
|
if level == LevelFatal {
|
||||||
|
if f, ok := l.writer.(*os.File); ok {
|
||||||
|
_ = f.Sync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format message
|
||||||
|
var message string
|
||||||
|
if len(args) > 0 {
|
||||||
|
message = fmt.Sprintf(format, args...)
|
||||||
|
} else {
|
||||||
|
message = format
|
||||||
|
}
|
||||||
|
|
||||||
|
l.writeMessage(level, message, false)
|
||||||
|
|
||||||
|
// Exit on fatal errors
|
||||||
|
if level == LevelFatal {
|
||||||
|
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...)
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
// Debug logs a debug message
|
// Debug logs a debug message
|
||||||
func (l *Logger) Debug(format string, args ...any) {
|
func (l *Logger) Debug(format string, args ...any) {
|
||||||
l.log(LevelDebug, format, args...)
|
l.log(LevelDebug, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DebugCont logs a debug message as a continuation of the previous log
|
|
||||||
func (l *Logger) DebugCont(format string, args ...any) {
|
|
||||||
l.continuation(LevelDebug, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info logs an informational message
|
// Info logs an informational message
|
||||||
func (l *Logger) Info(format string, args ...any) {
|
func (l *Logger) Info(format string, args ...any) {
|
||||||
l.log(LevelInfo, format, args...)
|
l.log(LevelInfo, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoCont logs an informational message as a continuation of the previous log
|
|
||||||
func (l *Logger) InfoCont(format string, args ...any) {
|
|
||||||
l.continuation(LevelInfo, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warning logs a warning message
|
// Warning logs a warning message
|
||||||
func (l *Logger) Warning(format string, args ...any) {
|
func (l *Logger) Warning(format string, args ...any) {
|
||||||
l.log(LevelWarning, format, args...)
|
l.log(LevelWarning, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WarningCont logs a warning message as a continuation of the previous log
|
|
||||||
func (l *Logger) WarningCont(format string, args ...any) {
|
|
||||||
l.continuation(LevelWarning, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error logs an error message
|
// Error logs an error message
|
||||||
func (l *Logger) Error(format string, args ...any) {
|
func (l *Logger) Error(format string, args ...any) {
|
||||||
l.log(LevelError, format, args...)
|
l.log(LevelError, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorCont logs an error message as a continuation of the previous log
|
|
||||||
func (l *Logger) ErrorCont(format string, args ...any) {
|
|
||||||
l.continuation(LevelError, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal logs a fatal error message and exits
|
// Fatal logs a fatal error message and exits
|
||||||
func (l *Logger) Fatal(format string, args ...any) {
|
func (l *Logger) Fatal(format string, args ...any) {
|
||||||
l.log(LevelFatal, format, args...)
|
l.log(LevelFatal, format, args...)
|
||||||
// No need for os.Exit here as it's handled in log()
|
// No need for os.Exit here as it's handled in log()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FatalCont logs a fatal error message as a continuation of the previous log and exits
|
|
||||||
func (l *Logger) FatalCont(format string, args ...any) {
|
|
||||||
l.continuation(LevelFatal, format, args...)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server logs a server message
|
// Server logs a server message
|
||||||
func (l *Logger) Server(format string, args ...any) {
|
func (l *Logger) Server(format string, args ...any) {
|
||||||
l.log(LevelServer, format, args...)
|
l.log(LevelServer, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerCont logs a server message as a continuation of the previous log
|
|
||||||
func (l *Logger) ServerCont(format string, args ...any) {
|
|
||||||
l.continuation(LevelServer, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global helper functions that use the global logger
|
// Global helper functions that use the global logger
|
||||||
|
|
||||||
// Debug logs a debug message to the global logger
|
// Debug logs a debug message to the global logger
|
||||||
@ -504,61 +319,31 @@ func Debug(format string, args ...any) {
|
|||||||
GetLogger().Debug(format, args...)
|
GetLogger().Debug(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DebugCont logs a debug message as a continuation of the previous log to the global logger
|
|
||||||
func DebugCont(format string, args ...any) {
|
|
||||||
GetLogger().DebugCont(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info logs an informational message to the global logger
|
// Info logs an informational message to the global logger
|
||||||
func Info(format string, args ...any) {
|
func Info(format string, args ...any) {
|
||||||
GetLogger().Info(format, args...)
|
GetLogger().Info(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoCont logs an informational message as a continuation of the previous log to the global logger
|
|
||||||
func InfoCont(format string, args ...any) {
|
|
||||||
GetLogger().InfoCont(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warning logs a warning message to the global logger
|
// Warning logs a warning message to the global logger
|
||||||
func Warning(format string, args ...any) {
|
func Warning(format string, args ...any) {
|
||||||
GetLogger().Warning(format, args...)
|
GetLogger().Warning(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WarningCont logs a warning message as a continuation of the previous log to the global logger
|
|
||||||
func WarningCont(format string, args ...any) {
|
|
||||||
GetLogger().WarningCont(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error logs an error message to the global logger
|
// Error logs an error message to the global logger
|
||||||
func Error(format string, args ...any) {
|
func Error(format string, args ...any) {
|
||||||
GetLogger().Error(format, args...)
|
GetLogger().Error(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorCont logs an error message as a continuation of the previous log to the global logger
|
|
||||||
func ErrorCont(format string, args ...any) {
|
|
||||||
GetLogger().ErrorCont(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal logs a fatal error message to the global logger and exits
|
// Fatal logs a fatal error message to the global logger and exits
|
||||||
func Fatal(format string, args ...any) {
|
func Fatal(format string, args ...any) {
|
||||||
GetLogger().Fatal(format, args...)
|
GetLogger().Fatal(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FatalCont logs a fatal error message as a continuation of the previous log to the global logger and exits
|
|
||||||
func FatalCont(format string, args ...any) {
|
|
||||||
GetLogger().FatalCont(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server logs a server message to the global logger
|
// Server logs a server message to the global logger
|
||||||
func Server(format string, args ...any) {
|
func Server(format string, args ...any) {
|
||||||
GetLogger().Server(format, args...)
|
GetLogger().Server(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerCont logs a server message as a continuation of the previous log to the global logger
|
|
||||||
func ServerCont(format string, args ...any) {
|
|
||||||
GetLogger().ServerCont(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogRaw logs a raw message to the global logger
|
// LogRaw logs a raw message to the global logger
|
||||||
func LogRaw(format string, args ...any) {
|
func LogRaw(format string, args ...any) {
|
||||||
GetLogger().LogRaw(format, args...)
|
GetLogger().LogRaw(format, args...)
|
||||||
@ -620,9 +405,9 @@ func (l *Logger) LogSpacer() {
|
|||||||
restSpacer := strings.Repeat("-", 20) // Fixed width for the rest
|
restSpacer := strings.Repeat("-", 20) // Fixed width for the rest
|
||||||
|
|
||||||
if l.useColors {
|
if l.useColors {
|
||||||
spacer = fmt.Sprintf("%s%s%s %s%s%s %s\n",
|
timeStr := l.applyColor(strings.Repeat("-", timeWidth), colorGray)
|
||||||
colorGray, strings.Repeat("-", timeWidth), colorReset,
|
tagStr := l.applyColor(tagSpacer, colorCyan)
|
||||||
colorCyan, tagSpacer, colorReset, restSpacer)
|
spacer = fmt.Sprintf("%s %s %s\n", timeStr, tagStr, restSpacer)
|
||||||
} else {
|
} else {
|
||||||
spacer = fmt.Sprintf("%s %s %s\n",
|
spacer = fmt.Sprintf("%s %s %s\n",
|
||||||
strings.Repeat("-", timeWidth), tagSpacer, restSpacer)
|
strings.Repeat("-", timeWidth), tagSpacer, restSpacer)
|
||||||
@ -633,8 +418,8 @@ func (l *Logger) LogSpacer() {
|
|||||||
restSpacer := strings.Repeat("-", 20) // Fixed width for the rest
|
restSpacer := strings.Repeat("-", 20) // Fixed width for the rest
|
||||||
|
|
||||||
if l.useColors {
|
if l.useColors {
|
||||||
spacer = fmt.Sprintf("%s%s%s %s\n",
|
tagStr := l.applyColor(tagSpacer, colorCyan)
|
||||||
colorCyan, tagSpacer, colorReset, restSpacer)
|
spacer = fmt.Sprintf("%s %s\n", tagStr, restSpacer)
|
||||||
} else {
|
} else {
|
||||||
spacer = fmt.Sprintf("%s %s\n", tagSpacer, restSpacer)
|
spacer = fmt.Sprintf("%s %s\n", tagSpacer, restSpacer)
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user