Files
2026-07-13 13:39:47 +08:00

177 lines
4.4 KiB
Go

// Package logger provides log management functionality.
// Supports concurrent output to console and file with automatic log rotation.
package logger
import (
"os"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var (
// Logger is the global logger instance.
Logger *zap.Logger
// Sugar is the convenience sugared logger instance.
Sugar *zap.SugaredLogger
)
// Config holds the logger configuration.
type Config struct {
// Level is the log level: debug, info, warn, error
Level string
// LogDir is the directory where log files are stored.
LogDir string
// LogFileName is the name of the log file.
LogFileName string
// MaxSize is the maximum size of a single log file in MB.
MaxSize int
// MaxBackups is the maximum number of old log files to retain.
MaxBackups int
// MaxAge is the maximum number of days to retain old log files.
MaxAge int
// Compress indicates whether to compress old log files.
Compress bool
// Console indicates whether to output logs to the console.
Console bool
}
// DefaultConfig returns the default logger configuration.
func DefaultConfig() Config {
return Config{
Level: "info",
LogDir: "./logs",
LogFileName: "mediago-core.log",
MaxSize: 100, // 100MB
MaxBackups: 5, // retain 5 backups
MaxAge: 30, // 30 days
Compress: true, // compress old logs
Console: true, // output to console
}
}
// Init initializes the logging system.
func Init(cfg Config) error {
// Create the log directory
if err := os.MkdirAll(cfg.LogDir, 0755); err != nil {
return err
}
// Parse the log level
level := parseLevel(cfg.Level)
// Configure the encoder
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalColorLevelEncoder, // colored level output
EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 time format
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder, // short path format
}
// File encoder config (no color)
fileEncoderConfig := encoderConfig
fileEncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
// Console encoder (colored output)
consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)
// File encoder (JSON format)
fileEncoder := zapcore.NewJSONEncoder(fileEncoderConfig)
// Configure log writer: split by day by default
fileWriter := zapcore.AddSync(newDailyRotateWriter(cfg))
// Create multiple output cores
var cores []zapcore.Core
// File output core
cores = append(cores, zapcore.NewCore(fileEncoder, fileWriter, level))
// Console output core
if cfg.Console {
consoleWriter := zapcore.Lock(os.Stdout)
cores = append(cores, zapcore.NewCore(consoleEncoder, consoleWriter, level))
}
// Create the logger
core := zapcore.NewTee(cores...)
Logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
Sugar = Logger.Sugar()
return nil
}
// parseLevel parses a log level string into a zapcore.Level.
func parseLevel(levelStr string) zapcore.Level {
switch levelStr {
case "debug":
return zapcore.DebugLevel
case "info":
return zapcore.InfoLevel
case "warn":
return zapcore.WarnLevel
case "error":
return zapcore.ErrorLevel
default:
return zapcore.InfoLevel
}
}
// Sync flushes any buffered log entries.
func Sync() {
if Logger != nil {
_ = Logger.Sync()
}
if Sugar != nil {
_ = Sugar.Sync()
}
}
// Convenience methods - structured logging
func Debug(msg string, fields ...zap.Field) {
Logger.Debug(msg, fields...)
}
func Info(msg string, fields ...zap.Field) {
Logger.Info(msg, fields...)
}
func Warn(msg string, fields ...zap.Field) {
Logger.Warn(msg, fields...)
}
func Error(msg string, fields ...zap.Field) {
Logger.Error(msg, fields...)
}
func Fatal(msg string, fields ...zap.Field) {
Logger.Fatal(msg, fields...)
}
// Convenience methods - formatted logging
func Debugf(template string, args ...interface{}) {
Sugar.Debugf(template, args...)
}
func Infof(template string, args ...interface{}) {
Sugar.Infof(template, args...)
}
func Warnf(template string, args ...interface{}) {
Sugar.Warnf(template, args...)
}
func Errorf(template string, args ...interface{}) {
Sugar.Errorf(template, args...)
}
func Fatalf(template string, args ...interface{}) {
Sugar.Fatalf(template, args...)
}