添加自动文件压缩的实现
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package loggerx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// removeWithRetry 遇到「文件被占用」应重试;占用解除后必须删掉
|
||||
func TestRemoveWithRetryHandlesTransientLock(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "locked.log")
|
||||
if err := os.WriteFile(path, []byte("data"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// 先独占打开,制造占用
|
||||
holder, err := os.Open(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
locked := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
closed := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
close(locked)
|
||||
<-release
|
||||
_ = holder.Close()
|
||||
close(closed)
|
||||
}()
|
||||
<-locked
|
||||
|
||||
// 120ms 后释放占用;退避总时长(20+40+80+160+320+640 ≈ 1.2s)要能覆盖
|
||||
go func() {
|
||||
time.Sleep(120 * time.Millisecond)
|
||||
close(release)
|
||||
}()
|
||||
|
||||
start := time.Now()
|
||||
err = removeWithRetry(path, 7)
|
||||
elapsed := time.Since(start)
|
||||
<-closed
|
||||
wg.Wait()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("占用解除后仍未删除成功: %v", err)
|
||||
}
|
||||
if _, statErr := os.Stat(path); !os.IsNotExist(statErr) {
|
||||
t.Errorf("文件仍然存在")
|
||||
}
|
||||
t.Logf("重试 %v 后删除成功", elapsed.Round(time.Millisecond))
|
||||
}
|
||||
|
||||
// 不可重试的错误(文件不存在)应立即返回,不做无谓等待
|
||||
func TestRemoveWithRetryFastFail(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
missing := filepath.Join(dir, "nope.log")
|
||||
|
||||
start := time.Now()
|
||||
err := removeWithRetry(missing, 5)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("删除不存在的文件应该返回错误")
|
||||
}
|
||||
if elapsed > 100*time.Millisecond {
|
||||
t.Errorf("不该对不可重试的错误做退避等待,耗时 %v", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
// isTransientRemoveErr 判定
|
||||
func TestIsTransientRemoveErr(t *testing.T) {
|
||||
cases := []struct {
|
||||
msg string
|
||||
want bool
|
||||
}{
|
||||
{"remove x.log: The process cannot access the file because it is being used by another process.", true},
|
||||
{"sharing violation", true},
|
||||
{"resource busy", true},
|
||||
{"permission denied", false},
|
||||
{"no such file or directory", false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := isTransientRemoveErr(fmt.Errorf("%s", c.msg))
|
||||
if got != c.want {
|
||||
t.Errorf("isTransientRemoveErr(%q) = %v, 期望 %v", c.msg, got, c.want)
|
||||
}
|
||||
}
|
||||
if isTransientRemoveErr(nil) {
|
||||
t.Error("nil 不应判为可重试")
|
||||
}
|
||||
}
|
||||
|
||||
// 压缩完成后,源文件与 .gz 不应长期并存
|
||||
func TestCompressCleansSourceFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := filepath.Join(dir, "a.log")
|
||||
if err := os.WriteFile(src, []byte(strings.Repeat("log line\n", 500)), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := gzipFile(src, 1); err != nil {
|
||||
t.Fatalf("gzipFile: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(src + ".gz"); err != nil {
|
||||
t.Fatalf("没有生成 .gz: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(src); !os.IsNotExist(err) {
|
||||
t.Errorf("源文件没有被删除: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(src + ".gz.tmp"); !os.IsNotExist(err) {
|
||||
t.Errorf("残留了 .gz.tmp")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user