Files
godoxy/internal/logging/accesslog/shared_file_handle_test.go
yusing 92bf8b196f refactor(accesslog): restructure access logging; enhance console output format
Major refactoring of the access logging infrastructure to improve code organization and add proper console/stdout logging support.

- Renamed `Writer` interface to `File` and consolidated with `SupportRotate`
- Renamed `Log(req, res)` to `LogRequest(req, res)` for clarity
- Added new `ConsoleLogger` with zerolog console writer for formatted stdout output
- Moved type definitions to new `types.go` file
- Changed buffer handling from `[]byte` returns to `*bytes.Buffer` parameters
- Renamed internal files for clarity (`access_logger.go` → `file_access_logger.go`)
- Fixed fileserver access logging timing: moved logging after handler execution with defer
- Correct response handling in Fileserver
- Remove deprecated field `buffer_size`
- Simplify and removed unnecessary code

All callers have been updated to use the new APIs.
2026-01-19 15:00:37 +08:00

103 lines
2.1 KiB
Go

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()
}