package loggerx_test import ( "context" "fmt" "os" "path/filepath" "strings" "testing" "time" "github.com/yuninks/loggerx" ) // 定时刷盘 + 异步写入 + 按大小切割 + 压缩 全部开启时的综合正确性 func TestAllFeaturesCombined(t *testing.T) { dir := t.TempDir() l := loggerx.NewLogger(context.Background(), loggerx.SetDir(dir), loggerx.SetWriteAsync(), loggerx.SetFlushInterval(15*time.Millisecond), loggerx.SetSizeSplit(6*1024), loggerx.SetCompress(true), ) al := l.WriteAsync() const n = 1500 for i := 0; i < n; i++ { // 注意:内容里带了固定后缀,条目文本形如 ALL-0-yyy..., // 断言时用 "ALL--" 前缀而不是 "ALL-\"" al.Infof(context.Background(), "ALL-%d-%s", i, strings.Repeat("y", 40)) // 顺带混入 channel 与 error 事件,确认多句柄并发滚动也正常 if i%50 == 0 { l.Channel("sub").Errorf(context.Background(), "ERR-%d", i) } } if err := l.Close(); err != nil { t.Fatalf("Close: %v", err) } // 主日志(含压缩归档)一条不能少 gzContent := readAllGz(t, dir) logContent := readAllLogs(t, dir) main := logContent + gzContent t.Logf("未压缩 .log 共 %d 字节;.gz 解压后共 %d 字节", len(logContent), len(gzContent)) if len(gzContent) > 0 { n := 200 if len(gzContent) < n { n = len(gzContent) } t.Logf("gz 内容片段: %.200q", gzContent[:n]) } missing := 0 for i := 0; i < n; i++ { if !strings.Contains(main, fmt.Sprintf("ALL-%d-", i)) { missing++ } } if missing > 0 { t.Errorf("综合场景丢失 %d / %d 条主日志", missing, n) } // sub channel 的 error 日志也要完整 _ = filepath.Walk(dir, func(p string, info os.FileInfo, err error) error { if err != nil { return nil } rel, _ := filepath.Rel(dir, p) if info.IsDir() { t.Logf(" [dir] %s", rel) } else { t.Logf(" [file] %s %d 字节", rel, info.Size()) } return nil }) sub := readAllIn(t, dir, "sub") for i := 0; i < n; i += 50 { if !strings.Contains(sub, fmt.Sprintf("ERR-%d", i)) { t.Errorf("sub channel 丢失 ERR-%d", i) } } } // readAllIn 读取某个子目录(channel 目录)下的日志与压缩归档 func readAllIn(t *testing.T, dir, sub string) string { t.Helper() target := filepath.Join(dir, sub) if _, err := os.Stat(target); err != nil { t.Fatalf("channel 目录不存在: %v", err) } var sb strings.Builder files, _ := filepath.Glob(filepath.Join(target, "*")) for _, f := range files { st, err := os.Stat(f) if err != nil || st.IsDir() { continue } b, err := os.ReadFile(f) if err != nil { t.Fatalf("读取 %s: %v", f, err) } sb.Write(b) } return sb.String() } // 综合场景下定时刷盘不能把 Close 卡死(刷盘协程与关闭的互锁) func TestAllFeaturesCloseNotBlocked(t *testing.T) { dir := t.TempDir() l := loggerx.NewLogger(context.Background(), loggerx.SetDir(dir), loggerx.SetFlushInterval(1*time.Millisecond), // 刷盘非常频繁,放大竞争 loggerx.SetSizeSplit(2*1024), ) for i := 0; i < 800; i++ { l.Infof(context.Background(), "BLK-%d", i) } done := make(chan error, 1) go func() { done <- l.Close() }() select { case err := <-done: if err != nil { t.Errorf("Close 返回错误: %v", err) } case <-time.After(20 * time.Second): t.Fatal("高频定时刷盘时 Close 卡死") } }