package accesslog import ( "fmt" "math/rand/v2" "net/http" "os" "runtime" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/yusing/goutils/task" "golang.org/x/sync/errgroup" ) func TestConcurrentFileLoggersShareSameAccessLogIO(t *testing.T) { cfg := DefaultRequestLoggerConfig() cfg.Path = "test.log" loggerCount := runtime.GOMAXPROCS(0) accessLogIOs := make([]File, loggerCount) // make test log file file, err := os.Create(cfg.Path) assert.NoError(t, err) file.Close() t.Cleanup(func() { assert.NoError(t, os.Remove(cfg.Path)) }) var errs errgroup.Group for i := range loggerCount { errs.Go(func() error { file, err := OpenFile(cfg.Path) if err != nil { return err } accessLogIOs[i] = file return nil }) } err = errs.Wait() assert.NoError(t, err) firstIO := accessLogIOs[0] for _, io := range accessLogIOs { assert.Equal(t, firstIO, io) } } func TestConcurrentAccessLoggerLogAndFlush(t *testing.T) { for _, buffered := range []bool{false, true} { t.Run(fmt.Sprintf("buffered=%t", buffered), func(t *testing.T) { file := NewMockFile(buffered) cfg := DefaultRequestLoggerConfig() parent := task.RootTask("test", false) loggerCount := runtime.GOMAXPROCS(0) logCountPerLogger := 10 loggers := make([]AccessLogger, loggerCount) for i := range loggerCount { loggers[i] = NewFileAccessLogger(parent, file, cfg) } req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil) resp := &http.Response{StatusCode: http.StatusOK} var wg sync.WaitGroup for _, logger := range loggers { wg.Go(func() { concurrentLog(logger, req, resp, logCountPerLogger) }) } wg.Wait() for _, logger := range loggers { logger.Close() } expected := loggerCount * logCountPerLogger actual := file.NumLines() assert.Equal(t, expected, actual) }) } } func concurrentLog(logger AccessLogger, req *http.Request, resp *http.Response, n int) { var wg sync.WaitGroup for range n { wg.Go(func() { logger.LogRequest(req, resp) if rand.IntN(2) == 0 { logger.Flush() } }) } wg.Wait() }