Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
438a424d94 | ||
|
|
9c1dc3d7bc | ||
|
|
07ee637572 | ||
|
|
19a691c5af | ||
|
|
49a1ee715b |
25
CHANGELOG.md
25
CHANGELOG.md
@ -1,5 +1,30 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.5.5] - 2026-06-05
|
||||||
|
- **规范化: Warning 日志行为对齐**:
|
||||||
|
- ** Convention 对齐**: 移除了 `Warning` 级别日志自动附带堆栈信息(`CallStacks`)的逻辑。
|
||||||
|
- **性能优化**: 减少了在产生非故障告警时的堆栈获取开销。
|
||||||
|
- **设计初衷**: 遵循主流日志规范,仅在 `Error` 级别及以上保留自动堆栈,使告警信息更整洁。
|
||||||
|
|
||||||
|
## [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` 语义版本。
|
||||||
|
|
||||||
## [1.3.2] - 2026-05-13
|
## [1.3.2] - 2026-05-13
|
||||||
- **功能增强: 引入摩擦消除工具 `As`**:
|
- **功能增强: 引入摩擦消除工具 `As`**:
|
||||||
- **泛型支持**: 新增全局泛型函数 `log.As[T](v T, err error) T`,仿照 `cast.As` 设计,自动记录错误并返回零值,极大简化了带 error 返回值的函数链式调用。
|
- **泛型支持**: 新增全局泛型函数 `log.As[T](v T, err error) T`,仿照 `cast.As` 设计,自动记录错误并返回零值,极大简化了带 error 返回值的函数链式调用。
|
||||||
|
|||||||
@ -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 副本
|
||||||
|
|||||||
53
logger.go
53
logger.go
@ -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) {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
10
writer.go
10
writer.go
@ -25,7 +25,7 @@ type logPayload struct {
|
|||||||
// writerService manages the background writing of log entries.
|
// writerService manages the background writing of log entries.
|
||||||
type writerService struct {
|
type writerService struct {
|
||||||
Running atomic.Bool
|
Running atomic.Bool
|
||||||
StopChan chan bool
|
StopChan chan struct{}
|
||||||
LogChannel chan logPayload
|
LogChannel chan logPayload
|
||||||
Dropped atomic.Uint64
|
Dropped atomic.Uint64
|
||||||
Writers atomic.Value // []Writer
|
Writers atomic.Value // []Writer
|
||||||
@ -90,10 +90,13 @@ func GetDroppedLogs() uint64 {
|
|||||||
|
|
||||||
// Start implements starter.Service interface.
|
// Start implements starter.Service interface.
|
||||||
func (s *writerService) Start(_ context.Context, _ *Logger) error {
|
func (s *writerService) Start(_ context.Context, _ *Logger) error {
|
||||||
|
s.FilesLock.Lock()
|
||||||
|
defer s.FilesLock.Unlock()
|
||||||
if !s.Running.CompareAndSwap(false, true) {
|
if !s.Running.CompareAndSwap(false, true) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s.StopChan = make(chan bool)
|
s.LogChannel = make(chan logPayload, 10000)
|
||||||
|
s.StopChan = make(chan struct{})
|
||||||
go s.writerRunner()
|
go s.writerRunner()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -163,6 +166,9 @@ func (s *writerService) processLog(payload logPayload) {
|
|||||||
payload.writer.Log(payload.entry, payload.buf)
|
payload.writer.Log(payload.entry, payload.buf)
|
||||||
} else if payload.file != nil {
|
} else if payload.file != nil {
|
||||||
payload.file.Write(time.Now(), payload.buf)
|
payload.file.Write(time.Now(), payload.buf)
|
||||||
|
} else {
|
||||||
|
// 默认回退到控制台打印
|
||||||
|
fmt.Println(Viewable(string(payload.buf)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user