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() {
// 重新初始化日志系统
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.LogStartup("日志已重新打开", nil)

View File

@ -150,7 +150,7 @@ func DefaultConfig() *Config {
},
}},
Logging: LoggingConfig{
Format: "json",
Format: "text",
Access: AccessLogConfig{
// 近似 nginx combined 格式
// 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 formatText = "text"
// Init 初始化日志系统(兼容旧接口)。
func Init(level string, pretty bool) {
func Init(level string, format string) {
l := parseLevel(level)
if pretty {
log = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(l).With().Timestamp().Logger()
} else {
log = zerolog.New(os.Stdout).Level(l).With().Timestamp().Logger()
w := getOutput("") // stdout
switch format {
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)
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{
format: format,
@ -294,50 +311,21 @@ func NewAppLogger(cfg *config.LoggingConfig) *AppLogger {
// LogStartup 记录启动消息。
func (l *AppLogger) LogStartup(msg string, fields map[string]string) {
if l.format == formatJSON {
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 := ""
event := l.errorLog.Info()
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 记录停止消息。
func (l *AppLogger) LogShutdown(msg string) {
if l.format == formatJSON {
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)
l.errorLog.Info().Msg(msg)
}
// LogSignal 记录信号处理消息。
func (l *AppLogger) LogSignal(sig string, action string) {
if l.format == formatJSON {
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)
l.errorLog.Info().Str("signal", sig).Str("action", action).Msg("收到信号")
}
// Info 返回 Info 级别日志记录器。

View File

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

View File

@ -417,7 +417,7 @@ func (s *Server) buildLuaMiddlewares(luaCfg *config.LuaMiddlewareConfig) ([]midd
// - 调用前需确保配置已正确加载
// - Goroutine池和文件缓存根据配置自动启用
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()