You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
5.0 KiB
Go

// Package logging provides structured logging for the KnowFoolery application.
package logging
import (
"io"
"os"
"time"
"github.com/rs/zerolog"
)
// Config holds the configuration for the logger.
type Config struct {
// Level is the zerolog level string (e.g. debug, info, warn, error).
Level string
// Environment controls output format: development uses console, others use JSON.
Environment string
// ServiceName is injected in every log event as the service field.
ServiceName string
// Version is injected in every log event as the version field.
Version string
// Output defaults to stdout when nil.
Output io.Writer
}
// DefaultConfig returns a default configuration.
func DefaultConfig() Config {
return Config{
Level: "info",
Environment: "development",
ServiceName: "knowfoolery",
Version: "0.0.0",
Output: os.Stdout,
}
}
// Logger wraps zerolog.Logger with application-specific methods.
type Logger struct {
logger zerolog.Logger
}
// NewLogger creates a new Logger.
func NewLogger(config Config) *Logger {
// Parse log level
level, err := zerolog.ParseLevel(config.Level)
if err != nil {
level = zerolog.InfoLevel
}
// Set global level
zerolog.SetGlobalLevel(level)
zerolog.TimeFieldFormat = time.RFC3339Nano
var logger zerolog.Logger
output := config.Output
if output == nil {
output = os.Stdout
}
if config.Environment == "development" {
// Human-readable console output for development
logger = zerolog.New(zerolog.ConsoleWriter{
Out: output,
TimeFormat: "15:04:05",
}).With().Timestamp().Logger()
} else {
// JSON output for production
logger = zerolog.New(output).With().Timestamp().Logger()
}
// Add service metadata to every log line.
logger = logger.With().
Str("service", config.ServiceName).
Str("version", config.Version).
Str("environment", config.Environment).
Logger()
return &Logger{logger: logger}
}
// Debug logs a debug message.
func (l *Logger) Debug(msg string) {
l.logger.Debug().Msg(msg)
}
// Info logs an info message.
func (l *Logger) Info(msg string) {
l.logger.Info().Msg(msg)
}
// Warn logs a warning message.
func (l *Logger) Warn(msg string) {
l.logger.Warn().Msg(msg)
}
// Error logs an error message.
func (l *Logger) Error(msg string) {
l.logger.Error().Msg(msg)
}
// Fatal logs a fatal message and exits.
func (l *Logger) Fatal(msg string) {
l.logger.Fatal().Msg(msg)
}
// WithError adds an error to the log entry.
func (l *Logger) WithError(err error) *Logger {
return &Logger{
logger: l.logger.With().Err(err).Logger(),
}
}
// WithField adds a field to the log entry.
func (l *Logger) WithField(key string, value interface{}) *Logger {
return &Logger{
logger: l.logger.With().Interface(key, value).Logger(),
}
}
// WithFields adds multiple fields to the log entry.
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
ctx := l.logger.With()
for k, v := range fields {
ctx = ctx.Interface(k, v)
}
return &Logger{
logger: ctx.Logger(),
}
}
// GameEvent logs a game-related event.
func (l *Logger) GameEvent(event string, gameSessionID, userID string, properties map[string]interface{}) {
l.logger.Info().
Str("event_type", "game").
Str("event", event).
Str("game_session_id", gameSessionID).
Str("user_id", userID).
Fields(properties).
Msg("Game event occurred")
}
// APIRequest logs an API request.
func (l *Logger) APIRequest(method, path string, statusCode int, duration time.Duration, userID string) {
l.logger.Info().
Str("event_type", "api_request").
Str("method", method).
Str("path", path).
Int("status_code", statusCode).
Dur("duration_ms", duration).
Str("user_id", userID).
Msg("API request processed")
}
// DatabaseOperation logs a database operation.
func (l *Logger) DatabaseOperation(operation, table string, duration time.Duration, rowsAffected int64) {
l.logger.Debug().
Str("event_type", "database").
Str("operation", operation).
Str("table", table).
Dur("duration_ms", duration).
Int64("rows_affected", rowsAffected).
Msg("Database operation completed")
}
// AuthenticationEvent logs an authentication event.
func (l *Logger) AuthenticationEvent(event, userID, userType string, success bool, details map[string]string) {
logEntry := l.logger.Info()
if !success {
logEntry = l.logger.Warn()
}
logEntry.
Str("event_type", "authentication").
Str("event", event).
Str("user_id", userID).
Str("user_type", userType).
Bool("success", success).
Fields(details).
Msg("Authentication event")
}
// SecurityEvent logs a security event.
func (l *Logger) SecurityEvent(event, userID, ipAddress string, severity string, details map[string]interface{}) {
l.logger.Warn().
Str("event_type", "security").
Str("event", event).
Str("user_id", userID).
Str("ip_address", ipAddress).
Str("severity", severity).
Fields(details).
Msg("Security event detected")
}
// Zerolog returns the underlying zerolog.Logger for advanced usage.
func (l *Logger) Zerolog() zerolog.Logger {
return l.logger
}