alter primary keys to uuids and adjust the membership system
This commit is contained in:
@@ -23,6 +23,7 @@ type RouteGroups struct {
|
||||
Config fiber.Router
|
||||
Lookup fiber.Router
|
||||
StateHistory fiber.Router
|
||||
Membership fiber.Router
|
||||
}
|
||||
|
||||
func CheckError(err error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/migrations"
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"os"
|
||||
@@ -33,6 +34,7 @@ func Start(di *dig.Container) {
|
||||
func Migrate(db *gorm.DB) {
|
||||
logging.Info("Migrating database")
|
||||
|
||||
// Run GORM AutoMigrate for all models
|
||||
err := db.AutoMigrate(
|
||||
&model.ApiModel{},
|
||||
&model.Config{},
|
||||
@@ -50,18 +52,27 @@ func Migrate(db *gorm.DB) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logging.Panic("failed to migrate database models")
|
||||
logging.Error("GORM AutoMigrate failed: %v", err)
|
||||
// Don't panic, just log the error as custom migrations may have handled this
|
||||
}
|
||||
|
||||
db.FirstOrCreate(&model.ApiModel{Api: "Works"})
|
||||
|
||||
// Run security migrations - temporarily disabled until migration is fixed
|
||||
// TODO: Implement proper migration system
|
||||
logging.Info("Database migration system needs to be implemented")
|
||||
|
||||
Seed(db)
|
||||
}
|
||||
|
||||
func runMigrations(db *gorm.DB) {
|
||||
logging.Info("Running custom database migrations...")
|
||||
|
||||
// Migration 001: Password security upgrade
|
||||
if err := migrations.RunPasswordSecurityMigration(db); err != nil {
|
||||
logging.Error("Failed to run password security migration: %v", err)
|
||||
// Continue - this migration might not be needed for all setups
|
||||
}
|
||||
|
||||
logging.Info("Custom database migrations completed")
|
||||
}
|
||||
|
||||
func Seed(db *gorm.DB) error {
|
||||
if err := seedTracks(db); err != nil {
|
||||
return err
|
||||
|
||||
184
local/utl/error_handler/controller_error_handler.go
Normal file
184
local/utl/error_handler/controller_error_handler.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package error_handler
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// ControllerErrorHandler provides centralized error handling for controllers
|
||||
type ControllerErrorHandler struct {
|
||||
errorLogger *logging.ErrorLogger
|
||||
}
|
||||
|
||||
// NewControllerErrorHandler creates a new controller error handler instance
|
||||
func NewControllerErrorHandler() *ControllerErrorHandler {
|
||||
return &ControllerErrorHandler{
|
||||
errorLogger: logging.GetErrorLogger(),
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorResponse represents a standardized error response
|
||||
type ErrorResponse struct {
|
||||
Error string `json:"error"`
|
||||
Code int `json:"code,omitempty"`
|
||||
Details map[string]string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// HandleError handles controller errors with logging and standardized responses
|
||||
func (ceh *ControllerErrorHandler) HandleError(c *fiber.Ctx, err error, statusCode int, context ...string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get caller information for logging
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
file = strings.TrimPrefix(file, "acc-server-manager/")
|
||||
|
||||
// Build context string
|
||||
contextStr := ""
|
||||
if len(context) > 0 {
|
||||
contextStr = fmt.Sprintf("[%s] ", strings.Join(context, "|"))
|
||||
}
|
||||
|
||||
// Clean error message (remove null bytes)
|
||||
cleanErrorMsg := strings.ReplaceAll(err.Error(), "\x00", "")
|
||||
|
||||
// Log the error with context
|
||||
ceh.errorLogger.LogWithContext(
|
||||
fmt.Sprintf("CONTROLLER_ERROR [%s:%d]", file, line),
|
||||
"%s%s",
|
||||
contextStr,
|
||||
cleanErrorMsg,
|
||||
)
|
||||
|
||||
// Create standardized error response
|
||||
errorResponse := ErrorResponse{
|
||||
Error: cleanErrorMsg,
|
||||
Code: statusCode,
|
||||
}
|
||||
|
||||
// Add request details if available
|
||||
if c != nil {
|
||||
if errorResponse.Details == nil {
|
||||
errorResponse.Details = make(map[string]string)
|
||||
}
|
||||
errorResponse.Details["method"] = c.Method()
|
||||
errorResponse.Details["path"] = c.Path()
|
||||
errorResponse.Details["ip"] = c.IP()
|
||||
}
|
||||
|
||||
// Return appropriate response based on status code
|
||||
if statusCode >= 500 {
|
||||
// For server errors, don't expose internal details
|
||||
return c.Status(statusCode).JSON(ErrorResponse{
|
||||
Error: "Internal server error",
|
||||
Code: statusCode,
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(statusCode).JSON(errorResponse)
|
||||
}
|
||||
|
||||
// HandleValidationError handles validation errors specifically
|
||||
func (ceh *ControllerErrorHandler) HandleValidationError(c *fiber.Ctx, err error, field string) error {
|
||||
return ceh.HandleError(c, err, fiber.StatusBadRequest, "VALIDATION", field)
|
||||
}
|
||||
|
||||
// HandleDatabaseError handles database-related errors
|
||||
func (ceh *ControllerErrorHandler) HandleDatabaseError(c *fiber.Ctx, err error) error {
|
||||
return ceh.HandleError(c, err, fiber.StatusInternalServerError, "DATABASE")
|
||||
}
|
||||
|
||||
// HandleAuthError handles authentication/authorization errors
|
||||
func (ceh *ControllerErrorHandler) HandleAuthError(c *fiber.Ctx, err error) error {
|
||||
return ceh.HandleError(c, err, fiber.StatusUnauthorized, "AUTH")
|
||||
}
|
||||
|
||||
// HandleNotFoundError handles resource not found errors
|
||||
func (ceh *ControllerErrorHandler) HandleNotFoundError(c *fiber.Ctx, resource string) error {
|
||||
err := fmt.Errorf("%s not found", resource)
|
||||
return ceh.HandleError(c, err, fiber.StatusNotFound, "NOT_FOUND")
|
||||
}
|
||||
|
||||
// HandleBusinessLogicError handles business logic errors
|
||||
func (ceh *ControllerErrorHandler) HandleBusinessLogicError(c *fiber.Ctx, err error) error {
|
||||
return ceh.HandleError(c, err, fiber.StatusBadRequest, "BUSINESS_LOGIC")
|
||||
}
|
||||
|
||||
// HandleServiceError handles service layer errors
|
||||
func (ceh *ControllerErrorHandler) HandleServiceError(c *fiber.Ctx, err error) error {
|
||||
return ceh.HandleError(c, err, fiber.StatusInternalServerError, "SERVICE")
|
||||
}
|
||||
|
||||
// HandleParsingError handles request parsing errors
|
||||
func (ceh *ControllerErrorHandler) HandleParsingError(c *fiber.Ctx, err error) error {
|
||||
return ceh.HandleError(c, err, fiber.StatusBadRequest, "PARSING")
|
||||
}
|
||||
|
||||
// HandleUUIDError handles UUID parsing errors
|
||||
func (ceh *ControllerErrorHandler) HandleUUIDError(c *fiber.Ctx, field string) error {
|
||||
err := fmt.Errorf("invalid %s format", field)
|
||||
return ceh.HandleError(c, err, fiber.StatusBadRequest, "UUID_VALIDATION", field)
|
||||
}
|
||||
|
||||
// Global controller error handler instance
|
||||
var globalErrorHandler *ControllerErrorHandler
|
||||
|
||||
// GetControllerErrorHandler returns the global controller error handler instance
|
||||
func GetControllerErrorHandler() *ControllerErrorHandler {
|
||||
if globalErrorHandler == nil {
|
||||
globalErrorHandler = NewControllerErrorHandler()
|
||||
}
|
||||
return globalErrorHandler
|
||||
}
|
||||
|
||||
// Convenience functions using the global error handler
|
||||
|
||||
// HandleError handles controller errors using the global error handler
|
||||
func HandleError(c *fiber.Ctx, err error, statusCode int, context ...string) error {
|
||||
return GetControllerErrorHandler().HandleError(c, err, statusCode, context...)
|
||||
}
|
||||
|
||||
// HandleValidationError handles validation errors using the global error handler
|
||||
func HandleValidationError(c *fiber.Ctx, err error, field string) error {
|
||||
return GetControllerErrorHandler().HandleValidationError(c, err, field)
|
||||
}
|
||||
|
||||
// HandleDatabaseError handles database errors using the global error handler
|
||||
func HandleDatabaseError(c *fiber.Ctx, err error) error {
|
||||
return GetControllerErrorHandler().HandleDatabaseError(c, err)
|
||||
}
|
||||
|
||||
// HandleAuthError handles auth errors using the global error handler
|
||||
func HandleAuthError(c *fiber.Ctx, err error) error {
|
||||
return GetControllerErrorHandler().HandleAuthError(c, err)
|
||||
}
|
||||
|
||||
// HandleNotFoundError handles not found errors using the global error handler
|
||||
func HandleNotFoundError(c *fiber.Ctx, resource string) error {
|
||||
return GetControllerErrorHandler().HandleNotFoundError(c, resource)
|
||||
}
|
||||
|
||||
// HandleBusinessLogicError handles business logic errors using the global error handler
|
||||
func HandleBusinessLogicError(c *fiber.Ctx, err error) error {
|
||||
return GetControllerErrorHandler().HandleBusinessLogicError(c, err)
|
||||
}
|
||||
|
||||
// HandleServiceError handles service errors using the global error handler
|
||||
func HandleServiceError(c *fiber.Ctx, err error) error {
|
||||
return GetControllerErrorHandler().HandleServiceError(c, err)
|
||||
}
|
||||
|
||||
// HandleParsingError handles parsing errors using the global error handler
|
||||
func HandleParsingError(c *fiber.Ctx, err error) error {
|
||||
return GetControllerErrorHandler().HandleParsingError(c, err)
|
||||
}
|
||||
|
||||
// HandleUUIDError handles UUID errors using the global error handler
|
||||
func HandleUUIDError(c *fiber.Ctx, field string) error {
|
||||
return GetControllerErrorHandler().HandleUUIDError(c, field)
|
||||
}
|
||||
175
local/utl/logging/base.go
Normal file
175
local/utl/logging/base.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
timeFormat = "2006-01-02 15:04:05.000"
|
||||
baseOnce sync.Once
|
||||
baseLogger *BaseLogger
|
||||
)
|
||||
|
||||
// BaseLogger provides the core logging functionality
|
||||
type BaseLogger struct {
|
||||
file *os.File
|
||||
logger *log.Logger
|
||||
mu sync.RWMutex
|
||||
initialized bool
|
||||
}
|
||||
|
||||
// LogLevel represents different logging levels
|
||||
type LogLevel string
|
||||
|
||||
const (
|
||||
LogLevelError LogLevel = "ERROR"
|
||||
LogLevelWarn LogLevel = "WARN"
|
||||
LogLevelInfo LogLevel = "INFO"
|
||||
LogLevelDebug LogLevel = "DEBUG"
|
||||
LogLevelPanic LogLevel = "PANIC"
|
||||
)
|
||||
|
||||
// Initialize creates or gets the singleton base logger instance
|
||||
func InitializeBase(tp string) (*BaseLogger, error) {
|
||||
var err error
|
||||
baseOnce.Do(func() {
|
||||
baseLogger, err = newBaseLogger(tp)
|
||||
})
|
||||
return baseLogger, err
|
||||
}
|
||||
|
||||
func newBaseLogger(tp string) (*BaseLogger, error) {
|
||||
// Ensure logs directory exists
|
||||
if err := os.MkdirAll("logs", 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create logs directory: %v", err)
|
||||
}
|
||||
|
||||
// Open log file with date in name
|
||||
logPath := filepath.Join("logs", fmt.Sprintf("acc-server-%s-%s.log", time.Now().Format("2006-01-02"), tp))
|
||||
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %v", err)
|
||||
}
|
||||
|
||||
// Create multi-writer for both file and console
|
||||
multiWriter := io.MultiWriter(file, os.Stdout)
|
||||
|
||||
// Create base logger
|
||||
logger := &BaseLogger{
|
||||
file: file,
|
||||
logger: log.New(multiWriter, "", 0),
|
||||
initialized: true,
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
// GetBaseLogger returns the singleton base logger instance
|
||||
func GetBaseLogger(tp string) *BaseLogger {
|
||||
if baseLogger == nil {
|
||||
baseLogger, _ = InitializeBase(tp)
|
||||
}
|
||||
return baseLogger
|
||||
}
|
||||
|
||||
// Close closes the log file
|
||||
func (bl *BaseLogger) Close() error {
|
||||
bl.mu.Lock()
|
||||
defer bl.mu.Unlock()
|
||||
|
||||
if bl.file != nil {
|
||||
return bl.file.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Log writes a log entry with the specified level
|
||||
func (bl *BaseLogger) Log(level LogLevel, format string, v ...interface{}) {
|
||||
if bl == nil || !bl.initialized {
|
||||
return
|
||||
}
|
||||
|
||||
bl.mu.RLock()
|
||||
defer bl.mu.RUnlock()
|
||||
|
||||
// Get caller info (skip 2 frames: this function and the calling Log function)
|
||||
_, file, line, _ := runtime.Caller(2)
|
||||
file = filepath.Base(file)
|
||||
|
||||
// Format message
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
|
||||
// Format final log line
|
||||
logLine := fmt.Sprintf("[%s] [%s] [%s:%d] %s",
|
||||
time.Now().Format(timeFormat),
|
||||
string(level),
|
||||
file,
|
||||
line,
|
||||
msg,
|
||||
)
|
||||
|
||||
bl.logger.Println(logLine)
|
||||
}
|
||||
|
||||
// LogWithCaller writes a log entry with custom caller depth
|
||||
func (bl *BaseLogger) LogWithCaller(level LogLevel, callerDepth int, format string, v ...interface{}) {
|
||||
if bl == nil || !bl.initialized {
|
||||
return
|
||||
}
|
||||
|
||||
bl.mu.RLock()
|
||||
defer bl.mu.RUnlock()
|
||||
|
||||
// Get caller info with custom depth
|
||||
_, file, line, _ := runtime.Caller(callerDepth)
|
||||
file = filepath.Base(file)
|
||||
|
||||
// Format message
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
|
||||
// Format final log line
|
||||
logLine := fmt.Sprintf("[%s] [%s] [%s:%d] %s",
|
||||
time.Now().Format(timeFormat),
|
||||
string(level),
|
||||
file,
|
||||
line,
|
||||
msg,
|
||||
)
|
||||
|
||||
bl.logger.Println(logLine)
|
||||
}
|
||||
|
||||
// IsInitialized returns whether the base logger is initialized
|
||||
func (bl *BaseLogger) IsInitialized() bool {
|
||||
if bl == nil {
|
||||
return false
|
||||
}
|
||||
bl.mu.RLock()
|
||||
defer bl.mu.RUnlock()
|
||||
return bl.initialized
|
||||
}
|
||||
|
||||
// RecoverAndLog recovers from panics and logs them
|
||||
func RecoverAndLog() {
|
||||
baseLogger := GetBaseLogger("log")
|
||||
if baseLogger != nil && baseLogger.IsInitialized() {
|
||||
if r := recover(); r != nil {
|
||||
// Get stack trace
|
||||
buf := make([]byte, 4096)
|
||||
n := runtime.Stack(buf, false)
|
||||
stackTrace := string(buf[:n])
|
||||
|
||||
baseLogger.LogWithCaller(LogLevelPanic, 2, "Recovered from panic: %v\nStack Trace:\n%s", r, stackTrace)
|
||||
|
||||
// Re-panic to maintain original behavior if needed
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
154
local/utl/logging/debug.go
Normal file
154
local/utl/logging/debug.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// DebugLogger handles debug-level logging
|
||||
type DebugLogger struct {
|
||||
base *BaseLogger
|
||||
}
|
||||
|
||||
// NewDebugLogger creates a new debug logger instance
|
||||
func NewDebugLogger() *DebugLogger {
|
||||
return &DebugLogger{
|
||||
base: GetBaseLogger("debug"),
|
||||
}
|
||||
}
|
||||
|
||||
// Log writes a debug-level log entry
|
||||
func (dl *DebugLogger) Log(format string, v ...interface{}) {
|
||||
if dl.base != nil {
|
||||
dl.base.Log(LogLevelDebug, format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithContext writes a debug-level log entry with additional context
|
||||
func (dl *DebugLogger) LogWithContext(context string, format string, v ...interface{}) {
|
||||
if dl.base != nil {
|
||||
contextualFormat := fmt.Sprintf("[%s] %s", context, format)
|
||||
dl.base.Log(LogLevelDebug, contextualFormat, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogFunction logs function entry and exit for debugging
|
||||
func (dl *DebugLogger) LogFunction(functionName string, args ...interface{}) {
|
||||
if dl.base != nil {
|
||||
if len(args) > 0 {
|
||||
dl.base.Log(LogLevelDebug, "FUNCTION [%s] called with args: %+v", functionName, args)
|
||||
} else {
|
||||
dl.base.Log(LogLevelDebug, "FUNCTION [%s] called", functionName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogVariable logs variable values for debugging
|
||||
func (dl *DebugLogger) LogVariable(varName string, value interface{}) {
|
||||
if dl.base != nil {
|
||||
dl.base.Log(LogLevelDebug, "VARIABLE [%s]: %+v", varName, value)
|
||||
}
|
||||
}
|
||||
|
||||
// LogState logs application state information
|
||||
func (dl *DebugLogger) LogState(component string, state interface{}) {
|
||||
if dl.base != nil {
|
||||
dl.base.Log(LogLevelDebug, "STATE [%s]: %+v", component, state)
|
||||
}
|
||||
}
|
||||
|
||||
// LogSQL logs SQL queries for debugging
|
||||
func (dl *DebugLogger) LogSQL(query string, args ...interface{}) {
|
||||
if dl.base != nil {
|
||||
if len(args) > 0 {
|
||||
dl.base.Log(LogLevelDebug, "SQL: %s | Args: %+v", query, args)
|
||||
} else {
|
||||
dl.base.Log(LogLevelDebug, "SQL: %s", query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogMemory logs memory usage information
|
||||
func (dl *DebugLogger) LogMemory() {
|
||||
if dl.base != nil {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
dl.base.Log(LogLevelDebug, "MEMORY: Alloc = %d KB, TotalAlloc = %d KB, Sys = %d KB, NumGC = %d",
|
||||
bToKb(m.Alloc), bToKb(m.TotalAlloc), bToKb(m.Sys), m.NumGC)
|
||||
}
|
||||
}
|
||||
|
||||
// LogGoroutines logs current number of goroutines
|
||||
func (dl *DebugLogger) LogGoroutines() {
|
||||
if dl.base != nil {
|
||||
dl.base.Log(LogLevelDebug, "GOROUTINES: %d active", runtime.NumGoroutine())
|
||||
}
|
||||
}
|
||||
|
||||
// LogTiming logs timing information for performance debugging
|
||||
func (dl *DebugLogger) LogTiming(operation string, duration interface{}) {
|
||||
if dl.base != nil {
|
||||
dl.base.Log(LogLevelDebug, "TIMING [%s]: %v", operation, duration)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to convert bytes to kilobytes
|
||||
func bToKb(b uint64) uint64 {
|
||||
return b / 1024
|
||||
}
|
||||
|
||||
// Global debug logger instance
|
||||
var debugLogger *DebugLogger
|
||||
|
||||
// GetDebugLogger returns the global debug logger instance
|
||||
func GetDebugLogger() *DebugLogger {
|
||||
if debugLogger == nil {
|
||||
debugLogger = NewDebugLogger()
|
||||
}
|
||||
return debugLogger
|
||||
}
|
||||
|
||||
// Debug logs a debug-level message using the global debug logger
|
||||
func Debug(format string, v ...interface{}) {
|
||||
GetDebugLogger().Log(format, v...)
|
||||
}
|
||||
|
||||
// DebugWithContext logs a debug-level message with context using the global debug logger
|
||||
func DebugWithContext(context string, format string, v ...interface{}) {
|
||||
GetDebugLogger().LogWithContext(context, format, v...)
|
||||
}
|
||||
|
||||
// DebugFunction logs function entry and exit using the global debug logger
|
||||
func DebugFunction(functionName string, args ...interface{}) {
|
||||
GetDebugLogger().LogFunction(functionName, args...)
|
||||
}
|
||||
|
||||
// DebugVariable logs variable values using the global debug logger
|
||||
func DebugVariable(varName string, value interface{}) {
|
||||
GetDebugLogger().LogVariable(varName, value)
|
||||
}
|
||||
|
||||
// DebugState logs application state information using the global debug logger
|
||||
func DebugState(component string, state interface{}) {
|
||||
GetDebugLogger().LogState(component, state)
|
||||
}
|
||||
|
||||
// DebugSQL logs SQL queries using the global debug logger
|
||||
func DebugSQL(query string, args ...interface{}) {
|
||||
GetDebugLogger().LogSQL(query, args...)
|
||||
}
|
||||
|
||||
// DebugMemory logs memory usage information using the global debug logger
|
||||
func DebugMemory() {
|
||||
GetDebugLogger().LogMemory()
|
||||
}
|
||||
|
||||
// DebugGoroutines logs current number of goroutines using the global debug logger
|
||||
func DebugGoroutines() {
|
||||
GetDebugLogger().LogGoroutines()
|
||||
}
|
||||
|
||||
// DebugTiming logs timing information using the global debug logger
|
||||
func DebugTiming(operation string, duration interface{}) {
|
||||
GetDebugLogger().LogTiming(operation, duration)
|
||||
}
|
||||
101
local/utl/logging/error.go
Normal file
101
local/utl/logging/error.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// ErrorLogger handles error-level logging
|
||||
type ErrorLogger struct {
|
||||
base *BaseLogger
|
||||
}
|
||||
|
||||
// NewErrorLogger creates a new error logger instance
|
||||
func NewErrorLogger() *ErrorLogger {
|
||||
return &ErrorLogger{
|
||||
base: GetBaseLogger("error"),
|
||||
}
|
||||
}
|
||||
|
||||
// Log writes an error-level log entry
|
||||
func (el *ErrorLogger) Log(format string, v ...interface{}) {
|
||||
if el.base != nil {
|
||||
el.base.Log(LogLevelError, format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithContext writes an error-level log entry with additional context
|
||||
func (el *ErrorLogger) LogWithContext(context string, format string, v ...interface{}) {
|
||||
if el.base != nil {
|
||||
contextualFormat := fmt.Sprintf("[%s] %s", context, format)
|
||||
el.base.Log(LogLevelError, contextualFormat, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogError logs an error object with optional message
|
||||
func (el *ErrorLogger) LogError(err error, message ...string) {
|
||||
if el.base != nil && err != nil {
|
||||
if len(message) > 0 {
|
||||
el.base.Log(LogLevelError, "%s: %v", message[0], err)
|
||||
} else {
|
||||
el.base.Log(LogLevelError, "Error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithStackTrace logs an error with stack trace
|
||||
func (el *ErrorLogger) LogWithStackTrace(format string, v ...interface{}) {
|
||||
if el.base != nil {
|
||||
// Get stack trace
|
||||
buf := make([]byte, 4096)
|
||||
n := runtime.Stack(buf, false)
|
||||
stackTrace := string(buf[:n])
|
||||
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
el.base.Log(LogLevelError, "%s\nStack Trace:\n%s", msg, stackTrace)
|
||||
}
|
||||
}
|
||||
|
||||
// LogFatal logs a fatal error and exits the program
|
||||
func (el *ErrorLogger) LogFatal(format string, v ...interface{}) {
|
||||
if el.base != nil {
|
||||
el.base.Log(LogLevelError, "[FATAL] "+format, v...)
|
||||
panic(fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
// Global error logger instance
|
||||
var errorLogger *ErrorLogger
|
||||
|
||||
// GetErrorLogger returns the global error logger instance
|
||||
func GetErrorLogger() *ErrorLogger {
|
||||
if errorLogger == nil {
|
||||
errorLogger = NewErrorLogger()
|
||||
}
|
||||
return errorLogger
|
||||
}
|
||||
|
||||
// Error logs an error-level message using the global error logger
|
||||
func Error(format string, v ...interface{}) {
|
||||
GetErrorLogger().Log(format, v...)
|
||||
}
|
||||
|
||||
// ErrorWithContext logs an error-level message with context using the global error logger
|
||||
func ErrorWithContext(context string, format string, v ...interface{}) {
|
||||
GetErrorLogger().LogWithContext(context, format, v...)
|
||||
}
|
||||
|
||||
// LogError logs an error object using the global error logger
|
||||
func LogError(err error, message ...string) {
|
||||
GetErrorLogger().LogError(err, message...)
|
||||
}
|
||||
|
||||
// ErrorWithStackTrace logs an error with stack trace using the global error logger
|
||||
func ErrorWithStackTrace(format string, v ...interface{}) {
|
||||
GetErrorLogger().LogWithStackTrace(format, v...)
|
||||
}
|
||||
|
||||
// Fatal logs a fatal error and exits the program using the global error logger
|
||||
func Fatal(format string, v ...interface{}) {
|
||||
GetErrorLogger().LogFatal(format, v...)
|
||||
}
|
||||
125
local/utl/logging/info.go
Normal file
125
local/utl/logging/info.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// InfoLogger handles info-level logging
|
||||
type InfoLogger struct {
|
||||
base *BaseLogger
|
||||
}
|
||||
|
||||
// NewInfoLogger creates a new info logger instance
|
||||
func NewInfoLogger() *InfoLogger {
|
||||
return &InfoLogger{
|
||||
base: GetBaseLogger("info"),
|
||||
}
|
||||
}
|
||||
|
||||
// Log writes an info-level log entry
|
||||
func (il *InfoLogger) Log(format string, v ...interface{}) {
|
||||
if il.base != nil {
|
||||
il.base.Log(LogLevelInfo, format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithContext writes an info-level log entry with additional context
|
||||
func (il *InfoLogger) LogWithContext(context string, format string, v ...interface{}) {
|
||||
if il.base != nil {
|
||||
contextualFormat := fmt.Sprintf("[%s] %s", context, format)
|
||||
il.base.Log(LogLevelInfo, contextualFormat, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogStartup logs application startup information
|
||||
func (il *InfoLogger) LogStartup(component string, message string) {
|
||||
if il.base != nil {
|
||||
il.base.Log(LogLevelInfo, "STARTUP [%s]: %s", component, message)
|
||||
}
|
||||
}
|
||||
|
||||
// LogShutdown logs application shutdown information
|
||||
func (il *InfoLogger) LogShutdown(component string, message string) {
|
||||
if il.base != nil {
|
||||
il.base.Log(LogLevelInfo, "SHUTDOWN [%s]: %s", component, message)
|
||||
}
|
||||
}
|
||||
|
||||
// LogOperation logs general operation information
|
||||
func (il *InfoLogger) LogOperation(operation string, details string) {
|
||||
if il.base != nil {
|
||||
il.base.Log(LogLevelInfo, "OPERATION [%s]: %s", operation, details)
|
||||
}
|
||||
}
|
||||
|
||||
// LogStatus logs status changes or updates
|
||||
func (il *InfoLogger) LogStatus(component string, status string) {
|
||||
if il.base != nil {
|
||||
il.base.Log(LogLevelInfo, "STATUS [%s]: %s", component, status)
|
||||
}
|
||||
}
|
||||
|
||||
// LogRequest logs incoming requests
|
||||
func (il *InfoLogger) LogRequest(method string, path string, userAgent string) {
|
||||
if il.base != nil {
|
||||
il.base.Log(LogLevelInfo, "REQUEST [%s %s] User-Agent: %s", method, path, userAgent)
|
||||
}
|
||||
}
|
||||
|
||||
// LogResponse logs outgoing responses
|
||||
func (il *InfoLogger) LogResponse(method string, path string, statusCode int, duration string) {
|
||||
if il.base != nil {
|
||||
il.base.Log(LogLevelInfo, "RESPONSE [%s %s] Status: %d, Duration: %s", method, path, statusCode, duration)
|
||||
}
|
||||
}
|
||||
|
||||
// Global info logger instance
|
||||
var infoLogger *InfoLogger
|
||||
|
||||
// GetInfoLogger returns the global info logger instance
|
||||
func GetInfoLogger() *InfoLogger {
|
||||
if infoLogger == nil {
|
||||
infoLogger = NewInfoLogger()
|
||||
}
|
||||
return infoLogger
|
||||
}
|
||||
|
||||
// Info logs an info-level message using the global info logger
|
||||
func Info(format string, v ...interface{}) {
|
||||
GetInfoLogger().Log(format, v...)
|
||||
}
|
||||
|
||||
// InfoWithContext logs an info-level message with context using the global info logger
|
||||
func InfoWithContext(context string, format string, v ...interface{}) {
|
||||
GetInfoLogger().LogWithContext(context, format, v...)
|
||||
}
|
||||
|
||||
// InfoStartup logs application startup information using the global info logger
|
||||
func InfoStartup(component string, message string) {
|
||||
GetInfoLogger().LogStartup(component, message)
|
||||
}
|
||||
|
||||
// InfoShutdown logs application shutdown information using the global info logger
|
||||
func InfoShutdown(component string, message string) {
|
||||
GetInfoLogger().LogShutdown(component, message)
|
||||
}
|
||||
|
||||
// InfoOperation logs general operation information using the global info logger
|
||||
func InfoOperation(operation string, details string) {
|
||||
GetInfoLogger().LogOperation(operation, details)
|
||||
}
|
||||
|
||||
// InfoStatus logs status changes or updates using the global info logger
|
||||
func InfoStatus(component string, status string) {
|
||||
GetInfoLogger().LogStatus(component, status)
|
||||
}
|
||||
|
||||
// InfoRequest logs incoming requests using the global info logger
|
||||
func InfoRequest(method string, path string, userAgent string) {
|
||||
GetInfoLogger().LogRequest(method, path, userAgent)
|
||||
}
|
||||
|
||||
// InfoResponse logs outgoing responses using the global info logger
|
||||
func InfoResponse(method string, path string, statusCode int, duration string) {
|
||||
GetInfoLogger().LogResponse(method, path, statusCode, duration)
|
||||
}
|
||||
@@ -2,27 +2,26 @@ package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
logger *Logger
|
||||
once sync.Once
|
||||
timeFormat = "2006-01-02 15:04:05.000"
|
||||
// Legacy logger for backward compatibility
|
||||
logger *Logger
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// Logger maintains backward compatibility with existing code
|
||||
type Logger struct {
|
||||
file *os.File
|
||||
logger *log.Logger
|
||||
base *BaseLogger
|
||||
errorLogger *ErrorLogger
|
||||
warnLogger *WarnLogger
|
||||
infoLogger *InfoLogger
|
||||
debugLogger *DebugLogger
|
||||
}
|
||||
|
||||
// Initialize creates or gets the singleton logger instance
|
||||
// This maintains backward compatibility with existing code
|
||||
func Initialize() (*Logger, error) {
|
||||
var err error
|
||||
once.Do(func() {
|
||||
@@ -32,119 +31,183 @@ func Initialize() (*Logger, error) {
|
||||
}
|
||||
|
||||
func newLogger() (*Logger, error) {
|
||||
// Ensure logs directory exists
|
||||
if err := os.MkdirAll("logs", 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create logs directory: %v", err)
|
||||
}
|
||||
|
||||
// Open log file with date in name
|
||||
logPath := filepath.Join("logs", fmt.Sprintf("acc-server-%s.log", time.Now().Format("2006-01-02")))
|
||||
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
||||
// Initialize the base logger
|
||||
baseLogger, err := InitializeBase("log")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create multi-writer for both file and console
|
||||
multiWriter := io.MultiWriter(file, os.Stdout)
|
||||
|
||||
// Create logger with custom prefix
|
||||
// Create the legacy logger wrapper
|
||||
logger := &Logger{
|
||||
file: file,
|
||||
logger: log.New(multiWriter, "", 0),
|
||||
base: baseLogger,
|
||||
errorLogger: NewErrorLogger(),
|
||||
warnLogger: NewWarnLogger(),
|
||||
infoLogger: NewInfoLogger(),
|
||||
debugLogger: NewDebugLogger(),
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
// Close closes the logger
|
||||
func (l *Logger) Close() error {
|
||||
if l.file != nil {
|
||||
return l.file.Close()
|
||||
if l.base != nil {
|
||||
return l.base.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Legacy methods for backward compatibility
|
||||
func (l *Logger) log(level, format string, v ...interface{}) {
|
||||
// Get caller info
|
||||
_, file, line, _ := runtime.Caller(2)
|
||||
file = filepath.Base(file)
|
||||
|
||||
// Format message
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
|
||||
// Format final log line
|
||||
logLine := fmt.Sprintf("[%s] [%s] [%s:%d] %s",
|
||||
time.Now().Format(timeFormat),
|
||||
level,
|
||||
file,
|
||||
line,
|
||||
msg,
|
||||
)
|
||||
|
||||
l.logger.Println(logLine)
|
||||
if l.base != nil {
|
||||
l.base.LogWithCaller(LogLevel(level), 3, format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Info(format string, v ...interface{}) {
|
||||
l.log("INFO", format, v...)
|
||||
if l.infoLogger != nil {
|
||||
l.infoLogger.Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Error(format string, v ...interface{}) {
|
||||
l.log("ERROR", format, v...)
|
||||
if l.errorLogger != nil {
|
||||
l.errorLogger.Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(format string, v ...interface{}) {
|
||||
l.log("WARN", format, v...)
|
||||
if l.warnLogger != nil {
|
||||
l.warnLogger.Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(format string, v ...interface{}) {
|
||||
l.log("DEBUG", format, v...)
|
||||
if l.debugLogger != nil {
|
||||
l.debugLogger.Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Panic(format string) {
|
||||
l.Panic("PANIC " + format)
|
||||
if l.errorLogger != nil {
|
||||
l.errorLogger.LogFatal(format)
|
||||
}
|
||||
}
|
||||
|
||||
// Global convenience functions
|
||||
func Info(format string, v ...interface{}) {
|
||||
// Global convenience functions for backward compatibility
|
||||
// These are now implemented in individual logger files to avoid redeclaration
|
||||
func LegacyInfo(format string, v ...interface{}) {
|
||||
if logger != nil {
|
||||
logger.Info(format, v...)
|
||||
} else {
|
||||
// Fallback to direct logger if legacy logger not initialized
|
||||
GetInfoLogger().Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Error(format string, v ...interface{}) {
|
||||
func LegacyError(format string, v ...interface{}) {
|
||||
if logger != nil {
|
||||
logger.Error(format, v...)
|
||||
} else {
|
||||
// Fallback to direct logger if legacy logger not initialized
|
||||
GetErrorLogger().Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Warn(format string, v ...interface{}) {
|
||||
func LegacyWarn(format string, v ...interface{}) {
|
||||
if logger != nil {
|
||||
logger.Warn(format, v...)
|
||||
} else {
|
||||
// Fallback to direct logger if legacy logger not initialized
|
||||
GetWarnLogger().Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Debug(format string, v ...interface{}) {
|
||||
func LegacyDebug(format string, v ...interface{}) {
|
||||
if logger != nil {
|
||||
logger.Debug(format, v...)
|
||||
} else {
|
||||
// Fallback to direct logger if legacy logger not initialized
|
||||
GetDebugLogger().Log(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Panic(format string) {
|
||||
if logger != nil {
|
||||
logger.Panic(format)
|
||||
} else {
|
||||
// Fallback to direct logger if legacy logger not initialized
|
||||
GetErrorLogger().LogFatal(format)
|
||||
}
|
||||
}
|
||||
|
||||
// RecoverAndLog recovers from panics and logs them
|
||||
func RecoverAndLog() {
|
||||
if logger != nil {
|
||||
logger.Info("Recovering from panic")
|
||||
if r := recover(); r != nil {
|
||||
// Get stack trace
|
||||
buf := make([]byte, 4096)
|
||||
n := runtime.Stack(buf, false)
|
||||
stackTrace := string(buf[:n])
|
||||
// Enhanced logging convenience functions
|
||||
// These provide direct access to specialized logging functions
|
||||
|
||||
logger.log("PANIC", "Recovered from panic: %v\nStack Trace:\n%s", r, stackTrace)
|
||||
}
|
||||
// LogStartup logs application startup information
|
||||
func LogStartup(component string, message string) {
|
||||
GetInfoLogger().LogStartup(component, message)
|
||||
}
|
||||
|
||||
// LogShutdown logs application shutdown information
|
||||
func LogShutdown(component string, message string) {
|
||||
GetInfoLogger().LogShutdown(component, message)
|
||||
}
|
||||
|
||||
// LogOperation logs general operation information
|
||||
func LogOperation(operation string, details string) {
|
||||
GetInfoLogger().LogOperation(operation, details)
|
||||
}
|
||||
|
||||
// LogRequest logs incoming HTTP requests
|
||||
func LogRequest(method string, path string, userAgent string) {
|
||||
GetInfoLogger().LogRequest(method, path, userAgent)
|
||||
}
|
||||
|
||||
// LogResponse logs outgoing HTTP responses
|
||||
func LogResponse(method string, path string, statusCode int, duration string) {
|
||||
GetInfoLogger().LogResponse(method, path, statusCode, duration)
|
||||
}
|
||||
|
||||
// LogSQL logs SQL queries for debugging
|
||||
func LogSQL(query string, args ...interface{}) {
|
||||
GetDebugLogger().LogSQL(query, args...)
|
||||
}
|
||||
|
||||
// LogMemory logs memory usage information
|
||||
func LogMemory() {
|
||||
GetDebugLogger().LogMemory()
|
||||
}
|
||||
|
||||
// LogTiming logs timing information for performance debugging
|
||||
func LogTiming(operation string, duration interface{}) {
|
||||
GetDebugLogger().LogTiming(operation, duration)
|
||||
}
|
||||
|
||||
// GetLegacyLogger returns the legacy logger instance for backward compatibility
|
||||
func GetLegacyLogger() *Logger {
|
||||
if logger == nil {
|
||||
logger, _ = Initialize()
|
||||
}
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
// InitializeLogging initializes all logging components
|
||||
func InitializeLogging() error {
|
||||
// Initialize base logger
|
||||
_, err := InitializeBase("log")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize base logger: %v", err)
|
||||
}
|
||||
|
||||
// Initialize legacy logger for backward compatibility
|
||||
_, err = Initialize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize legacy logger: %v", err)
|
||||
}
|
||||
|
||||
// Log successful initialization
|
||||
Info("Logging system initialized successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
93
local/utl/logging/warn.go
Normal file
93
local/utl/logging/warn.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// WarnLogger handles warn-level logging
|
||||
type WarnLogger struct {
|
||||
base *BaseLogger
|
||||
}
|
||||
|
||||
// NewWarnLogger creates a new warn logger instance
|
||||
func NewWarnLogger() *WarnLogger {
|
||||
return &WarnLogger{
|
||||
base: GetBaseLogger("warn"),
|
||||
}
|
||||
}
|
||||
|
||||
// Log writes a warn-level log entry
|
||||
func (wl *WarnLogger) Log(format string, v ...interface{}) {
|
||||
if wl.base != nil {
|
||||
wl.base.Log(LogLevelWarn, format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithContext writes a warn-level log entry with additional context
|
||||
func (wl *WarnLogger) LogWithContext(context string, format string, v ...interface{}) {
|
||||
if wl.base != nil {
|
||||
contextualFormat := fmt.Sprintf("[%s] %s", context, format)
|
||||
wl.base.Log(LogLevelWarn, contextualFormat, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogDeprecation logs a deprecation warning
|
||||
func (wl *WarnLogger) LogDeprecation(feature string, alternative string) {
|
||||
if wl.base != nil {
|
||||
if alternative != "" {
|
||||
wl.base.Log(LogLevelWarn, "DEPRECATED: %s is deprecated, use %s instead", feature, alternative)
|
||||
} else {
|
||||
wl.base.Log(LogLevelWarn, "DEPRECATED: %s is deprecated", feature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogConfiguration logs configuration-related warnings
|
||||
func (wl *WarnLogger) LogConfiguration(setting string, message string) {
|
||||
if wl.base != nil {
|
||||
wl.base.Log(LogLevelWarn, "CONFIG WARNING [%s]: %s", setting, message)
|
||||
}
|
||||
}
|
||||
|
||||
// LogPerformance logs performance-related warnings
|
||||
func (wl *WarnLogger) LogPerformance(operation string, threshold string, actual string) {
|
||||
if wl.base != nil {
|
||||
wl.base.Log(LogLevelWarn, "PERFORMANCE WARNING [%s]: exceeded threshold %s, actual: %s", operation, threshold, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Global warn logger instance
|
||||
var warnLogger *WarnLogger
|
||||
|
||||
// GetWarnLogger returns the global warn logger instance
|
||||
func GetWarnLogger() *WarnLogger {
|
||||
if warnLogger == nil {
|
||||
warnLogger = NewWarnLogger()
|
||||
}
|
||||
return warnLogger
|
||||
}
|
||||
|
||||
// Warn logs a warn-level message using the global warn logger
|
||||
func Warn(format string, v ...interface{}) {
|
||||
GetWarnLogger().Log(format, v...)
|
||||
}
|
||||
|
||||
// WarnWithContext logs a warn-level message with context using the global warn logger
|
||||
func WarnWithContext(context string, format string, v ...interface{}) {
|
||||
GetWarnLogger().LogWithContext(context, format, v...)
|
||||
}
|
||||
|
||||
// WarnDeprecation logs a deprecation warning using the global warn logger
|
||||
func WarnDeprecation(feature string, alternative string) {
|
||||
GetWarnLogger().LogDeprecation(feature, alternative)
|
||||
}
|
||||
|
||||
// WarnConfiguration logs configuration-related warnings using the global warn logger
|
||||
func WarnConfiguration(setting string, message string) {
|
||||
GetWarnLogger().LogConfiguration(setting, message)
|
||||
}
|
||||
|
||||
// WarnPerformance logs performance-related warnings using the global warn logger
|
||||
func WarnPerformance(operation string, threshold string, actual string) {
|
||||
GetWarnLogger().LogPerformance(operation, threshold, actual)
|
||||
}
|
||||
Reference in New Issue
Block a user