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 }