refactor(logging): 将日志格式参数从布尔值改为字符串,支持 json/text/console 格式

将 Init 接口从 pretty bool 参数改为 format string 参数,
支持 json(纯 JSON)、text(无颜色 ConsoleWriter)、console(带颜色)三种格式。
简化 AppLogger 实现,统一使用 zerolog 格式化输出。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-17 17:23:49 +08:00
parent 2ef5fc3b7f
commit 5a5f733cb4
5 changed files with 39 additions and 51 deletions

View File

@ -459,7 +459,7 @@ func (a *App) reloadConfig() {
func (a *App) reopenLogs() { func (a *App) reopenLogs() {
// 重新初始化日志系统 // 重新初始化日志系统
if a.cfg != nil { if a.cfg != nil {
logging.Init(a.cfg.Logging.Error.Level, false) logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format)
a.logger = logging.NewAppLogger(&a.cfg.Logging) a.logger = logging.NewAppLogger(&a.cfg.Logging)
} }
a.logger.LogStartup("日志已重新打开", nil) a.logger.LogStartup("日志已重新打开", nil)

View File

@ -150,7 +150,7 @@ func DefaultConfig() *Config {
}, },
}}, }},
Logging: LoggingConfig{ Logging: LoggingConfig{
Format: "json", Format: "text",
Access: AccessLogConfig{ Access: AccessLogConfig{
// 近似 nginx combined 格式 // 近似 nginx combined 格式
// nginx: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" // nginx: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"

View File

@ -51,13 +51,22 @@ var log zerolog.Logger
const formatJSON = "json" const formatJSON = "json"
const formatText = "text"
// Init 初始化日志系统(兼容旧接口)。 // Init 初始化日志系统(兼容旧接口)。
func Init(level string, pretty bool) { func Init(level string, format string) {
l := parseLevel(level) l := parseLevel(level)
if pretty { w := getOutput("") // stdout
log = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(l).With().Timestamp().Logger()
} else { switch format {
log = zerolog.New(os.Stdout).Level(l).With().Timestamp().Logger() case "console":
log = zerolog.New(zerolog.ConsoleWriter{Out: w, TimeFormat: time.RFC3339}).Level(l).With().Timestamp().Logger()
case formatText:
// text 格式:使用 ConsoleWriter 但不带颜色
log = zerolog.New(zerolog.ConsoleWriter{Out: w, TimeFormat: time.RFC3339, NoColor: true}).Level(l).With().Timestamp().Logger()
default:
// json 或空格式:使用 JSON
log = zerolog.New(w).Level(l).With().Timestamp().Logger()
} }
} }
@ -283,7 +292,15 @@ func NewAppLogger(cfg *config.LoggingConfig) *AppLogger {
} }
writer := getOutput(cfg.Error.Path) writer := getOutput(cfg.Error.Path)
errorLog := zerolog.New(writer).Level(parseLevel(cfg.Error.Level)).With().Timestamp().Logger()
var errorLog zerolog.Logger
if format == "text" {
// text 格式:使用 ConsoleWriter无颜色
errorLog = zerolog.New(zerolog.ConsoleWriter{Out: writer, TimeFormat: time.RFC3339, NoColor: true}).Level(parseLevel(cfg.Error.Level)).With().Timestamp().Logger()
} else {
// json 格式
errorLog = zerolog.New(writer).Level(parseLevel(cfg.Error.Level)).With().Timestamp().Logger()
}
return &AppLogger{ return &AppLogger{
format: format, format: format,
@ -294,50 +311,21 @@ func NewAppLogger(cfg *config.LoggingConfig) *AppLogger {
// LogStartup 记录启动消息。 // LogStartup 记录启动消息。
func (l *AppLogger) LogStartup(msg string, fields map[string]string) { func (l *AppLogger) LogStartup(msg string, fields map[string]string) {
if l.format == formatJSON { event := l.errorLog.Info()
event := l.errorLog.Info()
for k, v := range fields {
event.Str(k, v)
}
event.Msg(msg)
return
}
// 纯文本格式
timestamp := time.Now().Format("2006-01-02 15:04:05")
if len(fields) == 0 {
fmt.Fprintf(l.writer, "[%s] INFO %s\n", timestamp, msg)
return
}
// 带字段的文本格式
extra := ""
for k, v := range fields { for k, v := range fields {
extra += fmt.Sprintf(" %s=%s", k, v) event.Str(k, v)
} }
fmt.Fprintf(l.writer, "[%s] INFO %s%s\n", timestamp, msg, extra) event.Msg(msg)
} }
// LogShutdown 记录停止消息。 // LogShutdown 记录停止消息。
func (l *AppLogger) LogShutdown(msg string) { func (l *AppLogger) LogShutdown(msg string) {
if l.format == formatJSON { l.errorLog.Info().Msg(msg)
l.errorLog.Info().Msg(msg)
return
}
timestamp := time.Now().Format("2006-01-02 15:04:05")
fmt.Fprintf(l.writer, "[%s] INFO %s\n", timestamp, msg)
} }
// LogSignal 记录信号处理消息。 // LogSignal 记录信号处理消息。
func (l *AppLogger) LogSignal(sig string, action string) { func (l *AppLogger) LogSignal(sig string, action string) {
if l.format == formatJSON { l.errorLog.Info().Str("signal", sig).Str("action", action).Msg("收到信号")
l.errorLog.Info().Str("signal", sig).Str("action", action).Msg("")
return
}
timestamp := time.Now().Format("2006-01-02 15:04:05")
fmt.Fprintf(l.writer, "[%s] INFO 收到 %s%s\n", timestamp, sig, action)
} }
// Info 返回 Info 级别日志记录器。 // Info 返回 Info 级别日志记录器。

View File

@ -226,17 +226,17 @@ func TestInit(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
level string level string
pretty bool format string
}{ }{
{"debug pretty", "debug", true}, {"debug console", "debug", "console"},
{"info not pretty", "info", false}, {"info json", "info", "json"},
{"warn pretty", "warn", true}, {"warn text", "warn", "text"},
{"error not pretty", "error", false}, {"error json", "error", "json"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) { t.Run(tt.name, func(_ *testing.T) {
Init(tt.level, tt.pretty) Init(tt.level, tt.format)
// 验证全局 logger 已初始化 // 验证全局 logger 已初始化
Debug().Msg("test debug") Debug().Msg("test debug")
Info().Msg("test info") Info().Msg("test info")
@ -247,7 +247,7 @@ func TestInit(t *testing.T) {
} }
func TestGlobalLogFunctions(_ *testing.T) { func TestGlobalLogFunctions(_ *testing.T) {
Init("debug", false) Init("debug", "json")
// 测试全局日志函数 // 测试全局日志函数
Debug().Str("key", "value").Msg("global debug") Debug().Str("key", "value").Msg("global debug")
@ -257,7 +257,7 @@ func TestGlobalLogFunctions(_ *testing.T) {
} }
func TestLogAccessGlobal(_ *testing.T) { func TestLogAccessGlobal(_ *testing.T) {
Init("info", false) Init("info", "json")
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/global-test") ctx.Request.SetRequestURI("/global-test")

View File

@ -417,7 +417,7 @@ func (s *Server) buildLuaMiddlewares(luaCfg *config.LuaMiddlewareConfig) ([]midd
// - 调用前需确保配置已正确加载 // - 调用前需确保配置已正确加载
// - Goroutine池和文件缓存根据配置自动启用 // - Goroutine池和文件缓存根据配置自动启用
func (s *Server) Start() error { func (s *Server) Start() error {
logging.Init(s.config.Logging.Error.Level, true) logging.Init(s.config.Logging.Error.Level, s.config.Logging.Format)
// 记录启动时间 // 记录启动时间
s.startTime = time.Now() s.startTime = time.Now()