boxes-api/logger.go

214 lines
4.2 KiB
Go
Raw Permalink Normal View History

// logger.go
package main
import (
"fmt"
2024-10-29 16:32:47 +00:00
"io"
"log"
"os"
"runtime"
"time"
)
type Logger struct {
*log.Logger
2024-10-29 16:32:47 +00:00
writers []io.Writer
level LogLevel
}
type LogLevel int
const (
DEBUG LogLevel = iota
INFO
WARN
ERROR
)
var (
instance *Logger
// Map for converting string to LogLevel
logLevelMap = map[string]LogLevel{
"DEBUG": DEBUG,
"INFO": INFO,
"WARN": WARN,
"ERROR": ERROR,
}
)
2024-10-29 16:32:47 +00:00
// Initialize creates a new logger instance with specified configuration
func Initialize(logPath string, logOutput string) (*Logger, error) {
if instance != nil {
return instance, nil
}
2024-10-29 16:32:47 +00:00
var writers []io.Writer
// Handle different output configurations
switch logOutput {
case "stdout":
writers = append(writers, os.Stdout)
case "file":
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %v", err)
}
writers = append(writers, file)
case "both":
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %v", err)
}
writers = append(writers, file, os.Stdout)
default:
// Default to stdout if invalid option provided
writers = append(writers, os.Stdout)
}
// Create multi-writer if we have multiple writers
var writer io.Writer
if len(writers) > 1 {
writer = io.MultiWriter(writers...)
} else {
writer = writers[0]
}
2024-10-29 16:32:47 +00:00
// Create logger instance
logger := &Logger{
2024-10-29 16:32:47 +00:00
Logger: log.New(writer, "", 0),
writers: writers,
level: INFO, // Default to INFO level
}
2024-10-29 16:32:47 +00:00
instance = logger
return logger, nil
}
// SetLogLevel sets the logging level from a string
func (l *Logger) SetLogLevel(levelStr string) error {
level, ok := logLevelMap[levelStr]
if !ok {
return fmt.Errorf("invalid log level: %s. Must be one of: DEBUG, INFO, WARN, ERROR", levelStr)
}
l.level = level
return nil
}
// GetLogLevel returns the current logging level as a string
func (l *Logger) GetLogLevel() string {
for str, level := range logLevelMap {
if level == l.level {
return str
}
}
return "UNKNOWN"
}
// GetLogger returns the singleton logger instance
func GetLogger() *Logger {
return instance
}
2024-10-29 16:32:47 +00:00
// Close closes all writers that implement io.Closer
func (l *Logger) Close() error {
2024-10-29 16:32:47 +00:00
var errs []error
for _, writer := range l.writers {
if closer, ok := writer.(io.Closer); ok {
if err := closer.Close(); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return fmt.Errorf("errors closing writers: %v", errs)
}
return nil
}
func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
if l == nil {
return
}
if level < l.level {
return
}
// Get caller information
_, file, line, _ := runtime.Caller(2)
// Create timestamp
timestamp := time.Now().Format("2006-01-02 15:04:05")
// Create level string
levelStr := "INFO"
switch level {
case DEBUG:
levelStr = "DEBUG"
case WARN:
levelStr = "WARN"
case ERROR:
levelStr = "ERROR"
}
// Format message
message := fmt.Sprintf(format, v...)
// Final log format
logLine := fmt.Sprintf("[%s] [%s] [%s:%d] %s\n",
timestamp,
levelStr,
file,
line,
message,
)
l.Logger.Print(logLine)
}
// Debug logs a debug message
func (l *Logger) Debug(format string, v ...interface{}) {
l.log(DEBUG, format, v...)
}
// Info logs an info message
func (l *Logger) Info(format string, v ...interface{}) {
l.log(INFO, format, v...)
}
// Warn logs a warning message
func (l *Logger) Warn(format string, v ...interface{}) {
l.log(WARN, format, v...)
}
// Error logs an error message
func (l *Logger) Error(format string, v ...interface{}) {
l.log(ERROR, format, v...)
}
// HTTPRequest logs an HTTP request
func (l *Logger) HTTPRequest(method, path, username string, statusCode int) {
l.log(INFO, "HTTP %s %s - User: %s - Status: %d",
method,
path,
username,
statusCode,
)
}
// UserAction logs user actions like login, logout, etc.
func (l *Logger) UserAction(username, action string) {
l.log(INFO, "User Action - Username: %s - Action: %s",
username,
action,
)
}
// DatabaseAction logs database operations
func (l *Logger) DatabaseAction(operation, details string) {
l.log(INFO, "Database Action - Operation: %s - Details: %s",
operation,
details,
)
}