package log import ( "context" "fmt" "os" "path/filepath" "runtime" "time" "github.com/rs/zerolog" "gopkg.in/natefinch/lumberjack.v2" ) type LogType string const ( TypeAccess LogType = "access" TypeBusiness LogType = "business" TypeError LogType = "error" ) var ( logger zerolog.Logger ) // Setup 初始化日志配置 func Setup(conf LogConfig) error { // 确保日志目录存在 if err := os.MkdirAll(filepath.Dir(conf.OutputPath), 0755); err != nil { return fmt.Errorf("create log directory failed: %v", err) } // 配置日志轮转 writer := &lumberjack.Logger{ Filename: conf.OutputPath, MaxSize: conf.MaxSize, // MB MaxAge: conf.MaxAge, // days MaxBackups: conf.MaxBackups, Compress: conf.Compress, } // 设置日志级别 level, err := zerolog.ParseLevel(conf.Level) if err != nil { level = zerolog.InfoLevel } zerolog.SetGlobalLevel(level) // 初始化logger 并添加 app_name logger = zerolog.New(writer).With(). Timestamp(). Str("app_name", conf.AppName). // 添加 app_name Logger() return nil } // log 统一的日志记录函数 func log(ctx context.Context, level zerolog.Level, logType LogType, kv ...interface{}) { if len(kv)%2 != 0 { kv = append(kv, "MISSING") } event := logger.WithLevel(level) // 添加日志类型 event.Str("type", string(logType)) // 添加请求ID if reqID, exists := ctx.Value("request_id").(string); exists { event.Str("request_id", reqID) } // 添加调用位置 if pc, file, line, ok := runtime.Caller(2); ok { event.Str("caller", fmt.Sprintf("%s:%d %s", filepath.Base(file), line, runtime.FuncForPC(pc).Name())) } // 处理key-value对 for i := 0; i < len(kv); i += 2 { key, ok := kv[i].(string) if !ok { continue } value := kv[i+1] switch v := value.(type) { case error: event.AnErr(key, v) case int: event.Int(key, v) case int64: event.Int64(key, v) case float64: event.Float64(key, v) case bool: event.Bool(key, v) case time.Duration: event.Dur(key, v) case time.Time: event.Time(key, v) case []byte: event.Bytes(key, v) case string: event.Str(key, v) default: event.Interface(key, v) } } event.Send() } // Debug 记录调试日志 func Debug(ctx context.Context, kv ...interface{}) { log(ctx, zerolog.DebugLevel, TypeBusiness, kv...) } // Info 记录信息日志 func Info(ctx context.Context, kv ...interface{}) { log(ctx, zerolog.InfoLevel, TypeBusiness, kv...) } // Warn 记录警告日志 func Warn(ctx context.Context, kv ...interface{}) { log(ctx, zerolog.WarnLevel, TypeError, kv...) } // Error 记录错误日志 func Error(ctx context.Context, kv ...interface{}) { log(ctx, zerolog.ErrorLevel, TypeError, kv...) } func Fatal(ctx context.Context, kv ...interface{}) { // 获取错误堆栈 buf := make([]byte, 4096) n := runtime.Stack(buf, false) // 添加堆栈信息到kv newKv := make([]interface{}, 0, len(kv)+2) newKv = append(newKv, kv...) newKv = append(newKv, "stack", string(buf[:n])) log(ctx, zerolog.FatalLevel, TypeError, newKv...) } // Access 记录访问日志 func Access(ctx context.Context, kv ...interface{}) { log(ctx, zerolog.InfoLevel, TypeAccess, kv...) }