github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/logs_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "math/rand" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/juju/loggo" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/version" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/names.v2" 17 "gopkg.in/mgo.v2" 18 "gopkg.in/mgo.v2/bson" 19 20 "github.com/juju/juju/state" 21 coretesting "github.com/juju/juju/testing" 22 jujuversion "github.com/juju/juju/version" 23 ) 24 25 type LogsSuite struct { 26 ConnSuite 27 logsColl *mgo.Collection 28 } 29 30 var _ = gc.Suite(&LogsSuite{}) 31 32 func (s *LogsSuite) SetUpTest(c *gc.C) { 33 s.ConnSuite.SetUpTest(c) 34 35 session := s.State.MongoSession() 36 s.logsColl = session.DB("logs").C("logs") 37 } 38 39 func (s *LogsSuite) TestLastSentLogTrackerSetGet(c *gc.C) { 40 tracker := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink") 41 defer tracker.Close() 42 43 err := tracker.Set(10, 100) 44 c.Assert(err, jc.ErrorIsNil) 45 id1, ts1, err := tracker.Get() 46 c.Assert(err, jc.ErrorIsNil) 47 err = tracker.Set(20, 200) 48 c.Assert(err, jc.ErrorIsNil) 49 id2, ts2, err := tracker.Get() 50 c.Assert(err, jc.ErrorIsNil) 51 52 c.Check(id1, gc.Equals, int64(10)) 53 c.Check(ts1, gc.Equals, int64(100)) 54 c.Check(id2, gc.Equals, int64(20)) 55 c.Check(ts2, gc.Equals, int64(200)) 56 } 57 58 func (s *LogsSuite) TestLastSentLogTrackerGetNeverSet(c *gc.C) { 59 tracker := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test") 60 defer tracker.Close() 61 62 _, _, err := tracker.Get() 63 64 c.Check(err, gc.ErrorMatches, state.ErrNeverForwarded.Error()) 65 } 66 67 func (s *LogsSuite) TestLastSentLogTrackerIndependentModels(c *gc.C) { 68 tracker0 := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink") 69 defer tracker0.Close() 70 otherModel := s.NewStateForModelNamed(c, "test-model") 71 defer otherModel.Close() 72 tracker1 := state.NewLastSentLogTracker(otherModel, otherModel.ModelUUID(), "test-sink") // same sink 73 defer tracker1.Close() 74 err := tracker0.Set(10, 100) 75 c.Assert(err, jc.ErrorIsNil) 76 id0, ts0, err := tracker0.Get() 77 c.Assert(err, jc.ErrorIsNil) 78 c.Assert(id0, gc.Equals, int64(10)) 79 c.Assert(ts0, gc.Equals, int64(100)) 80 81 _, _, errBefore := tracker1.Get() 82 err = tracker1.Set(20, 200) 83 c.Assert(err, jc.ErrorIsNil) 84 id1, ts1, errAfter := tracker1.Get() 85 c.Assert(errAfter, jc.ErrorIsNil) 86 id0, ts0, err = tracker0.Get() 87 c.Assert(err, jc.ErrorIsNil) 88 89 c.Check(errBefore, gc.ErrorMatches, state.ErrNeverForwarded.Error()) 90 c.Check(id1, gc.Equals, int64(20)) 91 c.Check(ts1, gc.Equals, int64(200)) 92 c.Check(id0, gc.Equals, int64(10)) 93 c.Check(ts0, gc.Equals, int64(100)) 94 } 95 96 func (s *LogsSuite) TestLastSentLogTrackerIndependentSinks(c *gc.C) { 97 tracker0 := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink0") 98 defer tracker0.Close() 99 tracker1 := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink1") 100 defer tracker1.Close() 101 err := tracker0.Set(10, 100) 102 c.Assert(err, jc.ErrorIsNil) 103 id0, ts0, err := tracker0.Get() 104 c.Assert(err, jc.ErrorIsNil) 105 c.Assert(id0, gc.Equals, int64(10)) 106 c.Assert(ts0, gc.Equals, int64(100)) 107 108 _, _, errBefore := tracker1.Get() 109 err = tracker1.Set(20, 200) 110 c.Assert(err, jc.ErrorIsNil) 111 id1, ts1, errAfter := tracker1.Get() 112 c.Assert(errAfter, jc.ErrorIsNil) 113 id0, ts0, err = tracker0.Get() 114 c.Assert(err, jc.ErrorIsNil) 115 116 c.Check(errBefore, gc.ErrorMatches, state.ErrNeverForwarded.Error()) 117 c.Check(id1, gc.Equals, int64(20)) 118 c.Check(ts1, gc.Equals, int64(200)) 119 c.Check(id0, gc.Equals, int64(10)) 120 c.Check(ts0, gc.Equals, int64(100)) 121 } 122 123 func (s *LogsSuite) TestAllLastSentLogTrackerSetGet(c *gc.C) { 124 tracker, err := state.NewAllLastSentLogTracker(s.State, "test-sink") 125 c.Assert(err, jc.ErrorIsNil) 126 defer tracker.Close() 127 128 err = tracker.Set(10, 100) 129 c.Assert(err, jc.ErrorIsNil) 130 id1, ts1, err := tracker.Get() 131 c.Assert(err, jc.ErrorIsNil) 132 err = tracker.Set(20, 200) 133 c.Assert(err, jc.ErrorIsNil) 134 id2, ts2, err := tracker.Get() 135 c.Assert(err, jc.ErrorIsNil) 136 137 c.Check(id1, gc.Equals, int64(10)) 138 c.Check(ts1, gc.Equals, int64(100)) 139 c.Check(id2, gc.Equals, int64(20)) 140 c.Check(ts2, gc.Equals, int64(200)) 141 } 142 143 func (s *LogsSuite) TestAllLastSentLogTrackerNotController(c *gc.C) { 144 st := s.NewStateForModelNamed(c, "test-model") 145 defer st.Close() 146 147 _, err := state.NewAllLastSentLogTracker(st, "test") 148 149 c.Check(err, gc.ErrorMatches, `only the admin model can track all log records`) 150 } 151 152 func (s *LogsSuite) TestIndexesCreated(c *gc.C) { 153 // Indexes should be created on the logs collection when state is opened. 154 indexes, err := s.logsColl.Indexes() 155 c.Assert(err, jc.ErrorIsNil) 156 var keys []string 157 for _, index := range indexes { 158 keys = append(keys, strings.Join(index.Key, "-")) 159 } 160 c.Assert(keys, jc.SameContents, []string{ 161 "_id", // default index 162 "e-t", // model-uuid and timestamp 163 "e-n", // model-uuid and entity 164 }) 165 } 166 167 func (s *LogsSuite) TestDbLogger(c *gc.C) { 168 logger := state.NewDbLogger(s.State, names.NewMachineTag("22"), jujuversion.Current) 169 defer logger.Close() 170 t0 := coretesting.ZeroTime().Truncate(time.Millisecond) // MongoDB only stores timestamps with ms precision. 171 logger.Log(t0, "some.where", "foo.go:99", loggo.INFO, "all is well") 172 t1 := t0.Add(time.Second) 173 logger.Log(t1, "else.where", "bar.go:42", loggo.ERROR, "oh noes") 174 175 var docs []bson.M 176 err := s.logsColl.Find(nil).Sort("t").All(&docs) 177 c.Assert(err, jc.ErrorIsNil) 178 c.Assert(docs, gc.HasLen, 2) 179 180 c.Assert(docs[0]["t"], gc.Equals, t0.UnixNano()) 181 c.Assert(docs[0]["e"], gc.Equals, s.State.ModelUUID()) 182 c.Assert(docs[0]["n"], gc.Equals, "machine-22") 183 c.Assert(docs[0]["m"], gc.Equals, "some.where") 184 c.Assert(docs[0]["l"], gc.Equals, "foo.go:99") 185 c.Assert(docs[0]["v"], gc.Equals, int(loggo.INFO)) 186 c.Assert(docs[0]["x"], gc.Equals, "all is well") 187 188 c.Assert(docs[1]["t"], gc.Equals, t1.UnixNano()) 189 c.Assert(docs[1]["e"], gc.Equals, s.State.ModelUUID()) 190 c.Assert(docs[1]["n"], gc.Equals, "machine-22") 191 c.Assert(docs[1]["m"], gc.Equals, "else.where") 192 c.Assert(docs[1]["l"], gc.Equals, "bar.go:42") 193 c.Assert(docs[1]["v"], gc.Equals, int(loggo.ERROR)) 194 c.Assert(docs[1]["x"], gc.Equals, "oh noes") 195 } 196 197 func (s *LogsSuite) TestPruneLogsByTime(c *gc.C) { 198 dbLogger := state.NewDbLogger(s.State, names.NewMachineTag("22"), jujuversion.Current) 199 defer dbLogger.Close() 200 log := func(t time.Time, msg string) { 201 err := dbLogger.Log(t, "module", "loc", loggo.INFO, msg) 202 c.Assert(err, jc.ErrorIsNil) 203 } 204 205 now := coretesting.NonZeroTime() 206 maxLogTime := now.Add(-time.Minute) 207 log(now, "keep") 208 log(maxLogTime.Add(time.Second), "keep") 209 log(maxLogTime, "keep") 210 log(maxLogTime.Add(-time.Second), "prune") 211 log(maxLogTime.Add(-(2 * time.Second)), "prune") 212 213 noPruneMB := 100 214 err := state.PruneLogs(s.State, maxLogTime, noPruneMB) 215 c.Assert(err, jc.ErrorIsNil) 216 217 // After pruning there should just be 3 "keep" messages left. 218 var docs []bson.M 219 err = s.logsColl.Find(nil).All(&docs) 220 c.Assert(err, jc.ErrorIsNil) 221 c.Assert(docs, gc.HasLen, 3) 222 for _, doc := range docs { 223 c.Assert(doc["x"], gc.Equals, "keep") 224 } 225 } 226 227 func (s *LogsSuite) TestPruneLogsBySize(c *gc.C) { 228 // Set up 3 models and generate different amounts of logs 229 // for them. 230 now := coretesting.NonZeroTime().Truncate(time.Millisecond) 231 232 s0 := s.State 233 startingLogsS0 := 10 234 s.generateLogs(c, s0, now, startingLogsS0) 235 236 s1 := s.Factory.MakeModel(c, nil) 237 defer s1.Close() 238 startingLogsS1 := 10000 239 s.generateLogs(c, s1, now, startingLogsS1) 240 241 s2 := s.Factory.MakeModel(c, nil) 242 defer s2.Close() 243 startingLogsS2 := 12000 244 s.generateLogs(c, s2, now, startingLogsS2) 245 246 // Prune logs collection back to 1 MiB. 247 tsNoPrune := coretesting.NonZeroTime().Add(-3 * 24 * time.Hour) 248 err := state.PruneLogs(s.State, tsNoPrune, 1) 249 c.Assert(err, jc.ErrorIsNil) 250 251 // Logs for first env should not be touched. 252 c.Assert(s.countLogs(c, s0), gc.Equals, startingLogsS0) 253 254 // Logs for second env should be pruned. 255 c.Assert(s.countLogs(c, s1), jc.LessThan, startingLogsS1) 256 257 // Logs for third env should be pruned to a similar level as 258 // second env. 259 c.Assert(s.countLogs(c, s2), jc.LessThan, startingLogsS1) 260 261 // Ensure that the latest log records are still there. 262 assertLatestTs := func(st *state.State) { 263 var doc bson.M 264 err := s.logsColl.Find(bson.M{"e": st.ModelUUID()}).Sort("-t").One(&doc) 265 c.Assert(err, jc.ErrorIsNil) 266 c.Assert(doc["t"], gc.Equals, now.UnixNano()) 267 } 268 assertLatestTs(s0) 269 assertLatestTs(s1) 270 assertLatestTs(s2) 271 } 272 273 func (s *LogsSuite) generateLogs(c *gc.C, st *state.State, endTime time.Time, count int) { 274 dbLogger := state.NewDbLogger(st, names.NewMachineTag("0"), jujuversion.Current) 275 defer dbLogger.Close() 276 for i := 0; i < count; i++ { 277 ts := endTime.Add(-time.Duration(i) * time.Second) 278 err := dbLogger.Log(ts, "module", "loc", loggo.INFO, "message") 279 c.Assert(err, jc.ErrorIsNil) 280 } 281 } 282 283 func (s *LogsSuite) countLogs(c *gc.C, st *state.State) int { 284 count, err := s.logsColl.Find(bson.M{"e": st.ModelUUID()}).Count() 285 c.Assert(err, jc.ErrorIsNil) 286 return count 287 } 288 289 type LogTailerSuite struct { 290 ConnSuite 291 logsColl *mgo.Collection 292 oplogColl *mgo.Collection 293 otherState *state.State 294 } 295 296 var _ = gc.Suite(&LogTailerSuite{}) 297 298 func (s *LogTailerSuite) SetUpTest(c *gc.C) { 299 s.ConnSuite.SetUpTest(c) 300 301 session := s.State.MongoSession() 302 s.logsColl = session.DB("logs").C("logs") 303 304 // Create a fake oplog collection. 305 s.oplogColl = session.DB("logs").C("oplog.fake") 306 err := s.oplogColl.Create(&mgo.CollectionInfo{ 307 Capped: true, 308 MaxBytes: 1024 * 1024, 309 }) 310 c.Assert(err, jc.ErrorIsNil) 311 s.AddCleanup(func(*gc.C) { s.oplogColl.DropCollection() }) 312 313 s.otherState = s.NewStateForModelNamed(c, "test-model") 314 c.Assert(s.otherState, gc.NotNil) 315 s.AddCleanup(func(c *gc.C) { 316 err := s.otherState.Close() 317 c.Assert(err, jc.ErrorIsNil) 318 }) 319 } 320 321 func (s *LogTailerSuite) TestTimeFiltering(c *gc.C) { 322 // Add 10 logs that shouldn't be returned. 323 threshT := coretesting.NonZeroTime() 324 s.writeLogsT(c, 325 threshT.Add(-5*time.Second), threshT.Add(-time.Millisecond), 5, 326 logTemplate{Message: "dont want"}, 327 ) 328 329 // Add 5 logs that should be returned. 330 want := logTemplate{Message: "want"} 331 s.writeLogsT(c, threshT, threshT.Add(5*time.Second), 5, want) 332 tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{ 333 StartTime: threshT, 334 Oplog: s.oplogColl, 335 }) 336 c.Assert(err, jc.ErrorIsNil) 337 defer tailer.Stop() 338 s.assertTailer(c, tailer, 5, want) 339 340 // Write more logs. These will be read from the the oplog. 341 want2 := logTemplate{Message: "want 2"} 342 s.writeLogsT(c, threshT.Add(6*time.Second), threshT.Add(10*time.Second), 5, want2) 343 s.assertTailer(c, tailer, 5, want2) 344 345 } 346 347 func (s *LogTailerSuite) TestOplogTransition(c *gc.C) { 348 // Ensure that logs aren't repeated as the log tailer moves from 349 // reading from the logs collection to tailing the oplog. 350 // 351 // All logs are written out with the same timestamp to create a 352 // challenging scenario for the tailer. 353 354 for i := 0; i < 5; i++ { 355 s.writeLogs(c, 1, logTemplate{Message: strconv.Itoa(i)}) 356 } 357 358 tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{ 359 Oplog: s.oplogColl, 360 }) 361 c.Assert(err, jc.ErrorIsNil) 362 defer tailer.Stop() 363 for i := 0; i < 5; i++ { 364 s.assertTailer(c, tailer, 1, logTemplate{Message: strconv.Itoa(i)}) 365 } 366 367 // Write more logs. These will be read from the the oplog. 368 for i := 5; i < 10; i++ { 369 lt := logTemplate{Message: strconv.Itoa(i)} 370 s.writeLogs(c, 2, lt) 371 s.assertTailer(c, tailer, 2, lt) 372 } 373 } 374 375 func (s *LogTailerSuite) TestModelFiltering(c *gc.C) { 376 good := logTemplate{Message: "good"} 377 writeLogs := func() { 378 s.writeLogs(c, 1, logTemplate{ 379 ModelUUID: "someuuid0", 380 Message: "bad", 381 }) 382 s.writeLogs(c, 1, logTemplate{ 383 ModelUUID: "someuuid1", 384 Message: "bad", 385 }) 386 s.writeLogs(c, 1, good) 387 } 388 389 assert := func(tailer state.LogTailer) { 390 // Only the entries the s.State's UUID should be reported. 391 s.assertTailer(c, tailer, 1, good) 392 } 393 394 s.checkLogTailerFiltering(c, s.otherState, &state.LogTailerParams{}, writeLogs, assert) 395 } 396 397 func (s *LogTailerSuite) TestTailingLogsForAllModels(c *gc.C) { 398 writeLogs := func() { 399 s.writeLogs(c, 1, logTemplate{ 400 ModelUUID: "someuuid0", 401 Message: "bad", 402 }) 403 s.writeLogs(c, 1, logTemplate{ 404 ModelUUID: "someuuid1", 405 Message: "bad", 406 }) 407 s.writeLogs(c, 1, logTemplate{Message: "good"}) 408 } 409 410 assert := func(tailer state.LogTailer) { 411 messagesFrom := map[string]bool{ 412 s.otherState.ModelUUID(): false, 413 "someuuid0": false, 414 "someuuid1": false, 415 } 416 defer func() { 417 c.Assert(messagesFrom, gc.HasLen, 3) 418 for _, v := range messagesFrom { 419 c.Assert(v, jc.IsTrue) 420 } 421 }() 422 count := 0 423 for { 424 select { 425 case log := <-tailer.Logs(): 426 messagesFrom[log.ModelUUID] = true 427 count++ 428 c.Logf("count %d", count) 429 if count >= 3 { 430 return 431 } 432 case <-time.After(coretesting.ShortWait): 433 c.Fatalf("timeout waiting for logs %d", count) 434 } 435 } 436 } 437 s.checkLogTailerFiltering(c, s.State, &state.LogTailerParams{AllModels: true}, writeLogs, assert) 438 } 439 440 func (s *LogTailerSuite) TestTailingLogsOnlyForControllerModel(c *gc.C) { 441 writeLogs := func() { 442 s.writeLogs(c, 1, logTemplate{ 443 ModelUUID: s.otherState.ModelUUID(), 444 Message: "bad"}, 445 ) 446 s.writeLogs(c, 1, logTemplate{ 447 ModelUUID: s.State.ModelUUID(), 448 Message: "good1", 449 }) 450 s.writeLogs(c, 1, logTemplate{ 451 ModelUUID: s.State.ModelUUID(), 452 Message: "good2", 453 }) 454 } 455 456 assert := func(tailer state.LogTailer) { 457 messages := map[string]bool{} 458 defer func() { 459 c.Assert(messages, gc.HasLen, 2) 460 for m, _ := range messages { 461 if m != "good1" && m != "good2" { 462 c.Fatalf("received message: %v", m) 463 } 464 } 465 }() 466 count := 0 467 for { 468 select { 469 case log := <-tailer.Logs(): 470 c.Assert(log.ModelUUID, gc.Equals, s.State.ModelUUID()) 471 messages[log.Message] = true 472 count++ 473 c.Logf("count %d", count) 474 if count >= 2 { 475 return 476 } 477 case <-time.After(coretesting.ShortWait): 478 c.Fatalf("timeout waiting for logs %d", count) 479 } 480 } 481 } 482 s.checkLogTailerFiltering(c, s.State, &state.LogTailerParams{}, writeLogs, assert) 483 } 484 485 func (s *LogTailerSuite) TestTailingAllLogsFromNonController(c *gc.C) { 486 _, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{AllModels: true}) 487 c.Assert(err, gc.ErrorMatches, "not allowed to tail logs from all models: not a controller") 488 } 489 490 func (s *LogTailerSuite) TestLevelFiltering(c *gc.C) { 491 info := logTemplate{Level: loggo.INFO} 492 error := logTemplate{Level: loggo.ERROR} 493 writeLogs := func() { 494 s.writeLogs(c, 1, logTemplate{Level: loggo.DEBUG}) 495 s.writeLogs(c, 1, info) 496 s.writeLogs(c, 1, error) 497 } 498 params := &state.LogTailerParams{ 499 MinLevel: loggo.INFO, 500 } 501 assert := func(tailer state.LogTailer) { 502 s.assertTailer(c, tailer, 1, info) 503 s.assertTailer(c, tailer, 1, error) 504 } 505 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 506 } 507 508 func (s *LogTailerSuite) TestInitialLines(c *gc.C) { 509 expected := logTemplate{Message: "want"} 510 s.writeLogs(c, 3, logTemplate{Message: "dont want"}) 511 s.writeLogs(c, 5, expected) 512 513 tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{ 514 InitialLines: 5, 515 }) 516 c.Assert(err, jc.ErrorIsNil) 517 defer tailer.Stop() 518 519 // Should see just the last 5 lines as requested. 520 s.assertTailer(c, tailer, 5, expected) 521 } 522 523 func (s *LogTailerSuite) TestInitialLinesWithNotEnoughLines(c *gc.C) { 524 expected := logTemplate{Message: "want"} 525 s.writeLogs(c, 2, expected) 526 527 tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{ 528 InitialLines: 5, 529 }) 530 c.Assert(err, jc.ErrorIsNil) 531 defer tailer.Stop() 532 533 // Should see just the 2 lines that existed, even though 5 were 534 // asked for. 535 s.assertTailer(c, tailer, 2, expected) 536 } 537 538 func (s *LogTailerSuite) TestNoTail(c *gc.C) { 539 expected := logTemplate{Message: "want"} 540 s.writeLogs(c, 2, expected) 541 542 // Write a log entry that's only in the oplog. 543 doc := s.logTemplateToDoc(logTemplate{Message: "dont want"}, coretesting.ZeroTime()) 544 err := s.writeLogToOplog(doc) 545 c.Assert(err, jc.ErrorIsNil) 546 547 tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{ 548 NoTail: true, 549 }) 550 c.Assert(err, jc.ErrorIsNil) 551 // Not strictly necessary, just in case NoTail doesn't work in the test. 552 defer tailer.Stop() 553 554 // Logs only in the oplog shouldn't be reported and the tailer 555 // should stop itself once the log collection has been read. 556 s.assertTailer(c, tailer, 2, expected) 557 select { 558 case _, ok := <-tailer.Logs(): 559 if ok { 560 c.Fatal("shouldn't be any further logs") 561 } 562 case <-time.After(coretesting.LongWait): 563 c.Fatal("timed out waiting for logs channel to close") 564 } 565 566 select { 567 case <-tailer.Dying(): 568 // Success. 569 case <-time.After(coretesting.LongWait): 570 c.Fatal("tailer didn't stop itself") 571 } 572 } 573 574 func (s *LogTailerSuite) TestIncludeEntity(c *gc.C) { 575 machine0 := logTemplate{Entity: names.NewMachineTag("0")} 576 foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")} 577 foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")} 578 writeLogs := func() { 579 s.writeLogs(c, 3, machine0) 580 s.writeLogs(c, 2, foo0) 581 s.writeLogs(c, 1, foo1) 582 s.writeLogs(c, 3, machine0) 583 } 584 params := &state.LogTailerParams{ 585 IncludeEntity: []string{ 586 "unit-foo-0", 587 "unit-foo-1", 588 }, 589 } 590 assert := func(tailer state.LogTailer) { 591 s.assertTailer(c, tailer, 2, foo0) 592 s.assertTailer(c, tailer, 1, foo1) 593 } 594 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 595 } 596 597 func (s *LogTailerSuite) TestIncludeEntityWildcard(c *gc.C) { 598 machine0 := logTemplate{Entity: names.NewMachineTag("0")} 599 foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")} 600 foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")} 601 writeLogs := func() { 602 s.writeLogs(c, 3, machine0) 603 s.writeLogs(c, 2, foo0) 604 s.writeLogs(c, 1, foo1) 605 s.writeLogs(c, 3, machine0) 606 } 607 params := &state.LogTailerParams{ 608 IncludeEntity: []string{ 609 "unit-foo*", 610 }, 611 } 612 assert := func(tailer state.LogTailer) { 613 s.assertTailer(c, tailer, 2, foo0) 614 s.assertTailer(c, tailer, 1, foo1) 615 } 616 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 617 } 618 619 func (s *LogTailerSuite) TestExcludeEntity(c *gc.C) { 620 machine0 := logTemplate{Entity: names.NewMachineTag("0")} 621 foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")} 622 foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")} 623 writeLogs := func() { 624 s.writeLogs(c, 3, machine0) 625 s.writeLogs(c, 2, foo0) 626 s.writeLogs(c, 1, foo1) 627 s.writeLogs(c, 3, machine0) 628 } 629 params := &state.LogTailerParams{ 630 ExcludeEntity: []string{ 631 "machine-0", 632 "unit-foo-0", 633 }, 634 } 635 assert := func(tailer state.LogTailer) { 636 s.assertTailer(c, tailer, 1, foo1) 637 } 638 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 639 } 640 641 func (s *LogTailerSuite) TestExcludeEntityWildcard(c *gc.C) { 642 machine0 := logTemplate{Entity: names.NewMachineTag("0")} 643 foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")} 644 foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")} 645 writeLogs := func() { 646 s.writeLogs(c, 3, machine0) 647 s.writeLogs(c, 2, foo0) 648 s.writeLogs(c, 1, foo1) 649 s.writeLogs(c, 3, machine0) 650 } 651 params := &state.LogTailerParams{ 652 ExcludeEntity: []string{ 653 "machine*", 654 "unit-*-0", 655 }, 656 } 657 assert := func(tailer state.LogTailer) { 658 s.assertTailer(c, tailer, 1, foo1) 659 } 660 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 661 } 662 func (s *LogTailerSuite) TestIncludeModule(c *gc.C) { 663 mod0 := logTemplate{Module: "foo.bar"} 664 mod1 := logTemplate{Module: "juju.thing"} 665 subMod1 := logTemplate{Module: "juju.thing.hai"} 666 mod2 := logTemplate{Module: "elsewhere"} 667 writeLogs := func() { 668 s.writeLogs(c, 1, mod0) 669 s.writeLogs(c, 1, mod1) 670 s.writeLogs(c, 1, mod0) 671 s.writeLogs(c, 1, subMod1) 672 s.writeLogs(c, 1, mod0) 673 s.writeLogs(c, 1, mod2) 674 } 675 params := &state.LogTailerParams{ 676 IncludeModule: []string{"juju.thing", "elsewhere"}, 677 } 678 assert := func(tailer state.LogTailer) { 679 s.assertTailer(c, tailer, 1, mod1) 680 s.assertTailer(c, tailer, 1, subMod1) 681 s.assertTailer(c, tailer, 1, mod2) 682 } 683 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 684 } 685 686 func (s *LogTailerSuite) TestExcludeModule(c *gc.C) { 687 mod0 := logTemplate{Module: "foo.bar"} 688 mod1 := logTemplate{Module: "juju.thing"} 689 subMod1 := logTemplate{Module: "juju.thing.hai"} 690 mod2 := logTemplate{Module: "elsewhere"} 691 writeLogs := func() { 692 s.writeLogs(c, 1, mod0) 693 s.writeLogs(c, 1, mod1) 694 s.writeLogs(c, 1, mod0) 695 s.writeLogs(c, 1, subMod1) 696 s.writeLogs(c, 1, mod0) 697 s.writeLogs(c, 1, mod2) 698 } 699 params := &state.LogTailerParams{ 700 ExcludeModule: []string{"juju.thing", "elsewhere"}, 701 } 702 assert := func(tailer state.LogTailer) { 703 s.assertTailer(c, tailer, 2, mod0) 704 } 705 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 706 } 707 708 func (s *LogTailerSuite) TestIncludeExcludeModule(c *gc.C) { 709 foo := logTemplate{Module: "foo"} 710 bar := logTemplate{Module: "bar"} 711 barSub := logTemplate{Module: "bar.thing"} 712 baz := logTemplate{Module: "baz"} 713 qux := logTemplate{Module: "qux"} 714 writeLogs := func() { 715 s.writeLogs(c, 1, foo) 716 s.writeLogs(c, 1, bar) 717 s.writeLogs(c, 1, barSub) 718 s.writeLogs(c, 1, baz) 719 s.writeLogs(c, 1, qux) 720 } 721 params := &state.LogTailerParams{ 722 IncludeModule: []string{"foo", "bar", "qux"}, 723 ExcludeModule: []string{"foo", "bar"}, 724 } 725 assert := func(tailer state.LogTailer) { 726 // Except just "qux" because "foo" and "bar" were included and 727 // then excluded. 728 s.assertTailer(c, tailer, 1, qux) 729 } 730 s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert) 731 } 732 733 func (s *LogTailerSuite) checkLogTailerFiltering( 734 c *gc.C, 735 st *state.State, 736 params *state.LogTailerParams, 737 writeLogs func(), 738 assertTailer func(state.LogTailer), 739 ) { 740 // Check the tailer does the right thing when reading from the 741 // logs collection. 742 writeLogs() 743 params.Oplog = s.oplogColl 744 tailer, err := state.NewLogTailer(st, params) 745 c.Assert(err, jc.ErrorIsNil) 746 defer tailer.Stop() 747 assertTailer(tailer) 748 749 // Now write out logs and check the tailer again. These will be 750 // read from the oplog. 751 writeLogs() 752 assertTailer(tailer) 753 } 754 755 type logTemplate struct { 756 ModelUUID string 757 Entity names.Tag 758 Version version.Number 759 Module string 760 Location string 761 Level loggo.Level 762 Message string 763 } 764 765 // writeLogs creates count log messages at the current time using 766 // the supplied template. As well as writing to the logs collection, 767 // entries are also made into the fake oplog collection. 768 func (s *LogTailerSuite) writeLogs(c *gc.C, count int, lt logTemplate) { 769 t := coretesting.ZeroTime() 770 s.writeLogsT(c, t, t, count, lt) 771 } 772 773 // writeLogsT creates count log messages between startTime and 774 // endTime using the supplied template. As well as writing to the logs 775 // collection, entries are also made into the fake oplog collection. 776 func (s *LogTailerSuite) writeLogsT(c *gc.C, startTime, endTime time.Time, count int, lt logTemplate) { 777 interval := endTime.Sub(startTime) / time.Duration(count) 778 t := startTime 779 for i := 0; i < count; i++ { 780 doc := s.logTemplateToDoc(lt, t) 781 err := s.writeLogToOplog(doc) 782 c.Assert(err, jc.ErrorIsNil) 783 err = s.logsColl.Insert(doc) 784 c.Assert(err, jc.ErrorIsNil) 785 t = t.Add(interval) 786 } 787 } 788 789 // writeLogToOplog writes out a log record to the a (probably fake) 790 // oplog collection. 791 func (s *LogTailerSuite) writeLogToOplog(doc interface{}) error { 792 return s.oplogColl.Insert(bson.D{ 793 {"ts", bson.MongoTimestamp(coretesting.ZeroTime().Unix() << 32)}, // an approximation which will do 794 {"h", rand.Int63()}, // again, a suitable fake 795 {"op", "i"}, // this will always be an insert 796 {"ns", "logs.logs"}, 797 {"o", doc}, 798 }) 799 } 800 801 func (s *LogTailerSuite) normaliseLogTemplate(lt *logTemplate) { 802 if lt.ModelUUID == "" { 803 lt.ModelUUID = s.otherState.ModelUUID() 804 } 805 if lt.Entity == nil { 806 lt.Entity = names.NewMachineTag("0") 807 } 808 if lt.Version == version.Zero { 809 lt.Version = jujuversion.Current 810 } 811 if lt.Module == "" { 812 lt.Module = "module" 813 } 814 if lt.Location == "" { 815 lt.Location = "loc" 816 } 817 if lt.Level == loggo.UNSPECIFIED { 818 lt.Level = loggo.INFO 819 } 820 if lt.Message == "" { 821 lt.Message = "message" 822 } 823 } 824 825 func (s *LogTailerSuite) logTemplateToDoc(lt logTemplate, t time.Time) interface{} { 826 s.normaliseLogTemplate(<) 827 return state.MakeLogDoc( 828 lt.ModelUUID, 829 lt.Entity, 830 t, 831 lt.Module, 832 lt.Location, 833 lt.Level, 834 lt.Message, 835 ) 836 } 837 838 func (s *LogTailerSuite) assertTailer(c *gc.C, tailer state.LogTailer, expectedCount int, lt logTemplate) { 839 s.normaliseLogTemplate(<) 840 841 timeout := time.After(coretesting.LongWait) 842 count := 0 843 for { 844 select { 845 case log, ok := <-tailer.Logs(): 846 if !ok { 847 c.Fatalf("tailer died unexpectedly: %v", tailer.Err()) 848 } 849 c.Assert(log.Version, gc.Equals, lt.Version) 850 c.Assert(log.Entity, gc.Equals, lt.Entity) 851 c.Assert(log.Module, gc.Equals, lt.Module) 852 c.Assert(log.Location, gc.Equals, lt.Location) 853 c.Assert(log.Level, gc.Equals, lt.Level) 854 c.Assert(log.Message, gc.Equals, lt.Message) 855 c.Assert(log.ModelUUID, gc.Equals, lt.ModelUUID) 856 count++ 857 if count == expectedCount { 858 return 859 } 860 case <-timeout: 861 c.Fatalf("timed out waiting for logs (received %d)", count) 862 } 863 } 864 }