Files
go-web-gin/logger/logger.go
2026-03-03 00:58:22 +08:00

201 lines
5.1 KiB
Go

package logger
import (
"context"
"os"
"strings"
"sync"
"git.hujye.com/infrastructure/go-web-gin/config"
"git.hujye.com/infrastructure/go-web-gin/env"
"git.hujye.com/infrastructure/go-web-gin/web"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
type Logger struct {
logger *zap.Logger
}
var (
instance *Logger
once sync.Once
)
// parseLogLevel converts string log level to zapcore.Level
func parseLogLevel(level string) zapcore.Level {
switch strings.ToLower(level) {
case "debug":
return zapcore.DebugLevel
case "info":
return zapcore.InfoLevel
case "warn", "warning":
return zapcore.WarnLevel
case "error":
return zapcore.ErrorLevel
case "fatal":
return zapcore.FatalLevel
default:
return zapcore.InfoLevel
}
}
// GetLogger returns the singleton Logger instance
// Logger configuration is based on RUN_ENV environment variable and config file
func GetLogger() *Logger {
once.Do(func() {
runEnv := env.GetRunEnv()
cfg := config.Get()
// Production and Development use JSON encoder
// Local uses console encoder for better readability
var encoderType EncoderType
if runEnv == env.Production || runEnv == env.Development {
encoderType = JSONEncoder
} else {
encoderType = ConsoleEncoder
}
// Create encoder
encoder := NewEncoder(encoderType)
// Create writer syncs (console + file if enabled)
var writerSyncs zapcore.WriteSyncer
if cfg.Log.OutputToFile {
// Create rotating file writer with lumberjack
fileWriter := &lumberjack.Logger{
Filename: cfg.Log.Filename,
MaxSize: cfg.Log.MaxSize,
MaxBackups: cfg.Log.MaxBackups,
MaxAge: cfg.Log.MaxAge,
Compress: cfg.Log.Compress,
}
// Write to both console and file
writerSyncs = zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stderr), zapcore.AddSync(fileWriter))
} else {
// Write to console only
writerSyncs = zapcore.AddSync(os.Stderr)
}
// Parse log level from config
logLevel := parseLogLevel(cfg.App.LogLevel)
// Create core with custom encoder
core := zapcore.NewCore(
encoder,
writerSyncs,
logLevel,
)
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel))
instance = &Logger{
logger: logger,
}
})
return instance
}
// getUserFields extracts user information from context and returns zap fields
func getUserFields(ctx context.Context) []zap.Field {
if ctx == nil {
return nil
}
fields := []zap.Field{}
if userID := web.GetUserID(ctx); userID != "" {
fields = append(fields, zap.String("user_id", userID))
}
if userName := web.GetUserName(ctx); userName != "" {
fields = append(fields, zap.String("user_name", userName))
}
if trace := web.GetTrace(ctx); trace != "" {
fields = append(fields, zap.String("trace", trace))
}
if fromIP := web.GetFromIP(ctx); fromIP != "" {
fields = append(fields, zap.String("from_ip", fromIP))
}
if province := web.GetProvince(ctx); province != "" {
fields = append(fields, zap.String("province", province))
}
if city := web.GetCity(ctx); city != "" {
fields = append(fields, zap.String("city", city))
}
if len(fields) == 0 {
return nil
}
return fields
}
// Info logs an info message
func (l *Logger) Info(ctx context.Context, msg string, keyValues ...interface{}) {
allFields := getUserFields(ctx)
for i := 0; i < len(keyValues); i += 2 {
if i+1 < len(keyValues) {
if key, ok := keyValues[i].(string); ok {
allFields = append(allFields, zap.Any(key, keyValues[i+1]))
}
}
}
l.logger.Info(msg, allFields...)
}
// Error logs an error message
func (l *Logger) Error(ctx context.Context, msg string, keyValues ...interface{}) {
allFields := getUserFields(ctx)
for i := 0; i < len(keyValues); i += 2 {
if i+1 < len(keyValues) {
if key, ok := keyValues[i].(string); ok {
allFields = append(allFields, zap.Any(key, keyValues[i+1]))
}
}
}
l.logger.Error(msg, allFields...)
}
// Debug logs a debug message
func (l *Logger) Debug(ctx context.Context, msg string, keyValues ...interface{}) {
allFields := getUserFields(ctx)
for i := 0; i < len(keyValues); i += 2 {
if i+1 < len(keyValues) {
if key, ok := keyValues[i].(string); ok {
allFields = append(allFields, zap.Any(key, keyValues[i+1]))
}
}
}
l.logger.Debug(msg, allFields...)
}
// Warn logs a warning message
func (l *Logger) Warn(ctx context.Context, msg string, keyValues ...interface{}) {
allFields := getUserFields(ctx)
for i := 0; i < len(keyValues); i += 2 {
if i+1 < len(keyValues) {
if key, ok := keyValues[i].(string); ok {
allFields = append(allFields, zap.Any(key, keyValues[i+1]))
}
}
}
l.logger.Warn(msg, allFields...)
}
// Fatal logs a fatal message and exits
func (l *Logger) Fatal(ctx context.Context, msg string, keyValues ...interface{}) {
allFields := getUserFields(ctx)
for i := 0; i < len(keyValues); i += 2 {
if i+1 < len(keyValues) {
if key, ok := keyValues[i].(string); ok {
allFields = append(allFields, zap.Any(key, keyValues[i+1]))
}
}
}
l.logger.Fatal(msg, allFields...)
}
// Sync flushes any buffered log entries
func (l *Logger) Sync() error {
return l.logger.Sync()
}