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

     1  package logger // import "github.com/docker/docker/daemon/logger"
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"os"
     8  	"strings"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  )
    13  
    14  type TestLoggerJSON struct {
    15  	*json.Encoder
    16  	mu    sync.Mutex
    17  	delay time.Duration
    18  }
    19  
    20  func (l *TestLoggerJSON) Log(m *Message) error {
    21  	if l.delay > 0 {
    22  		time.Sleep(l.delay)
    23  	}
    24  	l.mu.Lock()
    25  	defer l.mu.Unlock()
    26  	return l.Encode(m)
    27  }
    28  
    29  func (l *TestLoggerJSON) Close() error { return nil }
    30  
    31  func (l *TestLoggerJSON) Name() string { return "json" }
    32  
    33  type TestSizedLoggerJSON struct {
    34  	*json.Encoder
    35  	mu sync.Mutex
    36  }
    37  
    38  func (l *TestSizedLoggerJSON) Log(m *Message) error {
    39  	l.mu.Lock()
    40  	defer l.mu.Unlock()
    41  	return l.Encode(m)
    42  }
    43  
    44  func (*TestSizedLoggerJSON) Close() error { return nil }
    45  
    46  func (*TestSizedLoggerJSON) Name() string { return "sized-json" }
    47  
    48  func (*TestSizedLoggerJSON) BufSize() int {
    49  	return 32 * 1024
    50  }
    51  
    52  func TestCopier(t *testing.T) {
    53  	stdoutLine := "Line that thinks that it is log line from docker stdout"
    54  	stderrLine := "Line that thinks that it is log line from docker stderr"
    55  	stdoutTrailingLine := "stdout trailing line"
    56  	stderrTrailingLine := "stderr trailing line"
    57  
    58  	var stdout bytes.Buffer
    59  	var stderr bytes.Buffer
    60  	for i := 0; i < 30; i++ {
    61  		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
    62  			t.Fatal(err)
    63  		}
    64  		if _, err := stderr.WriteString(stderrLine + "\n"); err != nil {
    65  			t.Fatal(err)
    66  		}
    67  	}
    68  
    69  	// Test remaining lines without line-endings
    70  	if _, err := stdout.WriteString(stdoutTrailingLine); err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	if _, err := stderr.WriteString(stderrTrailingLine); err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	var jsonBuf bytes.Buffer
    78  
    79  	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}
    80  
    81  	c := NewCopier(
    82  		map[string]io.Reader{
    83  			"stdout": &stdout,
    84  			"stderr": &stderr,
    85  		},
    86  		jsonLog)
    87  	c.Run()
    88  	wait := make(chan struct{})
    89  	go func() {
    90  		c.Wait()
    91  		close(wait)
    92  	}()
    93  	select {
    94  	case <-time.After(1 * time.Second):
    95  		t.Fatal("Copier failed to do its work in 1 second")
    96  	case <-wait:
    97  	}
    98  	dec := json.NewDecoder(&jsonBuf)
    99  	for {
   100  		var msg Message
   101  		if err := dec.Decode(&msg); err != nil {
   102  			if err == io.EOF {
   103  				break
   104  			}
   105  			t.Fatal(err)
   106  		}
   107  		if msg.Source != "stdout" && msg.Source != "stderr" {
   108  			t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
   109  		}
   110  		if msg.Source == "stdout" {
   111  			if string(msg.Line) != stdoutLine && string(msg.Line) != stdoutTrailingLine {
   112  				t.Fatalf("Wrong Line: %q, expected %q or %q", msg.Line, stdoutLine, stdoutTrailingLine)
   113  			}
   114  		}
   115  		if msg.Source == "stderr" {
   116  			if string(msg.Line) != stderrLine && string(msg.Line) != stderrTrailingLine {
   117  				t.Fatalf("Wrong Line: %q, expected %q or %q", msg.Line, stderrLine, stderrTrailingLine)
   118  			}
   119  		}
   120  	}
   121  }
   122  
   123  // TestCopierLongLines tests long lines without line breaks
   124  func TestCopierLongLines(t *testing.T) {
   125  	// Long lines (should be split at "defaultBufSize")
   126  	stdoutLongLine := strings.Repeat("a", defaultBufSize)
   127  	stderrLongLine := strings.Repeat("b", defaultBufSize)
   128  	stdoutTrailingLine := "stdout trailing line"
   129  	stderrTrailingLine := "stderr trailing line"
   130  
   131  	var stdout bytes.Buffer
   132  	var stderr bytes.Buffer
   133  
   134  	for i := 0; i < 3; i++ {
   135  		if _, err := stdout.WriteString(stdoutLongLine); err != nil {
   136  			t.Fatal(err)
   137  		}
   138  		if _, err := stderr.WriteString(stderrLongLine); err != nil {
   139  			t.Fatal(err)
   140  		}
   141  	}
   142  
   143  	if _, err := stdout.WriteString(stdoutTrailingLine); err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	if _, err := stderr.WriteString(stderrTrailingLine); err != nil {
   147  		t.Fatal(err)
   148  	}
   149  
   150  	var jsonBuf bytes.Buffer
   151  
   152  	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}
   153  
   154  	c := NewCopier(
   155  		map[string]io.Reader{
   156  			"stdout": &stdout,
   157  			"stderr": &stderr,
   158  		},
   159  		jsonLog)
   160  	c.Run()
   161  	wait := make(chan struct{})
   162  	go func() {
   163  		c.Wait()
   164  		close(wait)
   165  	}()
   166  	select {
   167  	case <-time.After(1 * time.Second):
   168  		t.Fatal("Copier failed to do its work in 1 second")
   169  	case <-wait:
   170  	}
   171  	dec := json.NewDecoder(&jsonBuf)
   172  	for {
   173  		var msg Message
   174  		if err := dec.Decode(&msg); err != nil {
   175  			if err == io.EOF {
   176  				break
   177  			}
   178  			t.Fatal(err)
   179  		}
   180  		if msg.Source != "stdout" && msg.Source != "stderr" {
   181  			t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
   182  		}
   183  		if msg.Source == "stdout" {
   184  			if string(msg.Line) != stdoutLongLine && string(msg.Line) != stdoutTrailingLine {
   185  				t.Fatalf("Wrong Line: %q, expected 'stdoutLongLine' or 'stdoutTrailingLine'", msg.Line)
   186  			}
   187  		}
   188  		if msg.Source == "stderr" {
   189  			if string(msg.Line) != stderrLongLine && string(msg.Line) != stderrTrailingLine {
   190  				t.Fatalf("Wrong Line: %q, expected 'stderrLongLine' or 'stderrTrailingLine'", msg.Line)
   191  			}
   192  		}
   193  	}
   194  }
   195  
   196  func TestCopierSlow(t *testing.T) {
   197  	stdoutLine := "Line that thinks that it is log line from docker stdout"
   198  	var stdout bytes.Buffer
   199  	for i := 0; i < 30; i++ {
   200  		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
   201  			t.Fatal(err)
   202  		}
   203  	}
   204  
   205  	var jsonBuf bytes.Buffer
   206  	// encoder := &encodeCloser{Encoder: json.NewEncoder(&jsonBuf)}
   207  	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf), delay: 100 * time.Millisecond}
   208  
   209  	c := NewCopier(map[string]io.Reader{"stdout": &stdout}, jsonLog)
   210  	c.Run()
   211  	wait := make(chan struct{})
   212  	go func() {
   213  		c.Wait()
   214  		close(wait)
   215  	}()
   216  	<-time.After(150 * time.Millisecond)
   217  	c.Close()
   218  	select {
   219  	case <-time.After(200 * time.Millisecond):
   220  		t.Fatal("failed to exit in time after the copier is closed")
   221  	case <-wait:
   222  	}
   223  }
   224  
   225  func TestCopierWithSized(t *testing.T) {
   226  	t.Run("as is", func(t *testing.T) {
   227  		testCopierWithSized(t, func(l SizedLogger) SizedLogger {
   228  			return l
   229  		})
   230  	})
   231  	t.Run("With RingLogger", func(t *testing.T) {
   232  		testCopierWithSized(t, func(l SizedLogger) SizedLogger {
   233  			return newRingLogger(l, Info{}, defaultRingMaxSize)
   234  		})
   235  	})
   236  }
   237  
   238  func testCopierWithSized(t *testing.T, loggerFactory func(SizedLogger) SizedLogger) {
   239  	var jsonBuf bytes.Buffer
   240  	expectedMsgs := 2
   241  	sizedLogger := loggerFactory(&TestSizedLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)})
   242  
   243  	size := sizedLogger.BufSize()
   244  	if size < 0 {
   245  		size = 100
   246  	}
   247  	logbuf := bytes.NewBufferString(strings.Repeat(".", size*expectedMsgs))
   248  	c := NewCopier(map[string]io.Reader{"stdout": logbuf}, sizedLogger)
   249  
   250  	c.Run()
   251  	// Wait for Copier to finish writing to the buffered logger.
   252  	c.Wait()
   253  	c.Close()
   254  
   255  	sizedLogger.Close()
   256  
   257  	recvdMsgs := 0
   258  	dec := json.NewDecoder(&jsonBuf)
   259  	for {
   260  		var msg Message
   261  		if err := dec.Decode(&msg); err != nil {
   262  			if err == io.EOF {
   263  				break
   264  			}
   265  			t.Fatal(err)
   266  		}
   267  		if msg.Source != "stdout" {
   268  			t.Fatalf("Wrong Source: %q, should be %q", msg.Source, "stdout")
   269  		}
   270  		if len(msg.Line) != sizedLogger.BufSize() {
   271  			t.Fatalf("Line was not of expected max length %d, was %d", sizedLogger.BufSize(), len(msg.Line))
   272  		}
   273  		recvdMsgs++
   274  	}
   275  	if recvdMsgs != expectedMsgs {
   276  		t.Fatalf("expected to receive %d messages, actually received %d %q", expectedMsgs, recvdMsgs, jsonBuf.String())
   277  	}
   278  }
   279  
   280  func checkIdentical(t *testing.T, msg Message, expectedID string, expectedTS time.Time) {
   281  	if msg.PLogMetaData.ID != expectedID {
   282  		t.Fatalf("IDs are not he same across partials. Expected: %s Received: %s",
   283  			expectedID, msg.PLogMetaData.ID)
   284  	}
   285  	if msg.Timestamp != expectedTS {
   286  		t.Fatalf("Timestamps are not the same across partials. Expected: %v Received: %v",
   287  			expectedTS.Format(time.UnixDate), msg.Timestamp.Format(time.UnixDate))
   288  	}
   289  }
   290  
   291  // Have long lines and make sure that it comes out with PartialMetaData
   292  func TestCopierWithPartial(t *testing.T) {
   293  	stdoutLongLine := strings.Repeat("a", defaultBufSize)
   294  	stderrLongLine := strings.Repeat("b", defaultBufSize)
   295  	stdoutTrailingLine := "stdout trailing line"
   296  	stderrTrailingLine := "stderr trailing line"
   297  	normalStr := "This is an impartial message :)"
   298  
   299  	var stdout bytes.Buffer
   300  	var stderr bytes.Buffer
   301  	var normalMsg bytes.Buffer
   302  
   303  	for i := 0; i < 3; i++ {
   304  		if _, err := stdout.WriteString(stdoutLongLine); err != nil {
   305  			t.Fatal(err)
   306  		}
   307  		if _, err := stderr.WriteString(stderrLongLine); err != nil {
   308  			t.Fatal(err)
   309  		}
   310  	}
   311  
   312  	if _, err := stdout.WriteString(stdoutTrailingLine + "\n"); err != nil {
   313  		t.Fatal(err)
   314  	}
   315  	if _, err := stderr.WriteString(stderrTrailingLine + "\n"); err != nil {
   316  		t.Fatal(err)
   317  	}
   318  	if _, err := normalMsg.WriteString(normalStr + "\n"); err != nil {
   319  		t.Fatal(err)
   320  	}
   321  
   322  	var jsonBuf bytes.Buffer
   323  
   324  	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}
   325  
   326  	c := NewCopier(
   327  		map[string]io.Reader{
   328  			"stdout": &stdout,
   329  			"normal": &normalMsg,
   330  			"stderr": &stderr,
   331  		},
   332  		jsonLog)
   333  	c.Run()
   334  	wait := make(chan struct{})
   335  	go func() {
   336  		c.Wait()
   337  		close(wait)
   338  	}()
   339  	select {
   340  	case <-time.After(1 * time.Second):
   341  		t.Fatal("Copier failed to do its work in 1 second")
   342  	case <-wait:
   343  	}
   344  
   345  	dec := json.NewDecoder(&jsonBuf)
   346  	expectedMsgs := 9
   347  	recvMsgs := 0
   348  	var expectedPartID1, expectedPartID2 string
   349  	var expectedTS1, expectedTS2 time.Time
   350  
   351  	for {
   352  		var msg Message
   353  
   354  		if err := dec.Decode(&msg); err != nil {
   355  			if err == io.EOF {
   356  				break
   357  			}
   358  			t.Fatal(err)
   359  		}
   360  		if msg.Source != "stdout" && msg.Source != "stderr" && msg.Source != "normal" {
   361  			t.Fatalf("Wrong Source: %q, should be %q or %q or %q", msg.Source, "stdout", "stderr", "normal")
   362  		}
   363  
   364  		if msg.Source == "stdout" {
   365  			if string(msg.Line) != stdoutLongLine && string(msg.Line) != stdoutTrailingLine {
   366  				t.Fatalf("Wrong Line: %q, expected 'stdoutLongLine' or 'stdoutTrailingLine'", msg.Line)
   367  			}
   368  
   369  			if msg.PLogMetaData.ID == "" {
   370  				t.Fatalf("Expected partial metadata. Got nothing")
   371  			}
   372  
   373  			if msg.PLogMetaData.Ordinal == 1 {
   374  				expectedPartID1 = msg.PLogMetaData.ID
   375  				expectedTS1 = msg.Timestamp
   376  			} else {
   377  				checkIdentical(t, msg, expectedPartID1, expectedTS1)
   378  			}
   379  			if msg.PLogMetaData.Ordinal == 4 && !msg.PLogMetaData.Last {
   380  				t.Fatalf("Last is not set for last chunk")
   381  			}
   382  		}
   383  
   384  		if msg.Source == "stderr" {
   385  			if string(msg.Line) != stderrLongLine && string(msg.Line) != stderrTrailingLine {
   386  				t.Fatalf("Wrong Line: %q, expected 'stderrLongLine' or 'stderrTrailingLine'", msg.Line)
   387  			}
   388  
   389  			if msg.PLogMetaData.ID == "" {
   390  				t.Fatalf("Expected partial metadata. Got nothing")
   391  			}
   392  
   393  			if msg.PLogMetaData.Ordinal == 1 {
   394  				expectedPartID2 = msg.PLogMetaData.ID
   395  				expectedTS2 = msg.Timestamp
   396  			} else {
   397  				checkIdentical(t, msg, expectedPartID2, expectedTS2)
   398  			}
   399  			if msg.PLogMetaData.Ordinal == 4 && !msg.PLogMetaData.Last {
   400  				t.Fatalf("Last is not set for last chunk")
   401  			}
   402  		}
   403  
   404  		if msg.Source == "normal" && msg.PLogMetaData != nil {
   405  			t.Fatalf("Normal messages should not have PartialLogMetaData")
   406  		}
   407  		recvMsgs++
   408  	}
   409  
   410  	if expectedMsgs != recvMsgs {
   411  		t.Fatalf("Expected msgs: %d Recv msgs: %d", expectedMsgs, recvMsgs)
   412  	}
   413  }
   414  
   415  type BenchmarkLoggerDummy struct {
   416  }
   417  
   418  func (l *BenchmarkLoggerDummy) Log(m *Message) error { PutMessage(m); return nil }
   419  
   420  func (l *BenchmarkLoggerDummy) Close() error { return nil }
   421  
   422  func (l *BenchmarkLoggerDummy) Name() string { return "dummy" }
   423  
   424  func BenchmarkCopier64(b *testing.B) {
   425  	benchmarkCopier(b, 1<<6)
   426  }
   427  func BenchmarkCopier128(b *testing.B) {
   428  	benchmarkCopier(b, 1<<7)
   429  }
   430  func BenchmarkCopier256(b *testing.B) {
   431  	benchmarkCopier(b, 1<<8)
   432  }
   433  func BenchmarkCopier512(b *testing.B) {
   434  	benchmarkCopier(b, 1<<9)
   435  }
   436  func BenchmarkCopier1K(b *testing.B) {
   437  	benchmarkCopier(b, 1<<10)
   438  }
   439  func BenchmarkCopier2K(b *testing.B) {
   440  	benchmarkCopier(b, 1<<11)
   441  }
   442  func BenchmarkCopier4K(b *testing.B) {
   443  	benchmarkCopier(b, 1<<12)
   444  }
   445  func BenchmarkCopier8K(b *testing.B) {
   446  	benchmarkCopier(b, 1<<13)
   447  }
   448  func BenchmarkCopier16K(b *testing.B) {
   449  	benchmarkCopier(b, 1<<14)
   450  }
   451  func BenchmarkCopier32K(b *testing.B) {
   452  	benchmarkCopier(b, 1<<15)
   453  }
   454  func BenchmarkCopier64K(b *testing.B) {
   455  	benchmarkCopier(b, 1<<16)
   456  }
   457  func BenchmarkCopier128K(b *testing.B) {
   458  	benchmarkCopier(b, 1<<17)
   459  }
   460  func BenchmarkCopier256K(b *testing.B) {
   461  	benchmarkCopier(b, 1<<18)
   462  }
   463  
   464  func piped(b *testing.B, iterations int, delay time.Duration, buf []byte) io.Reader {
   465  	r, w, err := os.Pipe()
   466  	if err != nil {
   467  		b.Fatal(err)
   468  		return nil
   469  	}
   470  	go func() {
   471  		for i := 0; i < iterations; i++ {
   472  			time.Sleep(delay)
   473  			if n, err := w.Write(buf); err != nil || n != len(buf) {
   474  				if err != nil {
   475  					b.Error(err)
   476  				}
   477  				b.Error("short write")
   478  			}
   479  		}
   480  		w.Close()
   481  	}()
   482  	return r
   483  }
   484  
   485  func benchmarkCopier(b *testing.B, length int) {
   486  	b.StopTimer()
   487  	buf := []byte{'A'}
   488  	for len(buf) < length {
   489  		buf = append(buf, buf...)
   490  	}
   491  	buf = append(buf[:length-1], []byte{'\n'}...)
   492  	b.StartTimer()
   493  	for i := 0; i < b.N; i++ {
   494  		c := NewCopier(
   495  			map[string]io.Reader{
   496  				"buffer": piped(b, 10, time.Nanosecond, buf),
   497  			},
   498  			&BenchmarkLoggerDummy{})
   499  		c.Run()
   500  		c.Wait()
   501  		c.Close()
   502  	}
   503  }