Files

103 lines
3.5 KiB
Go
Raw Permalink Normal View History

2026-09-14 00:14:30 +08:00
package loggerx_test
import (
"context"
"log"
"os"
"path/filepath"
"testing"
"github.com/yuninks/loggerx"
)
// 全局 log 默认不被接管:NewLogger 不该静默改变宿主的全局状态
func TestNoGlobalHijackByDefault(t *testing.T) {
before := log.Writer()
l := loggerx.NewLogger(context.Background(), loggerx.SetDir(t.TempDir()))
defer l.Close()
if got := log.Writer(); got != before {
t.Errorf("默认不该接管全局 log,但现在 log.Writer 变了: %T", got)
}
_ = l
}
// TestGlobalLogTakeoverAndRestore 显式 SetAsGlobalLog 才接管;Close 必须还原成最初的 writer
func TestGlobalLogTakeoverAndRestore(t *testing.T) {
// 全局 log 是进程级状态,先存后恢复,避免污染同包其它测试
original := log.Writer()
t.Cleanup(func() { log.SetOutput(original) })
l := loggerx.NewLogger(context.Background(),
loggerx.SetDir(t.TempDir()),
loggerx.SetAsGlobalLog(),
)
if got, ok := log.Writer().(*loggerx.Logger); !ok || got != l {
t.Fatalf("SetAsGlobalLog 之后 log.Writer 应是该实例,实际 %T", log.Writer())
}
if err := l.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
// Close 会还原成「纪录下来的最初 writer」。
// 若本进程此前已被别的实例接管过(测试场景常见),还原目标不是此刻的 original,
// 所以这里只断言「不再是已关闭的实例」,再断言它可写
if got, ok := log.Writer().(*loggerx.Logger); ok && got == l {
t.Errorf("Close 之后全局 log 仍指向已关闭的实例,后续 stdlib 日志会被静默丢弃")
}
log.Println("after-restore-probe")
}
// TestGlobalLogRestoresOriginalAfterTwoInstances 两个实例先后接管再关闭:
// 全局 log 不能停在「已关闭的实例」上,否则后续 stdlib 日志被静默丢弃
func TestGlobalLogRestoresOriginalAfterTwoInstances(t *testing.T) {
original := log.Writer()
t.Cleanup(func() { log.SetOutput(original) })
l1 := loggerx.NewLogger(context.Background(), loggerx.SetDir(t.TempDir()), loggerx.SetAsGlobalLog())
l2 := loggerx.NewLogger(context.Background(), loggerx.SetDir(t.TempDir()), loggerx.SetAsGlobalLog())
// l2 是当前持有者
if got, ok := log.Writer().(*loggerx.Logger); !ok || got != l2 {
t.Fatal("后接管的实例应该是当前持有者")
}
if err := l2.Close(); err != nil {
t.Fatalf("l2.Close: %v", err)
}
if err := l1.Close(); err != nil {
t.Fatalf("l1.Close: %v", err)
}
// 关键不变式:全局 log 必须是「可用的 writer」,绝不能是已关闭的 Logger
if got, ok := log.Writer().(*loggerx.Logger); ok {
t.Errorf("两次接管并关闭后全局 log 仍指向 Logger(%p),后续 stdlib 日志会被丢弃", got)
}
log.Println("after-restore")
}
// 关闭之后 stdlib log 不能继续写进已关闭的实例
func TestNoHijackAfterClose(t *testing.T) {
dir := t.TempDir()
l := loggerx.NewLogger(context.Background(), loggerx.SetDir(dir), loggerx.SetAsGlobalLog())
if err := l.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
// 直接对已关闭实例写入必须返回 ErrClosed(供调用方判断)
if _, err := l.Write([]byte("x")); err == nil {
t.Error("对已关闭实例写入应返回错误")
} else if err != loggerx.ErrClosed {
t.Logf("对已关闭实例写入返回: %v", err)
}
// 文件句柄应已全部释放(Windows 下能删掉才算真的关了)
files, _ := filepath.Glob(filepath.Join(dir, "*.log"))
for _, f := range files {
if err := os.Remove(f); err != nil {
t.Errorf("Close 后仍无法删除 %s: %v", filepath.Base(f), err)
}
}
}