201 lines
5.1 KiB
Go
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()
|
|
}
|