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:
parent
2ef5fc3b7f
commit
5a5f733cb4
@ -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)
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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 {
|
for k, v := range fields {
|
||||||
event.Str(k, v)
|
event.Str(k, v)
|
||||||
}
|
}
|
||||||
event.Msg(msg)
|
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 {
|
|
||||||
extra += fmt.Sprintf(" %s=%s", k, v)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(l.writer, "[%s] INFO %s%s\n", timestamp, msg, extra)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 级别日志记录器。
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user