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

     1  package jsonfilelog // import "github.com/docker/docker/daemon/logger/jsonfilelog"
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  	"testing"
    12  	"text/tabwriter"
    13  	"time"
    14  
    15  	"github.com/docker/docker/daemon/logger"
    16  	"github.com/docker/docker/daemon/logger/loggertest"
    17  	"gotest.tools/v3/assert"
    18  )
    19  
    20  func BenchmarkJSONFileLoggerReadLogs(b *testing.B) {
    21  	tmp := b.TempDir()
    22  
    23  	jsonlogger, err := New(logger.Info{
    24  		ContainerID: "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657",
    25  		LogPath:     filepath.Join(tmp, "container.log"),
    26  		Config: map[string]string{
    27  			"labels": "first,second",
    28  		},
    29  		ContainerLabels: map[string]string{
    30  			"first":  "label_value",
    31  			"second": "label_foo",
    32  		},
    33  	})
    34  	assert.NilError(b, err)
    35  	defer jsonlogger.Close()
    36  
    37  	const line = "Line that thinks that it is log line from docker\n"
    38  	ts := time.Date(2007, 1, 2, 3, 4, 5, 6, time.UTC)
    39  	msg := func() *logger.Message {
    40  		m := logger.NewMessage()
    41  		m.Line = append(m.Line, line...)
    42  		m.Source = "stderr"
    43  		m.Timestamp = ts
    44  		return m
    45  	}
    46  
    47  	var buf bytes.Buffer
    48  	assert.NilError(b, marshalMessage(msg(), nil, &buf))
    49  	b.SetBytes(int64(buf.Len()))
    50  
    51  	b.ResetTimer()
    52  
    53  	chError := make(chan error)
    54  	go func() {
    55  		for i := 0; i < b.N; i++ {
    56  			if err := jsonlogger.Log(msg()); err != nil {
    57  				chError <- err
    58  			}
    59  		}
    60  		if err := jsonlogger.Close(); err != nil {
    61  			chError <- err
    62  		}
    63  	}()
    64  
    65  	lw := jsonlogger.(*JSONFileLogger).ReadLogs(logger.ReadConfig{Follow: true})
    66  	for {
    67  		select {
    68  		case _, ok := <-lw.Msg:
    69  			if !ok {
    70  				return
    71  			}
    72  		case err := <-chError:
    73  			b.Fatal(err)
    74  		}
    75  	}
    76  }
    77  
    78  func TestEncodeDecode(t *testing.T) {
    79  	t.Parallel()
    80  
    81  	m1 := &logger.Message{Line: []byte("hello 1"), Timestamp: time.Now(), Source: "stdout"}
    82  	m2 := &logger.Message{Line: []byte("hello 2"), Timestamp: time.Now(), Source: "stdout"}
    83  	m3 := &logger.Message{Line: []byte("hello 3"), Timestamp: time.Now(), Source: "stdout"}
    84  
    85  	buf := bytes.NewBuffer(nil)
    86  	assert.Assert(t, marshalMessage(m1, nil, buf))
    87  	assert.Assert(t, marshalMessage(m2, nil, buf))
    88  	assert.Assert(t, marshalMessage(m3, nil, buf))
    89  
    90  	dec := decodeFunc(buf)
    91  	defer dec.Close()
    92  
    93  	msg, err := dec.Decode()
    94  	assert.NilError(t, err)
    95  	assert.Assert(t, string(msg.Line) == "hello 1\n", string(msg.Line))
    96  
    97  	msg, err = dec.Decode()
    98  	assert.NilError(t, err)
    99  	assert.Assert(t, string(msg.Line) == "hello 2\n")
   100  
   101  	msg, err = dec.Decode()
   102  	assert.NilError(t, err)
   103  	assert.Assert(t, string(msg.Line) == "hello 3\n")
   104  
   105  	_, err = dec.Decode()
   106  	assert.Assert(t, err == io.EOF)
   107  }
   108  
   109  func TestReadLogs(t *testing.T) {
   110  	t.Parallel()
   111  	r := loggertest.Reader{
   112  		Factory: func(t *testing.T, info logger.Info) func(*testing.T) logger.Logger {
   113  			dir := t.TempDir()
   114  			info.LogPath = filepath.Join(dir, info.ContainerID+".log")
   115  			return func(t *testing.T) logger.Logger {
   116  				l, err := New(info)
   117  				assert.NilError(t, err)
   118  				return l
   119  			}
   120  		},
   121  	}
   122  	t.Run("Tail", r.TestTail)
   123  	t.Run("Follow", r.TestFollow)
   124  }
   125  
   126  func TestTailLogsWithRotation(t *testing.T) {
   127  	t.Parallel()
   128  	compress := func(cmprs bool) {
   129  		t.Run(fmt.Sprintf("compress=%v", cmprs), func(t *testing.T) {
   130  			t.Parallel()
   131  			(&loggertest.Reader{
   132  				Factory: func(t *testing.T, info logger.Info) func(*testing.T) logger.Logger {
   133  					info.Config = map[string]string{
   134  						"compress": strconv.FormatBool(cmprs),
   135  						"max-size": "1b",
   136  						"max-file": "10",
   137  					}
   138  					dir := t.TempDir()
   139  					t.Cleanup(func() {
   140  						t.Logf("%s:\n%s", t.Name(), dirStringer{dir})
   141  					})
   142  					info.LogPath = filepath.Join(dir, info.ContainerID+".log")
   143  					return func(t *testing.T) logger.Logger {
   144  						l, err := New(info)
   145  						assert.NilError(t, err)
   146  						return l
   147  					}
   148  				},
   149  			}).TestTail(t)
   150  		})
   151  	}
   152  	compress(true)
   153  	compress(false)
   154  }
   155  
   156  func TestFollowLogsWithRotation(t *testing.T) {
   157  	t.Parallel()
   158  	compress := func(cmprs bool) {
   159  		t.Run(fmt.Sprintf("compress=%v", cmprs), func(t *testing.T) {
   160  			t.Parallel()
   161  			(&loggertest.Reader{
   162  				Factory: func(t *testing.T, info logger.Info) func(*testing.T) logger.Logger {
   163  					// The log follower can fall behind and drop logs if there are too many
   164  					// rotations in a short time. If that was to happen, loggertest would fail the
   165  					// test. Configure the logger so that there will be only one rotation with the
   166  					// set of logs that loggertest writes.
   167  					info.Config = map[string]string{
   168  						"compress": strconv.FormatBool(cmprs),
   169  						"max-size": "4096b",
   170  						"max-file": "3",
   171  					}
   172  					dir := t.TempDir()
   173  					t.Cleanup(func() {
   174  						t.Logf("%s:\n%s", t.Name(), dirStringer{dir})
   175  					})
   176  					info.LogPath = filepath.Join(dir, info.ContainerID+".log")
   177  					return func(t *testing.T) logger.Logger {
   178  						l, err := New(info)
   179  						assert.NilError(t, err)
   180  						return l
   181  					}
   182  				},
   183  			}).TestFollow(t)
   184  		})
   185  	}
   186  	compress(true)
   187  	compress(false)
   188  }
   189  
   190  type dirStringer struct {
   191  	d string
   192  }
   193  
   194  func (d dirStringer) String() string {
   195  	ls, err := os.ReadDir(d.d)
   196  	if err != nil {
   197  		return ""
   198  	}
   199  	buf := bytes.NewBuffer(nil)
   200  	tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0)
   201  	buf.WriteString("\n")
   202  
   203  	btw := bufio.NewWriter(tw)
   204  
   205  	for _, entry := range ls {
   206  		fi, err := entry.Info()
   207  		if err != nil {
   208  			return ""
   209  		}
   210  
   211  		btw.WriteString(fmt.Sprintf("%s\t%s\t%dB\t%s\n", fi.Name(), fi.Mode(), fi.Size(), fi.ModTime()))
   212  	}
   213  	btw.Flush()
   214  	tw.Flush()
   215  	return buf.String()
   216  }