新增日志模块

This commit is contained in:
zyj
2026-03-04 14:10:45 +08:00
parent 2cb4e1a87b
commit 7b01036f7c
5 changed files with 251 additions and 0 deletions

219
internal/logging/logger.go Normal file
View File

@@ -0,0 +1,219 @@
package logging
import (
"fmt"
"io"
"os"
"path/filepath"
"time"
"github.com/rs/zerolog"
)
// Logger 封装 zerolog提供全局日志能力
type Logger struct {
zl zerolog.Logger // 底层 zerolog 实例
level zerolog.Level // 当前日志级别
logFile *os.File // 日志文件句柄(可选)
quiet bool // 静默模式:不输出到控制台
verbose bool // 详细模式:启用 Debug 级别
}
// Options 创建 Logger 时的配置选项
type Options struct {
Level string // 日志级别: trace|debug|info|warn|error|fatal (默认 info)
LogFile string // 日志文件路径(空则不写文件)
Verbose bool // 详细模式,覆盖 Level 为 debug
Quiet bool // 静默模式,禁用控制台输出
NoColor bool // 禁用彩色输出
}
// 全局 Logger 实例,初始化前使用零值安全地丢弃日志
var global = &Logger{zl: zerolog.Nop()}
// G 返回全局 Logger 实例
func G() *Logger {
return global
}
// Init 根据选项初始化全局 Logger应在程序启动时调用一次
func Init(opts Options) (*Logger, error) {
logger, err := New(opts)
if err != nil {
return nil, err
}
global = logger
return logger, nil
}
// New 创建一个新的 Logger 实例
func New(opts Options) (*Logger, error) {
// 解析日志级别
level := zerolog.InfoLevel
if opts.Verbose {
level = zerolog.DebugLevel
} else if opts.Level != "" {
parsed, err := zerolog.ParseLevel(opts.Level)
if err != nil {
return nil, fmt.Errorf("无效的日志级别 %q: %w", opts.Level, err)
}
level = parsed
}
// 构建输出目标Writers
var writers []io.Writer
// 控制台输出(非静默模式时启用)
if !opts.Quiet {
console := zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: "2006-01-02 15:04:05",
NoColor: opts.NoColor,
}
writers = append(writers, console)
}
// 文件输出(指定了日志文件时启用)
var logFile *os.File
if opts.LogFile != "" {
dir := filepath.Dir(opts.LogFile)
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, fmt.Errorf("无法创建日志目录 %s: %w", dir, err)
}
f, err := os.OpenFile(opts.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
if err != nil {
return nil, fmt.Errorf("无法打开日志文件 %s: %w", opts.LogFile, err)
}
logFile = f
// 文件始终使用 JSON 格式,不带颜色
writers = append(writers, f)
}
// 若无任何输出目标,丢弃所有日志
var writer io.Writer
switch len(writers) {
case 0:
writer = io.Discard
case 1:
writer = writers[0]
default:
writer = zerolog.MultiLevelWriter(writers...)
}
zl := zerolog.New(writer).
Level(level).
With().
Timestamp().
Logger()
return &Logger{
zl: zl,
level: level,
logFile: logFile,
quiet: opts.Quiet,
verbose: opts.Verbose,
}, nil
}
// Close 关闭日志文件句柄(如果有的话),应在程序退出时调用
func (l *Logger) Close() {
if l.logFile != nil {
_ = l.logFile.Close()
}
}
// ---- 各级别日志方法 ----
// Trace 输出 Trace 级别日志
func (l *Logger) Trace(msg string) {
l.zl.Trace().Msg(msg)
}
// Tracef 输出格式化 Trace 级别日志
func (l *Logger) Tracef(format string, args ...any) {
l.zl.Trace().Msgf(format, args...)
}
// Debug 输出 Debug 级别日志
func (l *Logger) Debug(msg string) {
l.zl.Debug().Msg(msg)
}
// Debugf 输出格式化 Debug 级别日志
func (l *Logger) Debugf(format string, args ...any) {
l.zl.Debug().Msgf(format, args...)
}
// Info 输出 Info 级别日志
func (l *Logger) Info(msg string) {
l.zl.Info().Msg(msg)
}
// Infof 输出格式化 Info 级别日志
func (l *Logger) Infof(format string, args ...any) {
l.zl.Info().Msgf(format, args...)
}
// Warn 输出 Warn 级别日志
func (l *Logger) Warn(msg string) {
l.zl.Warn().Msg(msg)
}
// Warnf 输出格式化 Warn 级别日志
func (l *Logger) Warnf(format string, args ...any) {
l.zl.Warn().Msgf(format, args...)
}
// Error 输出 Error 级别日志
func (l *Logger) Error(msg string) {
l.zl.Error().Msg(msg)
}
// Errorf 输出格式化 Error 级别日志
func (l *Logger) Errorf(format string, args ...any) {
l.zl.Error().Msgf(format, args...)
}
// Fatal 输出 Fatal 级别日志并终止程序
func (l *Logger) Fatal(msg string) {
l.zl.Fatal().Msg(msg)
}
// Fatalf 输出格式化 Fatal 级别日志并终止程序
func (l *Logger) Fatalf(format string, args ...any) {
l.zl.Fatal().Msgf(format, args...)
}
// ---- 带字段的结构化日志 ----
// WithField 附加一个键值对字段,返回可链式调用的事件
func (l *Logger) WithField(key string, value any) *zerolog.Event {
return l.zl.Info().Any(key, value)
}
// WithError 附加一个错误对象,返回 Error 级别事件
func (l *Logger) WithError(err error) *zerolog.Event {
return l.zl.Error().Err(err)
}
// WithDuration 附加一个耗时字段,常用于性能日志
func (l *Logger) WithDuration(d time.Duration) *zerolog.Event {
return l.zl.Info().Dur("duration", d)
}
// ---- 工具方法 ----
// IsDebug 返回当前是否启用了 Debug 级别
func (l *Logger) IsDebug() bool {
return l.level <= zerolog.DebugLevel
}
// IsQuiet 返回当前是否处于静默模式
func (l *Logger) IsQuiet() bool {
return l.quiet
}
// IsVerbose 返回当前是否处于详细模式
func (l *Logger) IsVerbose() bool {
return l.verbose
}