Compare commits

...

4 Commits
v1.5.1 ... main

Author SHA1 Message Date
AI Engineer
438a424d94 feat(log): normalize Warning level behavior, remove automatic stack traces 2026-06-05 11:31:32 +08:00
AI Engineer
9c1dc3d7bc publish v1.5.4 2026-06-05 08:36:23 +08:00
AI Engineer
07ee637572 publish v1.5.3 2026-06-05 08:11:45 +08:00
AI Engineer
19a691c5af publish v1.5.2 2026-06-04 21:17:35 +08:00
4 changed files with 75 additions and 8 deletions

View File

@ -1,10 +1,27 @@
# Changelog # Changelog
## [1.5.1] - 2026-06-04 ## [1.5.5] - 2026-06-05
- **缺陷修复: 异步控制台日志丢失**: - **规范化: Warning 日志行为对齐**:
- **修复**: 在 `writerService.processLog` 中补充了默认的回退逻辑。当处于异步 `starter` 运行模式下,且日志的 `writer``file` 都为 `nil`(即未配置特定的输出目标,本应输出到控制台)时,不再静默丢弃该日志,而是默认回退调用 `fmt.Println` 并在控制台打印渲染后的视图。 - ** Convention 对齐**: 移除了 `Warning` 级别日志自动附带堆栈信息(`CallStacks`)的逻辑。
- **性能优化**: 减少了在产生非故障告警时的堆栈获取开销。
- **设计初衷**: 遵循主流日志规范,仅在 `Error` 级别及以上保留自动堆栈,使告警信息更整洁。
## [1.5.0] - 2026-05-10 ## [1.5.4] - 2026-06-05
- **架构重构: 灵活的日志降噪过滤器**:
- **解耦**: 移除了包内针对特定第三方库(如 SugarDB的硬编码过滤逻辑。
- **新 API**: 引入了 `log.AddStdLogFilter(func(string) bool)` 机制。现在业务包可以在启动时注册自定义的过滤规则,实现精准、按需的控制台噪音治理。
- **缺陷修复**: 补充了 `logger.go` 中缺失的 `sync` 包导入。
## [1.5.3] - 2026-06-04
- **功能增强: 标准库日志全量审计**:
- **实现**: 引入了 `log.RedirectStdLog()` 机制,并在 `DefaultLogger` 初始化时自动启用。该功能会劫持 Go 标准库 `log` 包的所有输出并转发到我们的结构化 Logger。
- **收益**: 确保了如 `SugarDB`, `Bleve` 等第三方依赖产生的原始日志也能自动携带 `serverId` 并符合全栈追踪规范。
## [1.5.2] - 2026-06-04
- **稳定性修复: 异步引擎平滑退出**:
- **修复**: 重构 `writerService` 的通道生命周期管理,彻底解决了在 `starter` 多次启停或服务退出时引发的 `panic: close of closed channel` 致命错误。
## [1.5.1] - 2026-06-04
- **全栈基础设施对齐**: - **全栈基础设施对齐**:
- 将所有内部依赖统一升级至 `v1.5.0` 语义版本。 - 将所有内部依赖统一升级至 `v1.5.0` 语义版本。

View File

@ -13,6 +13,7 @@ func init() {
var conf Config var conf Config
_ = config.Load(&conf, "log") _ = config.Load(&conf, "log")
DefaultLogger = NewLogger(conf) DefaultLogger = NewLogger(conf)
DefaultLogger.RedirectStdLog()
} }
// New 创建带有 traceId 的 Logger 副本 // New 创建带有 traceId 的 Logger 副本

View File

@ -2,15 +2,67 @@ package log
import ( import (
"fmt" "fmt"
"io"
"log" "log"
"os" "os"
"strings" "strings"
"sync"
"time" "time"
"apigo.cc/go/cast" "apigo.cc/go/cast"
"apigo.cc/go/id" "apigo.cc/go/id"
) )
var (
stdLogFilters []func(string) bool
stdLogFiltersLock sync.RWMutex
)
// AddStdLogFilter 注册一个标准库日志过滤器。
// 如果返回 true则该条日志将被静默丢弃。
func AddStdLogFilter(f func(string) bool) {
stdLogFiltersLock.Lock()
defer stdLogFiltersLock.Unlock()
stdLogFilters = append(stdLogFilters, f)
}
// logRedirectWriter 捕获标准 log 包的输出并转发到 structured logger
type logRedirectWriter struct {
logger *Logger
}
func (w *logRedirectWriter) Write(p []byte) (n int, err error) {
msg := strings.TrimSpace(string(p))
if msg == "" {
return len(p), nil
}
stdLogFiltersLock.RLock()
filters := stdLogFilters
stdLogFiltersLock.RUnlock()
for _, f := range filters {
if f(msg) {
return len(p), nil
}
}
w.logger.Info(msg)
return len(p), nil
}
// RedirectStdLog 将标准 log 包的输出重定向到当前 Logger。
// 注意:这会全局修改标准 log 包的配置。
func (logger *Logger) RedirectStdLog() {
log.SetOutput(&logRedirectWriter{logger: logger})
log.SetFlags(0) // 禁用标准 log 的时间前缀,由我们的 logger 统一处理
}
// SetStdLogOutput 仅设置输出目标而不修改 Flags
func SetStdLogOutput(w io.Writer) {
log.SetOutput(w)
}
type Logger struct { type Logger struct {
config Config config Config
level LevelType level LevelType
@ -179,7 +231,6 @@ func (logger *Logger) FillInfo(entry *InfoLog, message string) {
func (logger *Logger) FillWarning(entry *WarningLog, message string) { func (logger *Logger) FillWarning(entry *WarningLog, message string) {
logger.FillBase(&entry.BaseLog, LogTypeWarning) logger.FillBase(&entry.BaseLog, LogTypeWarning)
entry.Warning = message entry.Warning = message
entry.CallStacks = getCallStacks(logger.truncations)
} }
func (logger *Logger) FillError(entry *ErrorLog, message string) { func (logger *Logger) FillError(entry *ErrorLog, message string) {

View File

@ -80,14 +80,12 @@ func (l *InfoLog) Reset() {
type WarningLog struct { type WarningLog struct {
BaseLog BaseLog
Warning string `log:"pos:6,color:yellow,withoutkey:true"` Warning string `log:"pos:6,color:yellow,withoutkey:true"`
CallStacks []string `log:"pos:1001"`
} }
func (l *WarningLog) Reset() { func (l *WarningLog) Reset() {
l.BaseLog.Reset() l.BaseLog.Reset()
l.Warning = "" l.Warning = ""
l.CallStacks = l.CallStacks[:0]
} }
type ErrorLog struct { type ErrorLog struct {