//go:build race // +build race package loggerutils // import "github.com/docker/docker/daemon/logger/loggerutils" import ( "context" "fmt" "io" "path/filepath" "testing" "time" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/pkg/tailfile" "golang.org/x/sync/errgroup" "gotest.tools/v3/assert" ) func TestConcurrentLogging(t *testing.T) { const ( containers = 5 loggers = 3 // loggers per container messages = 50 // messages per logger capacity = 256 maxFiles = 3 compress = true ) getTailReader := func(ctx context.Context, r SizeReaderAt, lines int) (io.Reader, int, error) { return tailfile.NewTailReader(ctx, r, lines) } createDecoder := func(io.Reader) Decoder { return dummyDecoder{} } marshal := func(msg *logger.Message) ([]byte, error) { return []byte(fmt.Sprintf( "Line=%q Source=%q Timestamp=%v Attrs=%v PLogMetaData=%#v Err=%v", msg.Line, msg.Source, msg.Timestamp, msg.Attrs, msg.PLogMetaData, msg.Err, )), nil } g, ctx := errgroup.WithContext(context.Background()) for ct := 0; ct < containers; ct++ { ct := ct dir := t.TempDir() g.Go(func() (err error) { logfile, err := NewLogFile(filepath.Join(dir, "log.log"), capacity, maxFiles, compress, marshal, createDecoder, 0644, getTailReader) if err != nil { return err } defer func() { if cErr := logfile.Close(); cErr != nil && err == nil { err = cErr } }() lg, ctx := errgroup.WithContext(ctx) for ln := 0; ln < loggers; ln++ { ln := ln lg.Go(func() error { for m := 0; m < messages; m++ { select { case <-ctx.Done(): return ctx.Err() default: } msg := logger.NewMessage() msg.Line = append(msg.Line, fmt.Sprintf("container=%v logger=%v msg=%v", ct, ln, m)...) msg.Source = "stdout" msg.Timestamp = time.Now() msg.Attrs = append(msg.Attrs, backend.LogAttr{Key: "foo", Value: "bar"}) msg.PLogMetaData = &backend.PartialLogMetaData{ID: fmt.Sprintf("%v %v %v", ct, ln, m), Ordinal: 1, Last: true} if err := logfile.WriteLogEntry(msg); err != nil { return err } } return nil }) } return lg.Wait() }) } assert.NilError(t, g.Wait()) }