tercul-backend/logger/logger.go
Damir Mukimov 4957117cb6 Initial commit: Tercul Go project with comprehensive architecture
- Core Go application with GraphQL API using gqlgen
- Comprehensive data models for literary works, authors, translations
- Repository pattern with caching layer
- Authentication and authorization system
- Linguistics analysis capabilities with multiple adapters
- Vector search integration with Weaviate
- Docker containerization support
- Python data migration and analysis scripts
- Clean architecture with proper separation of concerns
- Production-ready configuration and middleware
- Proper .gitignore excluding vendor/, database files, and build artifacts
2025-08-13 07:42:32 +02:00

236 lines
5.5 KiB
Go

package logger
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)
}