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 }