github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/logger/loggertest/logreader.go (about)

     1  package loggertest // import "github.com/docker/docker/daemon/logger/loggertest"
     2  
     3  import (
     4  	"runtime"
     5  	"strings"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/google/go-cmp/cmp/cmpopts"
    12  	"gotest.tools/v3/assert"
    13  	"gotest.tools/v3/assert/opt"
    14  
    15  	"github.com/docker/docker/api/types/backend"
    16  	"github.com/docker/docker/daemon/logger"
    17  )
    18  
    19  type syncer interface {
    20  	// Sync commits the current logs to stable storage such that the most
    21  	// recently-logged message can be immediately read back by a LogReader.
    22  	Sync() error
    23  }
    24  
    25  func syncLogger(t *testing.T, l logger.Logger) {
    26  	if sl, ok := l.(syncer); ok {
    27  		assert.NilError(t, sl.Sync())
    28  	}
    29  }
    30  
    31  // Reader tests that a logger.LogReader implementation behaves as it should.
    32  type Reader struct {
    33  	// Factory returns a function which constructs loggers for the container
    34  	// specified in info. Each call to the returned function must yield a
    35  	// distinct logger instance which can read back logs written by earlier
    36  	// instances.
    37  	Factory func(*testing.T, logger.Info) func(*testing.T) logger.Logger
    38  }
    39  
    40  var compareLog cmp.Options = []cmp.Option{
    41  	// Not all log drivers can round-trip timestamps at full nanosecond
    42  	// precision.
    43  	opt.TimeWithThreshold(time.Millisecond),
    44  	// The json-log driver does not round-trip PLogMetaData and API users do
    45  	// not expect it.
    46  	cmpopts.IgnoreFields(logger.Message{}, "PLogMetaData"),
    47  	cmp.Transformer("string", func(b []byte) string { return string(b) }),
    48  }
    49  
    50  // TestTail tests the behavior of the LogReader's tail implementation.
    51  func (tr Reader) TestTail(t *testing.T) {
    52  	t.Run("Live", func(t *testing.T) { tr.testTail(t, true) })
    53  	t.Run("LiveEmpty", func(t *testing.T) { tr.testTailEmptyLogs(t, true) })
    54  	t.Run("Stopped", func(t *testing.T) { tr.testTail(t, false) })
    55  	t.Run("StoppedEmpty", func(t *testing.T) { tr.testTailEmptyLogs(t, false) })
    56  }
    57  
    58  func makeTestMessages() []*logger.Message {
    59  	return []*logger.Message{
    60  		{Source: "stdout", Timestamp: time.Now().Add(-1 * 30 * time.Minute), Line: []byte("a message")},
    61  		{Source: "stdout", Timestamp: time.Now().Add(-1 * 20 * time.Minute), Line: []byte("another message"), PLogMetaData: &backend.PartialLogMetaData{ID: "aaaaaaaa", Ordinal: 1, Last: true}},
    62  		{Source: "stderr", Timestamp: time.Now().Add(-1 * 15 * time.Minute), Line: []byte("to be..."), PLogMetaData: &backend.PartialLogMetaData{ID: "bbbbbbbb", Ordinal: 1}},
    63  		{Source: "stderr", Timestamp: time.Now().Add(-1 * 15 * time.Minute), Line: []byte("continued"), PLogMetaData: &backend.PartialLogMetaData{ID: "bbbbbbbb", Ordinal: 2, Last: true}},
    64  		{Source: "stderr", Timestamp: time.Now().Add(-1 * 10 * time.Minute), Line: []byte("a really long message " + strings.Repeat("a", 4096))},
    65  		{Source: "stderr", Timestamp: time.Now().Add(-1 * 10 * time.Minute), Line: []byte("just one more message")},
    66  		{Source: "stdout", Timestamp: time.Now().Add(-1 * 90 * time.Minute), Line: []byte("someone adjusted the clock")},
    67  	}
    68  }
    69  
    70  func (tr Reader) testTail(t *testing.T, live bool) {
    71  	t.Parallel()
    72  	factory := tr.Factory(t, logger.Info{
    73  		ContainerID:   "tailtest0000",
    74  		ContainerName: "logtail",
    75  	})
    76  	l := factory(t)
    77  	if live {
    78  		defer func() { assert.NilError(t, l.Close()) }()
    79  	}
    80  
    81  	mm := makeTestMessages()
    82  	expected := logMessages(t, l, mm)
    83  
    84  	if !live {
    85  		// Simulate reading from a stopped container.
    86  		assert.NilError(t, l.Close())
    87  		l = factory(t)
    88  		defer func() { assert.NilError(t, l.Close()) }()
    89  	}
    90  	lr := l.(logger.LogReader)
    91  
    92  	t.Run("Exact", func(t *testing.T) {
    93  		t.Parallel()
    94  		lw := lr.ReadLogs(logger.ReadConfig{Tail: len(mm)})
    95  		defer lw.ConsumerGone()
    96  		assert.DeepEqual(t, readAll(t, lw), expected, compareLog)
    97  	})
    98  
    99  	t.Run("LessThanAvailable", func(t *testing.T) {
   100  		t.Parallel()
   101  		lw := lr.ReadLogs(logger.ReadConfig{Tail: 2})
   102  		defer lw.ConsumerGone()
   103  		assert.DeepEqual(t, readAll(t, lw), expected[len(mm)-2:], compareLog)
   104  	})
   105  
   106  	t.Run("MoreThanAvailable", func(t *testing.T) {
   107  		t.Parallel()
   108  		lw := lr.ReadLogs(logger.ReadConfig{Tail: 100})
   109  		defer lw.ConsumerGone()
   110  		assert.DeepEqual(t, readAll(t, lw), expected, compareLog)
   111  	})
   112  
   113  	t.Run("All", func(t *testing.T) {
   114  		t.Parallel()
   115  		lw := lr.ReadLogs(logger.ReadConfig{Tail: -1})
   116  		defer lw.ConsumerGone()
   117  		assert.DeepEqual(t, readAll(t, lw), expected, compareLog)
   118  	})
   119  
   120  	t.Run("Since", func(t *testing.T) {
   121  		t.Parallel()
   122  		lw := lr.ReadLogs(logger.ReadConfig{Tail: -1, Since: mm[1].Timestamp.Truncate(time.Millisecond)})
   123  		defer lw.ConsumerGone()
   124  		assert.DeepEqual(t, readAll(t, lw), expected[1:], compareLog)
   125  	})
   126  
   127  	t.Run("MoreThanSince", func(t *testing.T) {
   128  		t.Parallel()
   129  		lw := lr.ReadLogs(logger.ReadConfig{Tail: len(mm), Since: mm[1].Timestamp.Truncate(time.Millisecond)})
   130  		defer lw.ConsumerGone()
   131  		assert.DeepEqual(t, readAll(t, lw), expected[1:], compareLog)
   132  	})
   133  
   134  	t.Run("LessThanSince", func(t *testing.T) {
   135  		t.Parallel()
   136  		lw := lr.ReadLogs(logger.ReadConfig{Tail: len(mm) - 2, Since: mm[1].Timestamp.Truncate(time.Millisecond)})
   137  		defer lw.ConsumerGone()
   138  		assert.DeepEqual(t, readAll(t, lw), expected[2:], compareLog)
   139  	})
   140  
   141  	t.Run("Until", func(t *testing.T) {
   142  		t.Parallel()
   143  		lw := lr.ReadLogs(logger.ReadConfig{Tail: -1, Until: mm[2].Timestamp.Add(-time.Millisecond)})
   144  		defer lw.ConsumerGone()
   145  		assert.DeepEqual(t, readAll(t, lw), expected[:2], compareLog)
   146  	})
   147  
   148  	t.Run("SinceAndUntil", func(t *testing.T) {
   149  		t.Parallel()
   150  		lw := lr.ReadLogs(logger.ReadConfig{Tail: -1, Since: mm[1].Timestamp.Truncate(time.Millisecond), Until: mm[1].Timestamp.Add(time.Millisecond)})
   151  		defer lw.ConsumerGone()
   152  		assert.DeepEqual(t, readAll(t, lw), expected[1:2], compareLog)
   153  	})
   154  }
   155  
   156  func (tr Reader) testTailEmptyLogs(t *testing.T, live bool) {
   157  	t.Parallel()
   158  	factory := tr.Factory(t, logger.Info{
   159  		ContainerID:   "tailemptytest",
   160  		ContainerName: "logtail",
   161  	})
   162  	l := factory(t)
   163  	if !live {
   164  		assert.NilError(t, l.Close())
   165  		l = factory(t)
   166  	}
   167  	defer func() { assert.NilError(t, l.Close()) }()
   168  
   169  	for _, tt := range []struct {
   170  		name string
   171  		cfg  logger.ReadConfig
   172  	}{
   173  		{name: "Zero", cfg: logger.ReadConfig{}},
   174  		{name: "All", cfg: logger.ReadConfig{Tail: -1}},
   175  		{name: "Tail", cfg: logger.ReadConfig{Tail: 42}},
   176  		{name: "Since", cfg: logger.ReadConfig{Since: time.Unix(1, 0)}},
   177  		{name: "Until", cfg: logger.ReadConfig{Until: time.Date(2100, time.January, 1, 1, 1, 1, 0, time.UTC)}},
   178  		{name: "SinceAndUntil", cfg: logger.ReadConfig{Since: time.Unix(1, 0), Until: time.Date(2100, time.January, 1, 1, 1, 1, 0, time.UTC)}},
   179  	} {
   180  		tt := tt
   181  		t.Run(tt.name, func(t *testing.T) {
   182  			t.Parallel()
   183  			lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{})
   184  			defer lw.ConsumerGone()
   185  			assert.DeepEqual(t, readAll(t, lw), ([]*logger.Message)(nil), cmpopts.EquateEmpty())
   186  		})
   187  	}
   188  }
   189  
   190  // TestFollow tests the LogReader's follow implementation.
   191  //
   192  // The LogReader is expected to be able to follow an arbitrary number of
   193  // messages at a high rate with no dropped messages.
   194  func (tr Reader) TestFollow(t *testing.T) {
   195  	// Reader sends all logs and closes after logger is closed
   196  	// - Starting from empty log (like run)
   197  	t.Run("FromEmptyLog", func(t *testing.T) {
   198  		t.Parallel()
   199  		l := tr.Factory(t, logger.Info{
   200  			ContainerID:   "followstart0",
   201  			ContainerName: "logloglog",
   202  		})(t)
   203  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: -1, Follow: true})
   204  		defer lw.ConsumerGone()
   205  
   206  		doneReading := make(chan struct{})
   207  		var logs []*logger.Message
   208  		go func() {
   209  			defer close(doneReading)
   210  			logs = readAll(t, lw)
   211  		}()
   212  
   213  		mm := makeTestMessages()
   214  		expected := logMessages(t, l, mm)
   215  		assert.NilError(t, l.Close())
   216  		<-doneReading
   217  		assert.DeepEqual(t, logs, expected, compareLog)
   218  	})
   219  
   220  	t.Run("AttachMidStream", func(t *testing.T) {
   221  		t.Parallel()
   222  		l := tr.Factory(t, logger.Info{
   223  			ContainerID:   "followmiddle",
   224  			ContainerName: "logloglog",
   225  		})(t)
   226  
   227  		mm := makeTestMessages()
   228  		expected := logMessages(t, l, mm[0:1])
   229  
   230  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: -1, Follow: true})
   231  		defer lw.ConsumerGone()
   232  
   233  		doneReading := make(chan struct{})
   234  		var logs []*logger.Message
   235  		go func() {
   236  			defer close(doneReading)
   237  			logs = readAll(t, lw)
   238  		}()
   239  
   240  		expected = append(expected, logMessages(t, l, mm[1:])...)
   241  		assert.NilError(t, l.Close())
   242  		<-doneReading
   243  		assert.DeepEqual(t, logs, expected, compareLog)
   244  	})
   245  
   246  	t.Run("Since", func(t *testing.T) {
   247  		t.Parallel()
   248  		l := tr.Factory(t, logger.Info{
   249  			ContainerID:   "followsince0",
   250  			ContainerName: "logloglog",
   251  		})(t)
   252  
   253  		mm := makeTestMessages()
   254  
   255  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: -1, Follow: true, Since: mm[2].Timestamp.Truncate(time.Millisecond)})
   256  		defer lw.ConsumerGone()
   257  
   258  		doneReading := make(chan struct{})
   259  		var logs []*logger.Message
   260  		go func() {
   261  			defer close(doneReading)
   262  			logs = readAll(t, lw)
   263  		}()
   264  
   265  		expected := logMessages(t, l, mm)[2:]
   266  		assert.NilError(t, l.Close())
   267  		<-doneReading
   268  		assert.DeepEqual(t, logs, expected, compareLog)
   269  	})
   270  
   271  	t.Run("Until", func(t *testing.T) {
   272  		t.Parallel()
   273  		l := tr.Factory(t, logger.Info{
   274  			ContainerID:   "followuntil0",
   275  			ContainerName: "logloglog",
   276  		})(t)
   277  
   278  		mm := makeTestMessages()
   279  
   280  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: -1, Follow: true, Until: mm[2].Timestamp.Add(-time.Millisecond)})
   281  		defer lw.ConsumerGone()
   282  
   283  		doneReading := make(chan struct{})
   284  		var logs []*logger.Message
   285  		go func() {
   286  			defer close(doneReading)
   287  			logs = readAll(t, lw)
   288  		}()
   289  
   290  		expected := logMessages(t, l, mm)[:2]
   291  		defer assert.NilError(t, l.Close()) // Reading should end before the logger is closed.
   292  		<-doneReading
   293  		assert.DeepEqual(t, logs, expected, compareLog)
   294  	})
   295  
   296  	t.Run("SinceAndUntil", func(t *testing.T) {
   297  		t.Parallel()
   298  		l := tr.Factory(t, logger.Info{
   299  			ContainerID:   "followbounded",
   300  			ContainerName: "logloglog",
   301  		})(t)
   302  
   303  		mm := makeTestMessages()
   304  
   305  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: -1, Follow: true, Since: mm[1].Timestamp.Add(-time.Millisecond), Until: mm[2].Timestamp.Add(-time.Millisecond)})
   306  		defer lw.ConsumerGone()
   307  
   308  		doneReading := make(chan struct{})
   309  		var logs []*logger.Message
   310  		go func() {
   311  			defer close(doneReading)
   312  			logs = readAll(t, lw)
   313  		}()
   314  
   315  		expected := logMessages(t, l, mm)[1:2]
   316  		defer assert.NilError(t, l.Close()) // Reading should end before the logger is closed.
   317  		<-doneReading
   318  		assert.DeepEqual(t, logs, expected, compareLog)
   319  	})
   320  
   321  	t.Run("Tail=0", func(t *testing.T) {
   322  		t.Parallel()
   323  		l := tr.Factory(t, logger.Info{
   324  			ContainerID:   "followtail00",
   325  			ContainerName: "logloglog",
   326  		})(t)
   327  
   328  		mm := makeTestMessages()
   329  		logMessages(t, l, mm[0:2])
   330  		syncLogger(t, l)
   331  
   332  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: 0, Follow: true})
   333  		defer lw.ConsumerGone()
   334  
   335  		doneReading := make(chan struct{})
   336  		var logs []*logger.Message
   337  		go func() {
   338  			defer close(doneReading)
   339  			logs = readAll(t, lw)
   340  		}()
   341  
   342  		expected := logMessages(t, l, mm[2:])
   343  		assert.NilError(t, l.Close())
   344  		<-doneReading
   345  		assert.DeepEqual(t, logs, expected, compareLog)
   346  	})
   347  
   348  	t.Run("Tail>0", func(t *testing.T) {
   349  		t.Parallel()
   350  		l := tr.Factory(t, logger.Info{
   351  			ContainerID:   "followtail00",
   352  			ContainerName: "logloglog",
   353  		})(t)
   354  
   355  		mm := makeTestMessages()
   356  		expected := logMessages(t, l, mm[0:2])[1:]
   357  		syncLogger(t, l)
   358  
   359  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: 1, Follow: true})
   360  		defer lw.ConsumerGone()
   361  
   362  		doneReading := make(chan struct{})
   363  		var logs []*logger.Message
   364  		go func() {
   365  			defer close(doneReading)
   366  			logs = readAll(t, lw)
   367  		}()
   368  
   369  		expected = append(expected, logMessages(t, l, mm[2:])...)
   370  		assert.NilError(t, l.Close())
   371  		<-doneReading
   372  		assert.DeepEqual(t, logs, expected, compareLog)
   373  	})
   374  
   375  	t.Run("MultipleStarts", func(t *testing.T) {
   376  		t.Parallel()
   377  		factory := tr.Factory(t, logger.Info{
   378  			ContainerID:   "startrestart",
   379  			ContainerName: "startmeup",
   380  		})
   381  
   382  		mm := makeTestMessages()
   383  		l := factory(t)
   384  		expected := logMessages(t, l, mm[:3])
   385  		assert.NilError(t, l.Close())
   386  
   387  		l = factory(t)
   388  		lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Tail: -1, Follow: true})
   389  		defer lw.ConsumerGone()
   390  
   391  		doneReading := make(chan struct{})
   392  		var logs []*logger.Message
   393  		go func() {
   394  			defer close(doneReading)
   395  			logs = readAll(t, lw)
   396  		}()
   397  
   398  		expected = append(expected, logMessages(t, l, mm[3:])...)
   399  		assert.NilError(t, l.Close())
   400  		<-doneReading
   401  		assert.DeepEqual(t, logs, expected, compareLog)
   402  	})
   403  
   404  	t.Run("Concurrent", tr.TestConcurrent)
   405  }
   406  
   407  // TestConcurrent tests the Logger and its LogReader implementation for
   408  // race conditions when logging from multiple goroutines concurrently.
   409  func (tr Reader) TestConcurrent(t *testing.T) {
   410  	t.Parallel()
   411  	l := tr.Factory(t, logger.Info{
   412  		ContainerID:   "logconcurrent0",
   413  		ContainerName: "logconcurrent123",
   414  	})(t)
   415  
   416  	// Split test messages
   417  	stderrMessages := []*logger.Message{}
   418  	stdoutMessages := []*logger.Message{}
   419  	for _, m := range makeTestMessages() {
   420  		if m.Source == "stdout" {
   421  			stdoutMessages = append(stdoutMessages, m)
   422  		} else if m.Source == "stderr" {
   423  			stderrMessages = append(stderrMessages, m)
   424  		}
   425  	}
   426  
   427  	// Follow all logs
   428  	lw := l.(logger.LogReader).ReadLogs(logger.ReadConfig{Follow: true, Tail: -1})
   429  	defer lw.ConsumerGone()
   430  
   431  	// Log concurrently from two sources and close log
   432  	wg := &sync.WaitGroup{}
   433  	logAll := func(msgs []*logger.Message) {
   434  		defer wg.Done()
   435  		for _, m := range msgs {
   436  			l.Log(copyLogMessage(m))
   437  		}
   438  	}
   439  
   440  	closed := make(chan struct{})
   441  	wg.Add(2)
   442  	go logAll(stdoutMessages)
   443  	go logAll(stderrMessages)
   444  	go func() {
   445  		defer close(closed)
   446  		defer l.Close()
   447  		wg.Wait()
   448  	}()
   449  
   450  	// Check if the message count, order and content is equal to what was logged
   451  	for {
   452  		l := readMessage(t, lw)
   453  		if l == nil {
   454  			break
   455  		}
   456  
   457  		var messages *[]*logger.Message
   458  		if l.Source == "stdout" {
   459  			messages = &stdoutMessages
   460  		} else if l.Source == "stderr" {
   461  			messages = &stderrMessages
   462  		} else {
   463  			t.Fatalf("Corrupted message.Source = %q", l.Source)
   464  		}
   465  
   466  		expectedMsg := transformToExpected((*messages)[0])
   467  
   468  		assert.DeepEqual(t, *expectedMsg, *l, compareLog)
   469  		*messages = (*messages)[1:]
   470  	}
   471  
   472  	assert.Equal(t, len(stdoutMessages), 0)
   473  	assert.Equal(t, len(stderrMessages), 0)
   474  
   475  	// Make sure log gets closed before we return
   476  	// so the temporary dir can be deleted
   477  	<-closed
   478  }
   479  
   480  // logMessages logs messages to l and returns a slice of messages as would be
   481  // expected to be read back. The message values are not modified and the
   482  // returned slice of messages are deep-copied.
   483  func logMessages(t *testing.T, l logger.Logger, messages []*logger.Message) []*logger.Message {
   484  	t.Helper()
   485  	var expected []*logger.Message
   486  	for _, m := range messages {
   487  		// Copy the log message because the underlying log writer resets
   488  		// the log message and returns it to a buffer pool.
   489  		assert.NilError(t, l.Log(copyLogMessage(m)))
   490  		runtime.Gosched()
   491  
   492  		expect := transformToExpected(m)
   493  		expected = append(expected, expect)
   494  	}
   495  	return expected
   496  }
   497  
   498  // Existing API consumers expect a newline to be appended to
   499  // messages other than nonterminal partials as that matches the
   500  // existing behavior of the json-file log driver.
   501  func transformToExpected(m *logger.Message) *logger.Message {
   502  	// Copy the log message again so as not to mutate the input.
   503  	copy := copyLogMessage(m)
   504  	if m.PLogMetaData == nil || m.PLogMetaData.Last {
   505  		copy.Line = append(copy.Line, '\n')
   506  	}
   507  
   508  	return copy
   509  }
   510  
   511  func copyLogMessage(src *logger.Message) *logger.Message {
   512  	dst := logger.NewMessage()
   513  	dst.Source = src.Source
   514  	dst.Timestamp = src.Timestamp
   515  	dst.Attrs = src.Attrs
   516  	dst.Err = src.Err
   517  	dst.Line = append(dst.Line, src.Line...)
   518  	if src.PLogMetaData != nil {
   519  		lmd := *src.PLogMetaData
   520  		dst.PLogMetaData = &lmd
   521  	}
   522  	return dst
   523  }
   524  func readMessage(t *testing.T, lw *logger.LogWatcher) *logger.Message {
   525  	t.Helper()
   526  	timeout := time.NewTimer(5 * time.Second)
   527  	defer timeout.Stop()
   528  	select {
   529  	case <-timeout.C:
   530  		t.Error("timed out waiting for message")
   531  		return nil
   532  	case err, open := <-lw.Err:
   533  		t.Errorf("unexpected receive on lw.Err: err=%v, open=%v", err, open)
   534  		return nil
   535  	case msg, open := <-lw.Msg:
   536  		if !open {
   537  			select {
   538  			case err, open := <-lw.Err:
   539  				t.Errorf("unexpected receive on lw.Err with closed lw.Msg: err=%v, open=%v", err, open)
   540  			default:
   541  			}
   542  			return nil
   543  		}
   544  		assert.Assert(t, msg != nil)
   545  		t.Logf("[%v] %s: %s", msg.Timestamp, msg.Source, msg.Line)
   546  		return msg
   547  	}
   548  }
   549  
   550  func readAll(t *testing.T, lw *logger.LogWatcher) []*logger.Message {
   551  	t.Helper()
   552  	var msgs []*logger.Message
   553  	for {
   554  		m := readMessage(t, lw)
   555  		if m == nil {
   556  			return msgs
   557  		}
   558  		msgs = append(msgs, m)
   559  	}
   560  }