github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/promtail/targets/file/filetargetmanager_test.go (about) 1 package file 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/go-kit/log" 14 "github.com/prometheus/common/model" 15 "github.com/prometheus/prometheus/discovery" 16 "github.com/prometheus/prometheus/discovery/targetgroup" 17 "gopkg.in/fsnotify.v1" 18 19 "github.com/grafana/loki/clients/pkg/promtail/api" 20 "github.com/grafana/loki/clients/pkg/promtail/client/fake" 21 "github.com/grafana/loki/clients/pkg/promtail/positions" 22 "github.com/grafana/loki/clients/pkg/promtail/scrapeconfig" 23 ) 24 25 func newTestLogDirectories(t *testing.T) string { 26 tmpDir := t.TempDir() 27 logFileDir := filepath.Join(tmpDir, "logs") 28 err := os.MkdirAll(logFileDir, 0750) 29 assert.NoError(t, err) 30 return logFileDir 31 } 32 33 func newTestPositions(logger log.Logger, filePath string) (positions.Positions, error) { 34 // Set the sync period to a really long value, to guarantee the sync timer never runs, this way we know 35 // everything saved was done through channel notifications when target.stop() was called. 36 pos, err := positions.New(logger, positions.Config{ 37 SyncPeriod: 60 * time.Second, 38 PositionsFile: filePath, 39 }) 40 if err != nil { 41 return nil, err 42 } 43 return pos, nil 44 } 45 46 func newTestFileTargetManager(logger log.Logger, client api.EntryHandler, positions positions.Positions, observePath string) (*FileTargetManager, error) { 47 targetGroup := targetgroup.Group{ 48 Targets: []model.LabelSet{{ 49 "localhost": "", 50 }}, 51 Labels: model.LabelSet{ 52 "job": "varlogs", 53 "match": "true", 54 "__path__": model.LabelValue(observePath), 55 }, 56 Source: "", 57 } 58 sc := scrapeconfig.Config{ 59 JobName: "", 60 PipelineStages: nil, 61 RelabelConfigs: nil, 62 ServiceDiscoveryConfig: scrapeconfig.ServiceDiscoveryConfig{ 63 StaticConfigs: discovery.StaticConfig{ 64 &targetGroup, 65 }, 66 }, 67 } 68 tc := &Config{ 69 SyncPeriod: 1 * time.Second, 70 } 71 72 metrics := NewMetrics(nil) 73 ftm, err := NewFileTargetManager(metrics, logger, positions, client, []scrapeconfig.Config{sc}, tc) 74 if err != nil { 75 return nil, err 76 } 77 return ftm, nil 78 } 79 80 func TestLongPositionsSyncDelayStillSavesCorrectPosition(t *testing.T) { 81 w := log.NewSyncWriter(os.Stderr) 82 logger := log.NewLogfmtLogger(w) 83 logDirName := newTestLogDirectories(t) 84 85 logFile := filepath.Join(logDirName, "test.log") 86 positionsFileName := filepath.Join(logDirName, "positions.yml") 87 ps, err := newTestPositions(logger, positionsFileName) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 client := fake.New(func() {}) 93 defer client.Stop() 94 95 ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*") 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 f, err := os.Create(logFile) 101 if err != nil { 102 t.Fatal(err) 103 } 104 105 for i := 0; i < 10; i++ { 106 _, err = f.WriteString("test\n") 107 if err != nil { 108 t.Fatal(err) 109 } 110 time.Sleep(1 * time.Millisecond) 111 } 112 113 assert.Eventually(t, func() bool { 114 return len(client.Received()) == 10 115 }, time.Second*10, time.Millisecond*1) 116 117 ftm.Stop() 118 ps.Stop() 119 120 // Assert the position value is in the correct spot. 121 val, err := ps.Get(logFile) 122 if err != nil { 123 t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error()) 124 } 125 if val != 50 { 126 t.Error("Incorrect position found, expected 50, found", val) 127 } 128 129 // Assert the number of messages the handler received is correct. 130 if len(client.Received()) != 10 { 131 t.Error("Handler did not receive the correct number of messages, expected 10 received", len(client.Received())) 132 } 133 134 // Spot check one of the messages. 135 if client.Received()[0].Line != "test" { 136 t.Error("Expected first log message to be 'test' but was", client.Received()[0]) 137 } 138 } 139 140 func TestWatchEntireDirectory(t *testing.T) { 141 w := log.NewSyncWriter(os.Stderr) 142 logger := log.NewLogfmtLogger(w) 143 logDirName := newTestLogDirectories(t) 144 145 logFile := filepath.Join(logDirName, "test.log") 146 positionsFileName := filepath.Join(logDirName, "positions.yml") 147 ps, err := newTestPositions(logger, positionsFileName) 148 if err != nil { 149 t.Fatal(err) 150 } 151 152 client := fake.New(func() {}) 153 defer client.Stop() 154 155 ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*") 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 f, err := os.Create(logFile) 161 if err != nil { 162 t.Fatal(err) 163 } 164 165 for i := 0; i < 10; i++ { 166 _, err = f.WriteString("test\n") 167 if err != nil { 168 t.Fatal(err) 169 } 170 time.Sleep(1 * time.Millisecond) 171 } 172 173 assert.Eventually(t, func() bool { 174 return len(client.Received()) == 10 175 }, time.Second*10, time.Millisecond*1) 176 177 ftm.Stop() 178 ps.Stop() 179 180 // Assert the position value is in the correct spot. 181 val, err := ps.Get(logFile) 182 if err != nil { 183 t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error()) 184 } 185 if val != 50 { 186 t.Error("Incorrect position found, expected 50, found", val) 187 } 188 189 // Assert the number of messages the handler received is correct. 190 if len(client.Received()) != 10 { 191 t.Error("Handler did not receive the correct number of messages, expected 10 received", len(client.Received())) 192 } 193 194 // Spot check one of the messages. 195 if client.Received()[0].Line != "test" { 196 t.Error("Expected first log message to be 'test' but was", client.Received()[0].Line) 197 } 198 } 199 200 func TestFileRolls(t *testing.T) { 201 w := log.NewSyncWriter(os.Stderr) 202 logger := log.NewLogfmtLogger(w) 203 logDirName := newTestLogDirectories(t) 204 205 logFile := filepath.Join(logDirName, "test.log") 206 positionsFileName := filepath.Join(logDirName, "positions.yml") 207 ps, err := newTestPositions(logger, positionsFileName) 208 if err != nil { 209 t.Fatal(err) 210 } 211 212 client := fake.New(func() {}) 213 defer client.Stop() 214 215 ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*.log") 216 if err != nil { 217 t.Fatal(err) 218 } 219 f, err := os.Create(logFile) 220 if err != nil { 221 t.Fatal(err) 222 } 223 224 for i := 0; i < 10; i++ { 225 _, err = f.WriteString("test1\n") 226 if err != nil { 227 t.Fatal(err) 228 } 229 time.Sleep(1 * time.Millisecond) 230 } 231 assert.Eventually(t, func() bool { 232 return len(client.Received()) == 10 233 }, time.Second*10, time.Millisecond*1) 234 235 // Rename the log file to something not in the pattern, then create a new file with the same name. 236 err = os.Rename(logFile, filepath.Join(logDirName, "test.log.1")) 237 if err != nil { 238 t.Fatal("Failed to rename log file for test", err) 239 } 240 f, err = os.Create(logFile) 241 if err != nil { 242 t.Fatal(err) 243 } 244 for i := 0; i < 10; i++ { 245 _, err = f.WriteString("test2\n") 246 if err != nil { 247 t.Fatal(err) 248 } 249 time.Sleep(1 * time.Millisecond) 250 } 251 assert.Eventually(t, func() bool { 252 return len(client.Received()) == 20 253 }, time.Second*10, time.Millisecond*1) 254 255 ftm.Stop() 256 ps.Stop() 257 258 if len(client.Received()) != 20 { 259 t.Error("Handler did not receive the correct number of messages, expected 20 received", len(client.Received())) 260 } 261 262 // Spot check one of the messages. 263 if client.Received()[0].Line != "test1" { 264 t.Error("Expected first log message to be 'test1' but was", client.Received()[0].Line) 265 } 266 267 // Spot check the first message from the second file. 268 if client.Received()[10].Line != "test2" { 269 t.Error("Expected first log message to be 'test2' but was", client.Received()[10].Line) 270 } 271 } 272 273 func TestResumesWhereLeftOff(t *testing.T) { 274 w := log.NewSyncWriter(os.Stderr) 275 logger := log.NewLogfmtLogger(w) 276 logDirName := newTestLogDirectories(t) 277 278 logFile := filepath.Join(logDirName, "test.log") 279 positionsFileName := filepath.Join(logDirName, "positions.yml") 280 ps, err := newTestPositions(logger, positionsFileName) 281 if err != nil { 282 t.Fatal(err) 283 } 284 285 client := fake.New(func() {}) 286 defer client.Stop() 287 288 ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*.log") 289 if err != nil { 290 t.Fatal(err) 291 } 292 f, err := os.Create(logFile) 293 if err != nil { 294 t.Fatal(err) 295 } 296 297 for i := 0; i < 10; i++ { 298 _, err = f.WriteString("test1\n") 299 if err != nil { 300 t.Fatal(err) 301 } 302 time.Sleep(1 * time.Millisecond) 303 } 304 assert.Eventually(t, func() bool { 305 return len(client.Received()) == 10 306 }, time.Second*10, time.Millisecond*1) 307 308 ftm.Stop() 309 ps.Stop() 310 311 // Create another positions (so that it loads from the previously saved positions file). 312 ps2, err := newTestPositions(logger, positionsFileName) 313 if err != nil { 314 t.Fatal(err) 315 } 316 317 // Create a new target manager, keep the same client so we can track what was sent through the handler. 318 ftm2, err := newTestFileTargetManager(logger, client, ps2, logDirName+"/*.log") 319 if err != nil { 320 t.Fatal(err) 321 } 322 323 for i := 0; i < 10; i++ { 324 _, err = f.WriteString("test2\n") 325 if err != nil { 326 t.Fatal(err) 327 } 328 time.Sleep(1 * time.Millisecond) 329 } 330 assert.Eventually(t, func() bool { 331 return len(client.Received()) == 20 332 }, time.Second*10, time.Millisecond*1) 333 334 ftm2.Stop() 335 ps2.Stop() 336 337 if len(client.Received()) != 20 { 338 t.Error("Handler did not receive the correct number of messages, expected 20 received", len(client.Received())) 339 } 340 341 // Spot check one of the messages. 342 if client.Received()[0].Line != "test1" { 343 t.Error("Expected first log message to be 'test1' but was", client.Received()[0]) 344 } 345 346 // Spot check the first message from the second file. 347 if client.Received()[10].Line != "test2" { 348 t.Error("Expected first log message to be 'test2' but was", client.Received()[10]) 349 } 350 } 351 352 func TestGlobWithMultipleFiles(t *testing.T) { 353 w := log.NewSyncWriter(os.Stderr) 354 logger := log.NewLogfmtLogger(w) 355 logDirName := newTestLogDirectories(t) 356 357 logFile1 := filepath.Join(logDirName, "test.log") 358 logFile2 := filepath.Join(logDirName, "dirt.log") 359 positionsFileName := filepath.Join(logDirName, "positions.yml") 360 ps, err := newTestPositions(logger, positionsFileName) 361 if err != nil { 362 t.Fatal(err) 363 } 364 365 client := fake.New(func() {}) 366 defer client.Stop() 367 368 ftm, err := newTestFileTargetManager(logger, client, ps, logDirName+"/*.log") 369 if err != nil { 370 t.Fatal(err) 371 } 372 373 f1, err := os.Create(logFile1) 374 if err != nil { 375 t.Fatal(err) 376 } 377 f2, err := os.Create(logFile2) 378 if err != nil { 379 t.Fatal(err) 380 } 381 382 for i := 0; i < 10; i++ { 383 _, err = f1.WriteString("test1\n") 384 if err != nil { 385 t.Fatal(err) 386 } 387 time.Sleep(1 * time.Millisecond) 388 _, err = f2.WriteString("dirt1\n") 389 if err != nil { 390 t.Fatal(err) 391 } 392 time.Sleep(1 * time.Millisecond) 393 } 394 assert.Eventually(t, func() bool { 395 return len(client.Received()) == 20 396 }, time.Second*10, time.Millisecond*1) 397 398 ftm.Stop() 399 ps.Stop() 400 401 // Assert the position value is in the correct spot. 402 val, err := ps.Get(logFile1) 403 if err != nil { 404 t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error()) 405 } 406 if val != 60 { 407 t.Error("Incorrect position found for file 1, expected 60, found", val) 408 } 409 val, err = ps.Get(logFile2) 410 if err != nil { 411 t.Errorf("Positions file did not contain any data for our test log file, err: %s", err.Error()) 412 } 413 if val != 60 { 414 t.Error("Incorrect position found for file 2, expected 60, found", val) 415 } 416 417 // Assert the number of messages the handler received is correct. 418 if len(client.Received()) != 20 { 419 t.Error("Handler did not receive the correct number of messages, expected 20 received", len(client.Received())) 420 } 421 } 422 423 func TestDeadlockTargetManager(t *testing.T) { 424 client := fake.New(func() {}) 425 defer client.Stop() 426 427 targetEventHandler := make(chan fileTargetEvent) 428 defer func() { 429 close(targetEventHandler) 430 }() 431 432 syncer := &targetSyncer{ 433 metrics: NewMetrics(nil), 434 log: log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), 435 positions: nil, 436 entryHandler: client, 437 hostname: "localhost", 438 fileEventWatchers: make(map[string]chan fsnotify.Event), 439 targets: make(map[string]*FileTarget), 440 targetConfig: &Config{ 441 SyncPeriod: time.Hour, 442 }, 443 } 444 445 syncer.sync([]*targetgroup.Group{ 446 { 447 Targets: []model.LabelSet{ 448 { 449 hostLabel: "localhost", 450 pathLabel: "baz", 451 "job": "bar", 452 }, 453 }, 454 }, 455 }, targetEventHandler) 456 457 require.Equal(t, len(syncer.targets), 1) 458 require.Equal(t, len(syncer.fileEventWatchers), 1) 459 460 syncer.sync([]*targetgroup.Group{ 461 { 462 Targets: []model.LabelSet{ 463 {}, 464 }, 465 }, 466 }, targetEventHandler) 467 468 syncer.sendFileCreateEvent(fsnotify.Event{Name: "baz"}) 469 470 require.Equal(t, len(syncer.targets), 0) 471 require.Equal(t, len(syncer.fileEventWatchers), 0) 472 } 473 474 func TestDeadlockStartWatchingDuringSync(t *testing.T) { 475 w := log.NewSyncWriter(os.Stderr) 476 logger := log.NewLogfmtLogger(w) 477 oldLogDir := newTestLogDirectories(t) 478 newLogDir := newTestLogDirectories(t) 479 480 positionsFileName := filepath.Join(oldLogDir, "positions.yml") 481 ps, err := newTestPositions(logger, positionsFileName) 482 assert.NoError(t, err) 483 484 client := fake.New(func() {}) 485 defer client.Stop() 486 487 ftm, err := newTestFileTargetManager(logger, client, ps, oldLogDir+"/*") 488 assert.NoError(t, err) 489 490 done := make(chan struct{}) 491 go func() { 492 for i := 0; i < 10; i++ { 493 dir := filepath.Join(newLogDir, fmt.Sprintf("%d", i)) 494 err := os.MkdirAll(dir, 0750) 495 assert.NoError(t, err) 496 time.Sleep(1 * time.Millisecond) 497 for j := 0; j < 100; j++ { 498 logFile := filepath.Join(dir, fmt.Sprintf("test%d.log", j)) 499 f, err := os.Create(logFile) 500 assert.NoError(t, err) 501 _, err = f.WriteString("just a log line\n") 502 assert.NoError(t, err) 503 err = f.Close() 504 assert.NoError(t, err) 505 time.Sleep(1 * time.Millisecond) 506 } 507 } 508 close(done) 509 }() 510 511 tg := targetgroup.Group{ 512 Targets: []model.LabelSet{{ 513 "localhost": "", 514 }}, 515 Labels: model.LabelSet{ 516 "job": "varlogs", 517 "match": "true", 518 "__path__": model.LabelValue(newLogDir + "/**/*.log"), 519 }, 520 Source: "", 521 } 522 for i := 0; i < 10; i++ { 523 ftm.syncers[""].sync([]*targetgroup.Group{&tg}, ftm.targetEventHandler) 524 } 525 <-done 526 527 ftm.Stop() 528 ps.Stop() 529 }