1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
18
"gopkg.in/mgo.v2/bson"
20
"github.com/juju/juju/state"
21
coretesting "github.com/juju/juju/testing"
22
jujuversion "github.com/juju/juju/version"
25
type LogsSuite struct {
27
logsColl *mgo.Collection
30
var _ = gc.Suite(&LogsSuite{})
32
func (s *LogsSuite) SetUpTest(c *gc.C) {
33
s.ConnSuite.SetUpTest(c)
35
session := s.State.MongoSession()
36
s.logsColl = session.DB("logs").C("logs")
39
func (s *LogsSuite) TestLastSentLogTrackerSetGet(c *gc.C) {
40
tracker := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test-sink")
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)
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))
58
func (s *LogsSuite) TestLastSentLogTrackerGetNeverSet(c *gc.C) {
59
tracker := state.NewLastSentLogTracker(s.State, s.State.ModelUUID(), "test")
62
_, _, err := tracker.Get()
64
c.Check(err, gc.ErrorMatches, state.ErrNeverForwarded.Error())
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))
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)
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))
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))
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)
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))
123
func (s *LogsSuite) TestAllLastSentLogTrackerSetGet(c *gc.C) {
124
st, err := s.State.ForModel(names.NewModelTag(s.State.ControllerUUID()))
125
c.Assert(err, jc.ErrorIsNil)
127
tracker, err := state.NewAllLastSentLogTracker(st, "test-sink")
128
c.Assert(err, jc.ErrorIsNil)
129
defer tracker.Close()
131
err = tracker.Set(10, 100)
132
c.Assert(err, jc.ErrorIsNil)
133
id1, ts1, err := tracker.Get()
134
c.Assert(err, jc.ErrorIsNil)
135
err = tracker.Set(20, 200)
136
c.Assert(err, jc.ErrorIsNil)
137
id2, ts2, err := tracker.Get()
138
c.Assert(err, jc.ErrorIsNil)
140
c.Check(id1, gc.Equals, int64(10))
141
c.Check(ts1, gc.Equals, int64(100))
142
c.Check(id2, gc.Equals, int64(20))
143
c.Check(ts2, gc.Equals, int64(200))
146
func (s *LogsSuite) TestAllLastSentLogTrackerNotController(c *gc.C) {
147
st := s.NewStateForModelNamed(c, "test-model")
150
_, err := state.NewAllLastSentLogTracker(st, "test")
152
c.Check(err, gc.ErrorMatches, `only the admin model can track all log records`)
155
func (s *LogsSuite) TestIndexesCreated(c *gc.C) {
156
// Indexes should be created on the logs collection when state is opened.
157
indexes, err := s.logsColl.Indexes()
158
c.Assert(err, jc.ErrorIsNil)
160
for _, index := range indexes {
161
keys = append(keys, strings.Join(index.Key, "-"))
163
c.Assert(keys, jc.SameContents, []string{
164
"_id", // default index
165
"e-t", // model-uuid and timestamp
166
"e-n", // model-uuid and entity
170
func (s *LogsSuite) TestDbLogger(c *gc.C) {
171
logger := state.NewDbLogger(s.State, names.NewMachineTag("22"), jujuversion.Current)
173
t0 := time.Now().Truncate(time.Millisecond) // MongoDB only stores timestamps with ms precision.
174
logger.Log(t0, "some.where", "foo.go:99", loggo.INFO, "all is well")
175
t1 := t0.Add(time.Second)
176
logger.Log(t1, "else.where", "bar.go:42", loggo.ERROR, "oh noes")
179
err := s.logsColl.Find(nil).Sort("t").All(&docs)
180
c.Assert(err, jc.ErrorIsNil)
181
c.Assert(docs, gc.HasLen, 2)
183
c.Assert(docs[0]["t"], gc.Equals, t0.UnixNano())
184
c.Assert(docs[0]["e"], gc.Equals, s.State.ModelUUID())
185
c.Assert(docs[0]["n"], gc.Equals, "machine-22")
186
c.Assert(docs[0]["m"], gc.Equals, "some.where")
187
c.Assert(docs[0]["l"], gc.Equals, "foo.go:99")
188
c.Assert(docs[0]["v"], gc.Equals, int(loggo.INFO))
189
c.Assert(docs[0]["x"], gc.Equals, "all is well")
191
c.Assert(docs[1]["t"], gc.Equals, t1.UnixNano())
192
c.Assert(docs[1]["e"], gc.Equals, s.State.ModelUUID())
193
c.Assert(docs[1]["n"], gc.Equals, "machine-22")
194
c.Assert(docs[1]["m"], gc.Equals, "else.where")
195
c.Assert(docs[1]["l"], gc.Equals, "bar.go:42")
196
c.Assert(docs[1]["v"], gc.Equals, int(loggo.ERROR))
197
c.Assert(docs[1]["x"], gc.Equals, "oh noes")
200
func (s *LogsSuite) TestPruneLogsByTime(c *gc.C) {
201
dbLogger := state.NewDbLogger(s.State, names.NewMachineTag("22"), jujuversion.Current)
202
defer dbLogger.Close()
203
log := func(t time.Time, msg string) {
204
err := dbLogger.Log(t, "module", "loc", loggo.INFO, msg)
205
c.Assert(err, jc.ErrorIsNil)
209
maxLogTime := now.Add(-time.Minute)
211
log(maxLogTime.Add(time.Second), "keep")
212
log(maxLogTime, "keep")
213
log(maxLogTime.Add(-time.Second), "prune")
214
log(maxLogTime.Add(-(2 * time.Second)), "prune")
217
err := state.PruneLogs(s.State, maxLogTime, noPruneMB)
218
c.Assert(err, jc.ErrorIsNil)
220
// After pruning there should just be 3 "keep" messages left.
222
err = s.logsColl.Find(nil).All(&docs)
223
c.Assert(err, jc.ErrorIsNil)
224
c.Assert(docs, gc.HasLen, 3)
225
for _, doc := range docs {
226
c.Assert(doc["x"], gc.Equals, "keep")
230
func (s *LogsSuite) TestPruneLogsBySize(c *gc.C) {
231
// Set up 3 models and generate different amounts of logs
233
now := time.Now().Truncate(time.Millisecond)
237
s.generateLogs(c, s0, now, startingLogsS0)
239
s1 := s.Factory.MakeModel(c, nil)
241
startingLogsS1 := 10000
242
s.generateLogs(c, s1, now, startingLogsS1)
244
s2 := s.Factory.MakeModel(c, nil)
246
startingLogsS2 := 12000
247
s.generateLogs(c, s2, now, startingLogsS2)
249
// Prune logs collection back to 1 MiB.
250
tsNoPrune := time.Now().Add(-3 * 24 * time.Hour)
251
err := state.PruneLogs(s.State, tsNoPrune, 1)
252
c.Assert(err, jc.ErrorIsNil)
254
// Logs for first env should not be touched.
255
c.Assert(s.countLogs(c, s0), gc.Equals, startingLogsS0)
257
// Logs for second env should be pruned.
258
c.Assert(s.countLogs(c, s1), jc.LessThan, startingLogsS1)
260
// Logs for third env should be pruned to a similar level as
262
c.Assert(s.countLogs(c, s2), jc.LessThan, startingLogsS1)
264
// Ensure that the latest log records are still there.
265
assertLatestTs := func(st *state.State) {
267
err := s.logsColl.Find(bson.M{"e": st.ModelUUID()}).Sort("-t").One(&doc)
268
c.Assert(err, jc.ErrorIsNil)
269
c.Assert(doc["t"], gc.Equals, now.UnixNano())
276
func (s *LogsSuite) generateLogs(c *gc.C, st *state.State, endTime time.Time, count int) {
277
dbLogger := state.NewDbLogger(st, names.NewMachineTag("0"), jujuversion.Current)
278
defer dbLogger.Close()
279
for i := 0; i < count; i++ {
280
ts := endTime.Add(-time.Duration(i) * time.Second)
281
err := dbLogger.Log(ts, "module", "loc", loggo.INFO, "message")
282
c.Assert(err, jc.ErrorIsNil)
286
func (s *LogsSuite) countLogs(c *gc.C, st *state.State) int {
287
count, err := s.logsColl.Find(bson.M{"e": st.ModelUUID()}).Count()
288
c.Assert(err, jc.ErrorIsNil)
292
type LogTailerSuite struct {
294
logsColl *mgo.Collection
295
oplogColl *mgo.Collection
296
otherState *state.State
299
var _ = gc.Suite(&LogTailerSuite{})
301
func (s *LogTailerSuite) SetUpTest(c *gc.C) {
302
s.ConnSuite.SetUpTest(c)
304
session := s.State.MongoSession()
305
s.logsColl = session.DB("logs").C("logs")
307
// Create a fake oplog collection.
308
s.oplogColl = session.DB("logs").C("oplog.fake")
309
err := s.oplogColl.Create(&mgo.CollectionInfo{
311
MaxBytes: 1024 * 1024,
313
c.Assert(err, jc.ErrorIsNil)
314
s.AddCleanup(func(*gc.C) { s.oplogColl.DropCollection() })
316
s.otherState = s.NewStateForModelNamed(c, "test-model")
317
c.Assert(s.otherState, gc.NotNil)
318
s.AddCleanup(func(c *gc.C) {
319
err := s.otherState.Close()
320
c.Assert(err, jc.ErrorIsNil)
324
func (s *LogTailerSuite) TestTimeFiltering(c *gc.C) {
325
// Add 10 logs that shouldn't be returned.
326
threshT := time.Now()
328
threshT.Add(-5*time.Second), threshT.Add(-time.Millisecond), 5,
329
logTemplate{Message: "dont want"},
332
// Add 5 logs that should be returned.
333
want := logTemplate{Message: "want"}
334
s.writeLogsT(c, threshT, threshT.Add(5*time.Second), 5, want)
335
tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
339
c.Assert(err, jc.ErrorIsNil)
341
s.assertTailer(c, tailer, 5, want)
343
// Write more logs. These will be read from the the oplog.
344
want2 := logTemplate{Message: "want 2"}
345
s.writeLogsT(c, threshT.Add(6*time.Second), threshT.Add(10*time.Second), 5, want2)
346
s.assertTailer(c, tailer, 5, want2)
350
func (s *LogTailerSuite) TestOplogTransition(c *gc.C) {
351
// Ensure that logs aren't repeated as the log tailer moves from
352
// reading from the logs collection to tailing the oplog.
354
// All logs are written out with the same timestamp to create a
355
// challenging scenario for the tailer.
357
for i := 0; i < 5; i++ {
358
s.writeLogs(c, 1, logTemplate{Message: strconv.Itoa(i)})
361
tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
364
c.Assert(err, jc.ErrorIsNil)
366
for i := 0; i < 5; i++ {
367
s.assertTailer(c, tailer, 1, logTemplate{Message: strconv.Itoa(i)})
370
// Write more logs. These will be read from the the oplog.
371
for i := 5; i < 10; i++ {
372
lt := logTemplate{Message: strconv.Itoa(i)}
373
s.writeLogs(c, 2, lt)
374
s.assertTailer(c, tailer, 2, lt)
378
func (s *LogTailerSuite) TestModelFiltering(c *gc.C) {
379
good := logTemplate{Message: "good"}
380
writeLogs := func() {
381
s.writeLogs(c, 1, logTemplate{
382
ModelUUID: "someuuid0",
385
s.writeLogs(c, 1, logTemplate{
386
ModelUUID: "someuuid1",
389
s.writeLogs(c, 1, good)
392
assert := func(tailer state.LogTailer) {
393
// Only the entries the s.State's UUID should be reported.
394
s.assertTailer(c, tailer, 1, good)
397
s.checkLogTailerFiltering(c, s.otherState, &state.LogTailerParams{}, writeLogs, assert)
400
func (s *LogTailerSuite) TestTailingLogsForAllModels(c *gc.C) {
401
writeLogs := func() {
402
s.writeLogs(c, 1, logTemplate{
403
ModelUUID: "someuuid0",
406
s.writeLogs(c, 1, logTemplate{
407
ModelUUID: "someuuid1",
410
s.writeLogs(c, 1, logTemplate{Message: "good"})
413
assert := func(tailer state.LogTailer) {
414
messagesFrom := map[string]bool{
415
s.otherState.ModelUUID(): false,
420
c.Assert(messagesFrom, gc.HasLen, 3)
421
for _, v := range messagesFrom {
422
c.Assert(v, jc.IsTrue)
428
case log := <-tailer.Logs():
429
messagesFrom[log.ModelUUID] = true
431
c.Logf("count %d", count)
435
case <-time.After(coretesting.ShortWait):
436
c.Fatalf("timeout waiting for logs %d", count)
440
s.checkLogTailerFiltering(c, s.State, &state.LogTailerParams{AllModels: true}, writeLogs, assert)
443
func (s *LogTailerSuite) TestTailingLogsOnlyForControllerModel(c *gc.C) {
444
writeLogs := func() {
445
s.writeLogs(c, 1, logTemplate{
446
ModelUUID: s.otherState.ModelUUID(),
449
s.writeLogs(c, 1, logTemplate{
450
ModelUUID: s.State.ModelUUID(),
453
s.writeLogs(c, 1, logTemplate{
454
ModelUUID: s.State.ModelUUID(),
459
assert := func(tailer state.LogTailer) {
460
messages := map[string]bool{}
462
c.Assert(messages, gc.HasLen, 2)
463
for m, _ := range messages {
464
if m != "good1" && m != "good2" {
465
c.Fatalf("received message: %v", m)
472
case log := <-tailer.Logs():
473
c.Assert(log.ModelUUID, gc.Equals, s.State.ModelUUID())
474
messages[log.Message] = true
476
c.Logf("count %d", count)
480
case <-time.After(coretesting.ShortWait):
481
c.Fatalf("timeout waiting for logs %d", count)
485
s.checkLogTailerFiltering(c, s.State, &state.LogTailerParams{}, writeLogs, assert)
488
func (s *LogTailerSuite) TestTailingAllLogsFromNonController(c *gc.C) {
489
_, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{AllModels: true})
490
c.Assert(err, gc.ErrorMatches, "not allowed to tail logs from all models: not a controller")
493
func (s *LogTailerSuite) TestLevelFiltering(c *gc.C) {
494
info := logTemplate{Level: loggo.INFO}
495
error := logTemplate{Level: loggo.ERROR}
496
writeLogs := func() {
497
s.writeLogs(c, 1, logTemplate{Level: loggo.DEBUG})
498
s.writeLogs(c, 1, info)
499
s.writeLogs(c, 1, error)
501
params := &state.LogTailerParams{
502
MinLevel: loggo.INFO,
504
assert := func(tailer state.LogTailer) {
505
s.assertTailer(c, tailer, 1, info)
506
s.assertTailer(c, tailer, 1, error)
508
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
511
func (s *LogTailerSuite) TestInitialLines(c *gc.C) {
512
expected := logTemplate{Message: "want"}
513
s.writeLogs(c, 3, logTemplate{Message: "dont want"})
514
s.writeLogs(c, 5, expected)
516
tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
519
c.Assert(err, jc.ErrorIsNil)
522
// Should see just the last 5 lines as requested.
523
s.assertTailer(c, tailer, 5, expected)
526
func (s *LogTailerSuite) TestInitialLinesWithNotEnoughLines(c *gc.C) {
527
expected := logTemplate{Message: "want"}
528
s.writeLogs(c, 2, expected)
530
tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
533
c.Assert(err, jc.ErrorIsNil)
536
// Should see just the 2 lines that existed, even though 5 were
538
s.assertTailer(c, tailer, 2, expected)
541
func (s *LogTailerSuite) TestNoTail(c *gc.C) {
542
expected := logTemplate{Message: "want"}
543
s.writeLogs(c, 2, expected)
545
// Write a log entry that's only in the oplog.
546
doc := s.logTemplateToDoc(logTemplate{Message: "dont want"}, time.Now())
547
err := s.writeLogToOplog(doc)
548
c.Assert(err, jc.ErrorIsNil)
550
tailer, err := state.NewLogTailer(s.otherState, &state.LogTailerParams{
553
c.Assert(err, jc.ErrorIsNil)
554
// Not strictly necessary, just in case NoTail doesn't work in the test.
557
// Logs only in the oplog shouldn't be reported and the tailer
558
// should stop itself once the log collection has been read.
559
s.assertTailer(c, tailer, 2, expected)
561
case _, ok := <-tailer.Logs():
563
c.Fatal("shouldn't be any further logs")
565
case <-time.After(coretesting.LongWait):
566
c.Fatal("timed out waiting for logs channel to close")
570
case <-tailer.Dying():
572
case <-time.After(coretesting.LongWait):
573
c.Fatal("tailer didn't stop itself")
577
func (s *LogTailerSuite) TestIncludeEntity(c *gc.C) {
578
machine0 := logTemplate{Entity: names.NewMachineTag("0")}
579
foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
580
foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
581
writeLogs := func() {
582
s.writeLogs(c, 3, machine0)
583
s.writeLogs(c, 2, foo0)
584
s.writeLogs(c, 1, foo1)
585
s.writeLogs(c, 3, machine0)
587
params := &state.LogTailerParams{
588
IncludeEntity: []string{
593
assert := func(tailer state.LogTailer) {
594
s.assertTailer(c, tailer, 2, foo0)
595
s.assertTailer(c, tailer, 1, foo1)
597
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
600
func (s *LogTailerSuite) TestIncludeEntityWildcard(c *gc.C) {
601
machine0 := logTemplate{Entity: names.NewMachineTag("0")}
602
foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
603
foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
604
writeLogs := func() {
605
s.writeLogs(c, 3, machine0)
606
s.writeLogs(c, 2, foo0)
607
s.writeLogs(c, 1, foo1)
608
s.writeLogs(c, 3, machine0)
610
params := &state.LogTailerParams{
611
IncludeEntity: []string{
615
assert := func(tailer state.LogTailer) {
616
s.assertTailer(c, tailer, 2, foo0)
617
s.assertTailer(c, tailer, 1, foo1)
619
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
622
func (s *LogTailerSuite) TestExcludeEntity(c *gc.C) {
623
machine0 := logTemplate{Entity: names.NewMachineTag("0")}
624
foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
625
foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
626
writeLogs := func() {
627
s.writeLogs(c, 3, machine0)
628
s.writeLogs(c, 2, foo0)
629
s.writeLogs(c, 1, foo1)
630
s.writeLogs(c, 3, machine0)
632
params := &state.LogTailerParams{
633
ExcludeEntity: []string{
638
assert := func(tailer state.LogTailer) {
639
s.assertTailer(c, tailer, 1, foo1)
641
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
644
func (s *LogTailerSuite) TestExcludeEntityWildcard(c *gc.C) {
645
machine0 := logTemplate{Entity: names.NewMachineTag("0")}
646
foo0 := logTemplate{Entity: names.NewUnitTag("foo/0")}
647
foo1 := logTemplate{Entity: names.NewUnitTag("foo/1")}
648
writeLogs := func() {
649
s.writeLogs(c, 3, machine0)
650
s.writeLogs(c, 2, foo0)
651
s.writeLogs(c, 1, foo1)
652
s.writeLogs(c, 3, machine0)
654
params := &state.LogTailerParams{
655
ExcludeEntity: []string{
660
assert := func(tailer state.LogTailer) {
661
s.assertTailer(c, tailer, 1, foo1)
663
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
665
func (s *LogTailerSuite) TestIncludeModule(c *gc.C) {
666
mod0 := logTemplate{Module: "foo.bar"}
667
mod1 := logTemplate{Module: "juju.thing"}
668
subMod1 := logTemplate{Module: "juju.thing.hai"}
669
mod2 := logTemplate{Module: "elsewhere"}
670
writeLogs := func() {
671
s.writeLogs(c, 1, mod0)
672
s.writeLogs(c, 1, mod1)
673
s.writeLogs(c, 1, mod0)
674
s.writeLogs(c, 1, subMod1)
675
s.writeLogs(c, 1, mod0)
676
s.writeLogs(c, 1, mod2)
678
params := &state.LogTailerParams{
679
IncludeModule: []string{"juju.thing", "elsewhere"},
681
assert := func(tailer state.LogTailer) {
682
s.assertTailer(c, tailer, 1, mod1)
683
s.assertTailer(c, tailer, 1, subMod1)
684
s.assertTailer(c, tailer, 1, mod2)
686
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
689
func (s *LogTailerSuite) TestExcludeModule(c *gc.C) {
690
mod0 := logTemplate{Module: "foo.bar"}
691
mod1 := logTemplate{Module: "juju.thing"}
692
subMod1 := logTemplate{Module: "juju.thing.hai"}
693
mod2 := logTemplate{Module: "elsewhere"}
694
writeLogs := func() {
695
s.writeLogs(c, 1, mod0)
696
s.writeLogs(c, 1, mod1)
697
s.writeLogs(c, 1, mod0)
698
s.writeLogs(c, 1, subMod1)
699
s.writeLogs(c, 1, mod0)
700
s.writeLogs(c, 1, mod2)
702
params := &state.LogTailerParams{
703
ExcludeModule: []string{"juju.thing", "elsewhere"},
705
assert := func(tailer state.LogTailer) {
706
s.assertTailer(c, tailer, 2, mod0)
708
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
711
func (s *LogTailerSuite) TestIncludeExcludeModule(c *gc.C) {
712
foo := logTemplate{Module: "foo"}
713
bar := logTemplate{Module: "bar"}
714
barSub := logTemplate{Module: "bar.thing"}
715
baz := logTemplate{Module: "baz"}
716
qux := logTemplate{Module: "qux"}
717
writeLogs := func() {
718
s.writeLogs(c, 1, foo)
719
s.writeLogs(c, 1, bar)
720
s.writeLogs(c, 1, barSub)
721
s.writeLogs(c, 1, baz)
722
s.writeLogs(c, 1, qux)
724
params := &state.LogTailerParams{
725
IncludeModule: []string{"foo", "bar", "qux"},
726
ExcludeModule: []string{"foo", "bar"},
728
assert := func(tailer state.LogTailer) {
729
// Except just "qux" because "foo" and "bar" were included and
731
s.assertTailer(c, tailer, 1, qux)
733
s.checkLogTailerFiltering(c, s.otherState, params, writeLogs, assert)
736
func (s *LogTailerSuite) checkLogTailerFiltering(
739
params *state.LogTailerParams,
741
assertTailer func(state.LogTailer),
743
// Check the tailer does the right thing when reading from the
746
params.Oplog = s.oplogColl
747
tailer, err := state.NewLogTailer(st, params)
748
c.Assert(err, jc.ErrorIsNil)
752
// Now write out logs and check the tailer again. These will be
753
// read from the oplog.
758
type logTemplate struct {
761
Version version.Number
768
// writeLogs creates count log messages at the current time using
769
// the supplied template. As well as writing to the logs collection,
770
// entries are also made into the fake oplog collection.
771
func (s *LogTailerSuite) writeLogs(c *gc.C, count int, lt logTemplate) {
773
s.writeLogsT(c, t, t, count, lt)
776
// writeLogsT creates count log messages between startTime and
777
// endTime using the supplied template. As well as writing to the logs
778
// collection, entries are also made into the fake oplog collection.
779
func (s *LogTailerSuite) writeLogsT(c *gc.C, startTime, endTime time.Time, count int, lt logTemplate) {
780
interval := endTime.Sub(startTime) / time.Duration(count)
782
for i := 0; i < count; i++ {
783
doc := s.logTemplateToDoc(lt, t)
784
err := s.writeLogToOplog(doc)
785
c.Assert(err, jc.ErrorIsNil)
786
err = s.logsColl.Insert(doc)
787
c.Assert(err, jc.ErrorIsNil)
792
// writeLogToOplog writes out a log record to the a (probably fake)
794
func (s *LogTailerSuite) writeLogToOplog(doc interface{}) error {
795
return s.oplogColl.Insert(bson.D{
796
{"ts", bson.MongoTimestamp(time.Now().Unix() << 32)}, // an approximation which will do
797
{"h", rand.Int63()}, // again, a suitable fake
798
{"op", "i"}, // this will always be an insert
804
func (s *LogTailerSuite) normaliseLogTemplate(lt *logTemplate) {
805
if lt.ModelUUID == "" {
806
lt.ModelUUID = s.otherState.ModelUUID()
808
if lt.Entity == nil {
809
lt.Entity = names.NewMachineTag("0")
811
if lt.Version == version.Zero {
812
lt.Version = jujuversion.Current
817
if lt.Location == "" {
820
if lt.Level == loggo.UNSPECIFIED {
821
lt.Level = loggo.INFO
823
if lt.Message == "" {
824
lt.Message = "message"
828
func (s *LogTailerSuite) logTemplateToDoc(lt logTemplate, t time.Time) interface{} {
829
s.normaliseLogTemplate(<)
830
return state.MakeLogDoc(
841
func (s *LogTailerSuite) assertTailer(c *gc.C, tailer state.LogTailer, expectedCount int, lt logTemplate) {
842
s.normaliseLogTemplate(<)
844
timeout := time.After(coretesting.LongWait)
848
case log, ok := <-tailer.Logs():
850
c.Fatalf("tailer died unexpectedly: %v", tailer.Err())
852
c.Assert(log.Version, gc.Equals, lt.Version)
853
c.Assert(log.Entity, gc.Equals, lt.Entity)
854
c.Assert(log.Module, gc.Equals, lt.Module)
855
c.Assert(log.Location, gc.Equals, lt.Location)
856
c.Assert(log.Level, gc.Equals, lt.Level)
857
c.Assert(log.Message, gc.Equals, lt.Message)
858
c.Assert(log.ModelUUID, gc.Equals, lt.ModelUUID)
860
if count == expectedCount {
864
c.Fatalf("timed out waiting for logs (received %d)", count)