mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
236 lines
5.5 KiB
Go
236 lines
5.5 KiB
Go
package log
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// LogLevel represents the severity level of a log message
|
|
type LogLevel int
|
|
|
|
const (
|
|
// DebugLevel for detailed troubleshooting
|
|
DebugLevel LogLevel = iota
|
|
// InfoLevel for general operational information
|
|
InfoLevel
|
|
// WarnLevel for potentially harmful situations
|
|
WarnLevel
|
|
// ErrorLevel for error events that might still allow the application to continue
|
|
ErrorLevel
|
|
// FatalLevel for severe error events that will lead the application to abort
|
|
FatalLevel
|
|
)
|
|
|
|
// String returns the string representation of the log level
|
|
func (l LogLevel) String() string {
|
|
switch l {
|
|
case DebugLevel:
|
|
return "DEBUG"
|
|
case InfoLevel:
|
|
return "INFO"
|
|
case WarnLevel:
|
|
return "WARN"
|
|
case ErrorLevel:
|
|
return "ERROR"
|
|
case FatalLevel:
|
|
return "FATAL"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
// Field represents a key-value pair for structured logging
|
|
type Field struct {
|
|
Key string
|
|
Value interface{}
|
|
}
|
|
|
|
// F creates a new Field
|
|
func F(key string, value interface{}) Field {
|
|
return Field{Key: key, Value: value}
|
|
}
|
|
|
|
// Logger provides structured logging capabilities
|
|
type Logger struct {
|
|
level LogLevel
|
|
writer io.Writer
|
|
fields []Field
|
|
context map[string]interface{}
|
|
}
|
|
|
|
// New creates a new Logger with the specified log level and writer
|
|
func New(level LogLevel, writer io.Writer) *Logger {
|
|
if writer == nil {
|
|
writer = os.Stdout
|
|
}
|
|
return &Logger{
|
|
level: level,
|
|
writer: writer,
|
|
fields: []Field{},
|
|
context: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// Debug logs a message at debug level
|
|
func (l *Logger) Debug(msg string, fields ...Field) {
|
|
if l.level <= DebugLevel {
|
|
l.log(DebugLevel, msg, fields...)
|
|
}
|
|
}
|
|
|
|
// Info logs a message at info level
|
|
func (l *Logger) Info(msg string, fields ...Field) {
|
|
if l.level <= InfoLevel {
|
|
l.log(InfoLevel, msg, fields...)
|
|
}
|
|
}
|
|
|
|
// Warn logs a message at warn level
|
|
func (l *Logger) Warn(msg string, fields ...Field) {
|
|
if l.level <= WarnLevel {
|
|
l.log(WarnLevel, msg, fields...)
|
|
}
|
|
}
|
|
|
|
// Error logs a message at error level
|
|
func (l *Logger) Error(msg string, fields ...Field) {
|
|
if l.level <= ErrorLevel {
|
|
l.log(ErrorLevel, msg, fields...)
|
|
}
|
|
}
|
|
|
|
// Fatal logs a message at fatal level and then calls os.Exit(1)
|
|
func (l *Logger) Fatal(msg string, fields ...Field) {
|
|
if l.level <= FatalLevel {
|
|
l.log(FatalLevel, msg, fields...)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// WithFields returns a new logger with the given fields added
|
|
func (l *Logger) WithFields(fields ...Field) *Logger {
|
|
newLogger := &Logger{
|
|
level: l.level,
|
|
writer: l.writer,
|
|
fields: append(l.fields, fields...),
|
|
context: l.context,
|
|
}
|
|
return newLogger
|
|
}
|
|
|
|
// WithContext returns a new logger with the given context added
|
|
func (l *Logger) WithContext(ctx map[string]interface{}) *Logger {
|
|
newContext := make(map[string]interface{})
|
|
for k, v := range l.context {
|
|
newContext[k] = v
|
|
}
|
|
for k, v := range ctx {
|
|
newContext[k] = v
|
|
}
|
|
|
|
newLogger := &Logger{
|
|
level: l.level,
|
|
writer: l.writer,
|
|
fields: l.fields,
|
|
context: newContext,
|
|
}
|
|
return newLogger
|
|
}
|
|
|
|
// SetLevel sets the log level
|
|
func (l *Logger) SetLevel(level LogLevel) {
|
|
l.level = level
|
|
}
|
|
|
|
// log formats and writes a log message
|
|
func (l *Logger) log(level LogLevel, msg string, fields ...Field) {
|
|
timestamp := time.Now().Format(time.RFC3339)
|
|
|
|
// Get caller information
|
|
_, file, line, ok := runtime.Caller(2)
|
|
caller := "unknown"
|
|
if ok {
|
|
parts := strings.Split(file, "/")
|
|
if len(parts) > 2 {
|
|
caller = fmt.Sprintf("%s:%d", parts[len(parts)-1], line)
|
|
} else {
|
|
caller = fmt.Sprintf("%s:%d", file, line)
|
|
}
|
|
}
|
|
|
|
// Format fields
|
|
allFields := append(l.fields, fields...)
|
|
fieldStr := ""
|
|
for _, field := range allFields {
|
|
fieldStr += fmt.Sprintf(" %s=%v", field.Key, field.Value)
|
|
}
|
|
|
|
// Format context
|
|
contextStr := ""
|
|
for k, v := range l.context {
|
|
contextStr += fmt.Sprintf(" %s=%v", k, v)
|
|
}
|
|
|
|
// Format log message
|
|
logMsg := fmt.Sprintf("%s [%s] %s %s%s%s\n", timestamp, level.String(), caller, msg, fieldStr, contextStr)
|
|
|
|
// Write log message
|
|
_, err := l.writer.Write([]byte(logMsg))
|
|
if err != nil {
|
|
log.Printf("Error writing log message: %v", err)
|
|
}
|
|
}
|
|
|
|
// Global logger instance
|
|
var defaultLogger = New(InfoLevel, os.Stdout)
|
|
|
|
// SetDefaultLogger sets the global logger instance
|
|
func SetDefaultLogger(logger *Logger) {
|
|
defaultLogger = logger
|
|
}
|
|
|
|
// SetDefaultLevel sets the log level for the default logger
|
|
func SetDefaultLevel(level LogLevel) {
|
|
defaultLogger.SetLevel(level)
|
|
}
|
|
|
|
// LogDebug logs a message at debug level using the default logger
|
|
func LogDebug(msg string, fields ...Field) {
|
|
defaultLogger.Debug(msg, fields...)
|
|
}
|
|
|
|
// LogInfo logs a message at info level using the default logger
|
|
func LogInfo(msg string, fields ...Field) {
|
|
defaultLogger.Info(msg, fields...)
|
|
}
|
|
|
|
// LogWarn logs a message at warn level using the default logger
|
|
func LogWarn(msg string, fields ...Field) {
|
|
defaultLogger.Warn(msg, fields...)
|
|
}
|
|
|
|
// LogError logs a message at error level using the default logger
|
|
func LogError(msg string, fields ...Field) {
|
|
defaultLogger.Error(msg, fields...)
|
|
}
|
|
|
|
// LogFatal logs a message at fatal level using the default logger and then calls os.Exit(1)
|
|
func LogFatal(msg string, fields ...Field) {
|
|
defaultLogger.Fatal(msg, fields...)
|
|
}
|
|
|
|
// WithFields returns a new logger with the given fields added using the default logger
|
|
func WithFields(fields ...Field) *Logger {
|
|
return defaultLogger.WithFields(fields...)
|
|
}
|
|
|
|
// WithContext returns a new logger with the given context added using the default logger
|
|
func WithContext(ctx map[string]interface{}) *Logger {
|
|
return defaultLogger.WithContext(ctx)
|
|
}
|