package logger import ( "io" "os" "path/filepath" "strings" "testing" ) func TestInit_DualOutput(t *testing.T) { tmpDir := t.TempDir() logPath := filepath.Join(tmpDir, "logs", "sub2api.log") origStdout := os.Stdout origStderr := os.Stderr stdoutR, stdoutW, err := os.Pipe() if err != nil { t.Fatalf("create stdout pipe: %v", err) } stderrR, stderrW, err := os.Pipe() if err != nil { t.Fatalf("create stderr pipe: %v", err) } os.Stdout = stdoutW os.Stderr = stderrW t.Cleanup(func() { os.Stdout = origStdout os.Stderr = origStderr _ = stdoutR.Close() _ = stderrR.Close() _ = stdoutW.Close() _ = stderrW.Close() }) err = Init(InitOptions{ Level: "debug", Format: "json", ServiceName: "sub2api", Environment: "test", Output: OutputOptions{ ToStdout: true, ToFile: true, FilePath: logPath, }, Rotation: RotationOptions{ MaxSizeMB: 10, MaxBackups: 2, MaxAgeDays: 1, }, Sampling: SamplingOptions{Enabled: false}, }) if err != nil { t.Fatalf("Init() error: %v", err) } L().Info("dual-output-info") L().Warn("dual-output-warn") Sync() _ = stdoutW.Close() _ = stderrW.Close() stdoutBytes, _ := io.ReadAll(stdoutR) stderrBytes, _ := io.ReadAll(stderrR) stdoutText := string(stdoutBytes) stderrText := string(stderrBytes) if !strings.Contains(stdoutText, "dual-output-info") { t.Fatalf("stdout missing info log: %s", stdoutText) } if !strings.Contains(stderrText, "dual-output-warn") { t.Fatalf("stderr missing warn log: %s", stderrText) } fileBytes, err := os.ReadFile(logPath) if err != nil { t.Fatalf("read log file: %v", err) } fileText := string(fileBytes) if !strings.Contains(fileText, "dual-output-info") || !strings.Contains(fileText, "dual-output-warn") { t.Fatalf("file missing logs: %s", fileText) } } func TestInit_FileOutputFailureDowngrade(t *testing.T) { origStdout := os.Stdout origStderr := os.Stderr _, stdoutW, err := os.Pipe() if err != nil { t.Fatalf("create stdout pipe: %v", err) } stderrR, stderrW, err := os.Pipe() if err != nil { t.Fatalf("create stderr pipe: %v", err) } os.Stdout = stdoutW os.Stderr = stderrW t.Cleanup(func() { os.Stdout = origStdout os.Stderr = origStderr _ = stdoutW.Close() _ = stderrR.Close() _ = stderrW.Close() }) err = Init(InitOptions{ Level: "info", Format: "json", Output: OutputOptions{ ToStdout: true, ToFile: true, FilePath: filepath.Join(os.DevNull, "logs", "sub2api.log"), }, Rotation: RotationOptions{ MaxSizeMB: 10, MaxBackups: 1, MaxAgeDays: 1, }, }) if err != nil { t.Fatalf("Init() should downgrade instead of failing, got: %v", err) } _ = stderrW.Close() stderrBytes, _ := io.ReadAll(stderrR) if !strings.Contains(string(stderrBytes), "日志文件输出初始化失败") { t.Fatalf("stderr should contain fallback warning, got: %s", string(stderrBytes)) } }