103 lines
3.5 KiB
Go
103 lines
3.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|