github.com/opsramp/moby@v1.13.1/daemon/logger/copier_test.go (about)

     1  package logger
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"strings"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  )
    14  
    15  type TestLoggerJSON struct {
    16  	*json.Encoder
    17  	mu    sync.Mutex
    18  	delay time.Duration
    19  }
    20  
    21  func (l *TestLoggerJSON) Log(m *Message) error {
    22  	if l.delay > 0 {
    23  		time.Sleep(l.delay)
    24  	}
    25  	l.mu.Lock()
    26  	defer l.mu.Unlock()
    27  	return l.Encode(m)
    28  }
    29  
    30  func (l *TestLoggerJSON) Close() error { return nil }
    31  
    32  func (l *TestLoggerJSON) Name() string { return "json" }
    33  
    34  func TestCopier(t *testing.T) {
    35  	stdoutLine := "Line that thinks that it is log line from docker stdout"
    36  	stderrLine := "Line that thinks that it is log line from docker stderr"
    37  	stdoutTrailingLine := "stdout trailing line"
    38  	stderrTrailingLine := "stderr trailing line"
    39  
    40  	var stdout bytes.Buffer
    41  	var stderr bytes.Buffer
    42  	for i := 0; i < 30; i++ {
    43  		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
    44  			t.Fatal(err)
    45  		}
    46  		if _, err := stderr.WriteString(stderrLine + "\n"); err != nil {
    47  			t.Fatal(err)
    48  		}
    49  	}
    50  
    51  	// Test remaining lines without line-endings
    52  	if _, err := stdout.WriteString(stdoutTrailingLine); err != nil {
    53  		t.Fatal(err)
    54  	}
    55  	if _, err := stderr.WriteString(stderrTrailingLine); err != nil {
    56  		t.Fatal(err)
    57  	}
    58  
    59  	var jsonBuf bytes.Buffer
    60  
    61  	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}
    62  
    63  	c := NewCopier(
    64  		map[string]io.Reader{
    65  			"stdout": &stdout,
    66  			"stderr": &stderr,
    67  		},
    68  		jsonLog)
    69  	c.Run()
    70  	wait := make(chan struct{})
    71  	go func() {
    72  		c.Wait()
    73  		close(wait)
    74  	}()
    75  	select {
    76  	case <-time.After(1 * time.Second):
    77  		t.Fatal("Copier failed to do its work in 1 second")
    78  	case <-wait:
    79  	}
    80  	dec := json.NewDecoder(&jsonBuf)
    81  	for {
    82  		var msg Message
    83  		if err := dec.Decode(&msg); err != nil {
    84  			if err == io.EOF {
    85  				break
    86  			}
    87  			t.Fatal(err)
    88  		}
    89  		if msg.Source != "stdout" && msg.Source != "stderr" {
    90  			t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
    91  		}
    92  		if msg.Source == "stdout" {
    93  			if string(msg.Line) != stdoutLine && string(msg.Line) != stdoutTrailingLine {
    94  				t.Fatalf("Wrong Line: %q, expected %q or %q", msg.Line, stdoutLine, stdoutTrailingLine)
    95  			}
    96  		}
    97  		if msg.Source == "stderr" {
    98  			if string(msg.Line) != stderrLine && string(msg.Line) != stderrTrailingLine {
    99  				t.Fatalf("Wrong Line: %q, expected %q or %q", msg.Line, stderrLine, stderrTrailingLine)
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  // TestCopierLongLines tests long lines without line breaks
   106  func TestCopierLongLines(t *testing.T) {
   107  	// Long lines (should be split at "bufSize")
   108  	const bufSize = 16 * 1024
   109  	stdoutLongLine := strings.Repeat("a", bufSize)
   110  	stderrLongLine := strings.Repeat("b", bufSize)
   111  	stdoutTrailingLine := "stdout trailing line"
   112  	stderrTrailingLine := "stderr trailing line"
   113  
   114  	var stdout bytes.Buffer
   115  	var stderr bytes.Buffer
   116  
   117  	for i := 0; i < 3; i++ {
   118  		if _, err := stdout.WriteString(stdoutLongLine); err != nil {
   119  			t.Fatal(err)
   120  		}
   121  		if _, err := stderr.WriteString(stderrLongLine); err != nil {
   122  			t.Fatal(err)
   123  		}
   124  	}
   125  
   126  	if _, err := stdout.WriteString(stdoutTrailingLine); err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	if _, err := stderr.WriteString(stderrTrailingLine); err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	var jsonBuf bytes.Buffer
   134  
   135  	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}
   136  
   137  	c := NewCopier(
   138  		map[string]io.Reader{
   139  			"stdout": &stdout,
   140  			"stderr": &stderr,
   141  		},
   142  		jsonLog)
   143  	c.Run()
   144  	wait := make(chan struct{})
   145  	go func() {
   146  		c.Wait()
   147  		close(wait)
   148  	}()
   149  	select {
   150  	case <-time.After(1 * time.Second):
   151  		t.Fatal("Copier failed to do its work in 1 second")
   152  	case <-wait:
   153  	}
   154  	dec := json.NewDecoder(&jsonBuf)
   155  	for {
   156  		var msg Message
   157  		if err := dec.Decode(&msg); err != nil {
   158  			if err == io.EOF {
   159  				break
   160  			}
   161  			t.Fatal(err)
   162  		}
   163  		if msg.Source != "stdout" && msg.Source != "stderr" {
   164  			t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
   165  		}
   166  		if msg.Source == "stdout" {
   167  			if string(msg.Line) != stdoutLongLine && string(msg.Line) != stdoutTrailingLine {
   168  				t.Fatalf("Wrong Line: %q, expected 'stdoutLongLine' or 'stdoutTrailingLine'", msg.Line)
   169  			}
   170  		}
   171  		if msg.Source == "stderr" {
   172  			if string(msg.Line) != stderrLongLine && string(msg.Line) != stderrTrailingLine {
   173  				t.Fatalf("Wrong Line: %q, expected 'stderrLongLine' or 'stderrTrailingLine'", msg.Line)
   174  			}
   175  		}
   176  	}
   177  }
   178  
   179  func TestCopierSlow(t *testing.T) {
   180  	stdoutLine := "Line that thinks that it is log line from docker stdout"
   181  	var stdout bytes.Buffer
   182  	for i := 0; i < 30; i++ {
   183  		if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
   184  			t.Fatal(err)
   185  		}
   186  	}
   187  
   188  	var jsonBuf bytes.Buffer
   189  	//encoder := &encodeCloser{Encoder: json.NewEncoder(&jsonBuf)}
   190  	jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf), delay: 100 * time.Millisecond}
   191  
   192  	c := NewCopier(map[string]io.Reader{"stdout": &stdout}, jsonLog)
   193  	c.Run()
   194  	wait := make(chan struct{})
   195  	go func() {
   196  		c.Wait()
   197  		close(wait)
   198  	}()
   199  	<-time.After(150 * time.Millisecond)
   200  	c.Close()
   201  	select {
   202  	case <-time.After(200 * time.Millisecond):
   203  		t.Fatalf("failed to exit in time after the copier is closed")
   204  	case <-wait:
   205  	}
   206  }
   207  
   208  type BenchmarkLoggerDummy struct {
   209  }
   210  
   211  func (l *BenchmarkLoggerDummy) Log(m *Message) error { return nil }
   212  
   213  func (l *BenchmarkLoggerDummy) Close() error { return nil }
   214  
   215  func (l *BenchmarkLoggerDummy) Name() string { return "dummy" }
   216  
   217  func BenchmarkCopier64(b *testing.B) {
   218  	benchmarkCopier(b, 1<<6)
   219  }
   220  func BenchmarkCopier128(b *testing.B) {
   221  	benchmarkCopier(b, 1<<7)
   222  }
   223  func BenchmarkCopier256(b *testing.B) {
   224  	benchmarkCopier(b, 1<<8)
   225  }
   226  func BenchmarkCopier512(b *testing.B) {
   227  	benchmarkCopier(b, 1<<9)
   228  }
   229  func BenchmarkCopier1K(b *testing.B) {
   230  	benchmarkCopier(b, 1<<10)
   231  }
   232  func BenchmarkCopier2K(b *testing.B) {
   233  	benchmarkCopier(b, 1<<11)
   234  }
   235  func BenchmarkCopier4K(b *testing.B) {
   236  	benchmarkCopier(b, 1<<12)
   237  }
   238  func BenchmarkCopier8K(b *testing.B) {
   239  	benchmarkCopier(b, 1<<13)
   240  }
   241  func BenchmarkCopier16K(b *testing.B) {
   242  	benchmarkCopier(b, 1<<14)
   243  }
   244  func BenchmarkCopier32K(b *testing.B) {
   245  	benchmarkCopier(b, 1<<15)
   246  }
   247  func BenchmarkCopier64K(b *testing.B) {
   248  	benchmarkCopier(b, 1<<16)
   249  }
   250  func BenchmarkCopier128K(b *testing.B) {
   251  	benchmarkCopier(b, 1<<17)
   252  }
   253  func BenchmarkCopier256K(b *testing.B) {
   254  	benchmarkCopier(b, 1<<18)
   255  }
   256  
   257  func piped(b *testing.B, iterations int, delay time.Duration, buf []byte) io.Reader {
   258  	r, w, err := os.Pipe()
   259  	if err != nil {
   260  		b.Fatal(err)
   261  		return nil
   262  	}
   263  	go func() {
   264  		for i := 0; i < iterations; i++ {
   265  			time.Sleep(delay)
   266  			if n, err := w.Write(buf); err != nil || n != len(buf) {
   267  				if err != nil {
   268  					b.Fatal(err)
   269  				}
   270  				b.Fatal(fmt.Errorf("short write"))
   271  			}
   272  		}
   273  		w.Close()
   274  	}()
   275  	return r
   276  }
   277  
   278  func benchmarkCopier(b *testing.B, length int) {
   279  	b.StopTimer()
   280  	buf := []byte{'A'}
   281  	for len(buf) < length {
   282  		buf = append(buf, buf...)
   283  	}
   284  	buf = append(buf[:length-1], []byte{'\n'}...)
   285  	b.StartTimer()
   286  	for i := 0; i < b.N; i++ {
   287  		c := NewCopier(
   288  			map[string]io.Reader{
   289  				"buffer": piped(b, 10, time.Nanosecond, buf),
   290  			},
   291  			&BenchmarkLoggerDummy{})
   292  		c.Run()
   293  		c.Wait()
   294  		c.Close()
   295  	}
   296  }