Moonshark/core/utils/debug.go

203 lines
5.8 KiB
Go

package utils
import (
"fmt"
"html/template"
"runtime"
"strings"
"time"
"git.sharkk.net/Sky/Moonshark/core/config"
"git.sharkk.net/Sky/Moonshark/core/metadata"
)
// ComponentStats holds stats from various system components
type ComponentStats struct {
RouteCount int // Number of routes
BytecodeBytes int64 // Total size of bytecode in bytes
ModuleCount int // Number of loaded modules
}
// SystemStats represents system statistics for debugging
type SystemStats struct {
Timestamp time.Time
GoVersion string
GoRoutines int
Memory runtime.MemStats
Components ComponentStats
Version string
Config *config.Config // Configuration information
}
// CollectSystemStats gathers basic system statistics
func CollectSystemStats(cfg *config.Config) SystemStats {
var stats SystemStats
var mem runtime.MemStats
// Collect basic system info
stats.Timestamp = time.Now()
stats.GoVersion = runtime.Version()
stats.GoRoutines = runtime.NumGoroutine()
stats.Version = metadata.Version
stats.Config = cfg
// Collect memory stats
runtime.ReadMemStats(&mem)
stats.Memory = mem
return stats
}
// DebugStatsPage generates an HTML debug stats page
func DebugStatsPage(stats SystemStats) string {
const debugTemplate = `
<!DOCTYPE html>
<html>
<head>
<title>Moonshark</title>
<style>
body {
font-family: sans-serif;
max-width: 900px;
margin: 0 auto;
background-color: #333;
color: white;
}
h1 {
padding: 1rem;
background-color: #4F5B93;
box-shadow: 0 2px 4px 0px rgba(0, 0, 0, 0.2);
margin-top: 0;
}
h2 { margin-top: 0; margin-bottom: 0.5rem; }
table { width: 100%; border-collapse: collapse; }
th { width: 1%; white-space: nowrap; border-right: 1px solid rgba(0, 0, 0, 0.1); }
th, td { text-align: left; padding: 0.75rem 0.5rem; border-bottom: 1px solid #ddd; }
tr:last-child th, tr:last-child td { border-bottom: none; }
table tr:nth-child(even), tbody tr:nth-child(even) { background-color: rgba(0, 0, 0, 0.1); }
.card {
background: #F2F2F2;
color: #333;
border-radius: 4px;
margin-bottom: 1rem;
box-shadow: 0 2px 4px 0px rgba(0, 0, 0, 0.2);
}
.timestamp { color: #999; font-size: 0.9em; margin-bottom: 1rem; }
.section { margin-bottom: 30px; }
</style>
</head>
<body>
<h1>Moonshark</h1>
<div class="timestamp">Generated at: {{.Timestamp.Format "2006-01-02 15:04:05"}}</div>
<div class="section">
<h2>Server</h2>
<div class="card">
<table>
<tr><th>Version</th><td>{{.Version}}</td></tr>
</table>
</div>
</div>
<div class="section">
<h2>System</h2>
<div class="card">
<table>
<tr><th>Go Version</th><td>{{.GoVersion}}</td></tr>
<tr><th>Goroutines</th><td>{{.GoRoutines}}</td></tr>
</table>
</div>
</div>
<div class="section">
<h2>Memory</h2>
<div class="card">
<table>
<tr><th>Allocated</th><td>{{ByteCount .Memory.Alloc}}</td></tr>
<tr><th>Total Allocated</th><td>{{ByteCount .Memory.TotalAlloc}}</td></tr>
<tr><th>System Memory</th><td>{{ByteCount .Memory.Sys}}</td></tr>
<tr><th>GC Cycles</th><td>{{.Memory.NumGC}}</td></tr>
</table>
</div>
</div>
<div class="section">
<h2>LuaRunner</h2>
<div class="card">
<table>
<tr><th>Interpreter</th><td>LuaJIT 2.1 (Lua 5.1)</td></tr>
<tr><th>Active Routes</th><td>{{.Components.RouteCount}}</td></tr>
<tr><th>Bytecode Size</th><td>{{ByteCount .Components.BytecodeBytes}}</td></tr>
<tr><th>Loaded Modules</th><td>{{.Components.ModuleCount}}</td></tr>
</table>
</div>
</div>
<div class="section">
<h2>Config</h2>
<div class="card">
<table>
<tr><th>Port</th><td>{{.Config.Port}}</td></tr>
<tr><th>Debug Mode</th><td>{{.Config.Debug}}</td></tr>
<tr><th>Log Level</th><td>{{.Config.LogLevel}}</td></tr>
<tr><th>Routes Directory</th><td>{{.Config.RoutesDir}}</td></tr>
<tr><th>Static Directory</th><td>{{.Config.StaticDir}}</td></tr>
<tr><th>Override Directory</th><td>{{.Config.OverrideDir}}</td></tr>
<tr><th>Buffer Size</th><td>{{.Config.BufferSize}}</td></tr>
<tr><th>HTTP Logging</th><td>{{.Config.HTTPLoggingEnabled}}</td></tr>
<tr><th>Lib Directories</th><td>{{range .Config.LibDirs}}{{.}}<br>{{end}}</td></tr>
<tr><th>Watch Routes</th><td>{{index .Config.Watchers "routes"}}</td></tr>
<tr><th>Watch Static</th><td>{{index .Config.Watchers "static"}}</td></tr>
<tr><th>Watch Modules</th><td>{{index .Config.Watchers "modules"}}</td></tr>
</table>
</div>
</div>
</body>
</html>
`
// Create a template function map
funcMap := template.FuncMap{
"ByteCount": func(b interface{}) string {
var bytes uint64
// Convert the value to uint64
switch v := b.(type) {
case uint64:
bytes = v
case int64:
bytes = uint64(v)
case int:
bytes = uint64(v)
default:
return "Unknown"
}
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d B", bytes)
}
div, exp := uint64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
},
}
// Parse the template
tmpl, err := template.New("debug").Funcs(funcMap).Parse(debugTemplate)
if err != nil {
return fmt.Sprintf("Error parsing template: %v", err)
}
// Execute the template
var output strings.Builder
if err := tmpl.Execute(&output, stats); err != nil {
return fmt.Sprintf("Error executing template: %v", err)
}
return output.String()
}