Description:
Fix the limit for the debug-log calls.
Unbeknownst to me, the filter method was being called
during the backlog iteration as well. This fix introduces
a callback that the tailer calls when it starts the forward
filtering.
This allows us to count the filter calls only when the
filter is being called to write out the results. We cannot
count the write calls because the tailer uses buffered i/o
there.
Affected files (+42, -10 lines):
A [revision details]
M container/lxc/clonetemplate.go
M state/apiserver/debuglog.go
M state/apiserver/debuglog_internal_test.go
M state/apiserver/debuglog_test.go
M utils/tailer/tailer.go
M utils/tailer/tailer_test.go
+// TailerFilterStartedFunc is a callback that is called when the filtering
is
+// starting.
+type TailerFilterStartedFunc func()
+
// Tailer reads an input line by line an tails them into the passed Writer.
// The lines have to be terminated with a newline.
type Tailer struct {
@@ -40,6 +44,7 @@
bufferSize int
polltime time.Duration
lookBack bool
+ callback TailerFilterStartedFunc
}
// NewTailerBacktrack starts a Tailer which reads strings from the passed
@@ -47,21 +52,24 @@
// lines are filtered. The matching lines are written to the passed
// Writer. The reading begins the specified number of matching lines
// from the end.
-func NewTailerBacktrack(readSeeker io.ReadSeeker, writer io.Writer, lines
uint, filter TailerFilterFunc) *Tailer {
- return newTailer(readSeeker, writer, lines, filter, bufferSize, polltime,
true)
+func NewTailerBacktrack(readSeeker io.ReadSeeker, writer io.Writer, lines
uint,
+ filter TailerFilterFunc, callback TailerFilterStartedFunc) *Tailer {
+ return newTailer(readSeeker, writer, lines, filter, callback, bufferSize,
polltime, true)
}
// NewTailer starts a Tailer which reads strings from the passed
// ReadSeeker line by line. If a filter function is specified the read
// lines are filtered. The matching lines are written to the passed
// Writer.
-func NewTailer(readSeeker io.ReadSeeker, writer io.Writer, filter
TailerFilterFunc) *Tailer {
- return newTailer(readSeeker, writer, 0, filter, bufferSize, polltime,
false)
+func NewTailer(readSeeker io.ReadSeeker, writer io.Writer,
+ filter TailerFilterFunc, callback TailerFilterStartedFunc) *Tailer {
+ return newTailer(readSeeker, writer, 0, filter, callback, bufferSize,
polltime, false)
}
// newTailer starts a Tailer like NewTailer but allows the setting of
// the read buffer size and the time between pollings for testing.
-func newTailer(readSeeker io.ReadSeeker, writer io.Writer, lines uint,
filter TailerFilterFunc,
+func newTailer(readSeeker io.ReadSeeker, writer io.Writer, lines uint,
+ filter TailerFilterFunc, callback TailerFilterStartedFunc,
bufferSize int, polltime time.Duration, lookBack bool) *Tailer {
t := &Tailer{
readSeeker: readSeeker,
@@ -72,6 +80,7 @@
bufferSize: bufferSize,
polltime: polltime,
lookBack: lookBack,
+ callback: callback,
}
go func() {
defer t.tomb.Done()
@@ -113,6 +122,9 @@
return err
}
}
+ if t.callback != nil {
+ t.callback()
+ }
// Start polling.
// TODO(mue) 2013-12-06
// Handling of read-seeker/files being truncated during
Reviewers: mp+215333_ code.launchpad. net,
Message:
Please take a look.
Description:
Fix the limit for the debug-log calls.
Unbeknownst to me, the filter method was being called
during the backlog iteration as well. This fix introduces
a callback that the tailer calls when it starts the forward
filtering.
This allows us to count the filter calls only when the
filter is being called to write out the results. We cannot
count the write calls because the tailer uses buffered i/o
there.
https:/ /code.launchpad .net/~thumper/ juju-core/ fix-backlog- limits/ +merge/ 215333
Requires: /code.launchpad .net/~thumper/ juju-core/ debug-log- api/+merge/ 215323
https:/
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/85570045/
Affected files (+42, -10 lines): lxc/clonetempla te.go /debuglog. go /debuglog_ internal_ test.go /debuglog_ test.go tailer. go tailer_ test.go
A [revision details]
M container/
M state/apiserver
M state/apiserver
M state/apiserver
M utils/tailer/
M utils/tailer/
Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: <email address hidden>
+New revision: <email address hidden>
Index: container/ lxc/clonetempla te.go lxc/clonetempla te.go' lxc/clonetempla te.go 2014-04-04 03:46:13 +0000 lxc/clonetempla te.go 2014-04-11 01:57:45 +0000
=== modified file 'container/
--- container/
+++ container/
@@ -179,7 +179,7 @@
}
tailWriter := &logTail{tick: time.Now()} NewTailer( console, tailWriter, nil) NewTailer( console, tailWriter, nil, nil) Stop()
- consoleTailer := tailer.
+ consoleTailer := tailer.
defer consoleTailer.
// We should wait maybe 1 minute between output?
Index: state/apiserver /debuglog. go apiserver/ debuglog. go' /debuglog. go 2014-04-10 09:43:51 +0000 /debuglog. go 2014-04-11 01:54:49 +0000
=== modified file 'state/
--- state/apiserver
+++ state/apiserver
@@ -209,18 +209,23 @@
maxLines uint
lineCount uint
fromTheStart bool
+ started bool
}
// start the tailer listening to the logFile, and sending the matching NewTailer( logFile, writer, stream.filterLine) NewTailer( logFile, writer, stream.filterLine, NewTailerBacktr ack(logFile, writer, NewTailerBacktr ack(logFile, writer,
// lines to the writer.
func (stream *logStream) start(logFile io.ReadSeeker, writer io.Writer) {
if stream.fromTheStart {
- stream.logTailer = tailer.
+ stream.logTailer = tailer.
stream.tailStarted)
} else {
- stream.logTailer = tailer.
stream.backlog, stream.filterLine)
+ stream.logTailer = tailer.
stream.backlog, stream.filterLine, stream.tailStarted)
}
}
+func (stream *logStream) tailStarted() { checkIncludeMod ule(log) && exclude( log) && checkLevel( log) lineCount+ +
+ stream.started = true
+}
+
// loop starts the tailer with the log file and the web socket.
func (stream *logStream) loop() error {
select {
@@ -239,7 +244,7 @@
stream.
!stream.
stream.
- if result && stream.maxLines > 0 {
+ if stream.started && result && stream.maxLines > 0 {
stream.
result = stream.lineCount <= stream.maxLines
if stream.lineCount == stream.maxLines {
Index: state/apiserver /debuglog_ internal_ test.go apiserver/ debuglog_ internal_ test.go' /debuglog_ internal_ test.go 2014-04-07 05:11:48 +0000 /debuglog_ internal_ test.go 2014-04-11 02:05:44 +0000 stream. filterLine( line), jc.IsTrue)
=== modified file 'state/
--- state/apiserver
+++ state/apiserver
@@ -183,6 +183,7 @@
stream := &logStream{
filterLevel: loggo.INFO,
maxLines: 5,
+ started: true,
}
line := []byte("machine-0: date time WARNING juju")
c.Check(
Index: state/apiserver /debuglog_ test.go apiserver/ debuglog_ test.go' /debuglog_ test.go 2014-04-10 09:43:51 +0000 /debuglog_ test.go 2014-04-11 01:45:10 +0000 ocketClosed( c, reader)
=== modified file 'state/
--- state/apiserver
+++ state/apiserver
@@ -122,6 +122,18 @@
s.assertWebs
}
+func (s *debugLogSuite) TestBacklogWith MaxLines( c *gc.C) { "backlog" : {"5"}, "maxLines": owing(c, reader) etClosed( c, reader) ile(c)
+ s.writeLogLines(c, 10)
+
+ reader := s.openWebsocket(c, url.Values{
{"10"}})
+ s.assertLogFoll
+ s.writeLogLines(c, logLineCount)
+
+ linesRead := s.readLogLines(c, reader, 10)
+ c.Assert(linesRead, jc.DeepEquals, logLines[5:15])
+ s.assertWebsock
+}
+
func (s *debugLogSuite) TestFilter(c *gc.C) {
s.ensureLogF
Index: utils/tailer/ tailer. go tailer/ tailer. go' tailer. go 2014-04-04 04:35:44 +0000 tailer. go 2014-04-11 01:54:49 +0000
=== modified file 'utils/
--- utils/tailer/
+++ utils/tailer/
@@ -27,6 +27,10 @@
// returns true) of shall be omitted (func returns false).
type TailerFilterFunc func(line []byte) bool
+// TailerFilterSta rtedFunc is a callback that is called when the filtering rtedFunc func() rtedFunc
is
+// starting.
+type TailerFilterSta
+
// Tailer reads an input line by line an tails them into the passed Writer.
// The lines have to be terminated with a newline.
type Tailer struct {
@@ -40,6 +44,7 @@
bufferSize int
polltime time.Duration
lookBack bool
+ callback TailerFilterSta
}
// NewTailerBacktrack starts a Tailer which reads strings from the passed ack(readSeeker io.ReadSeeker, writer io.Writer, lines readSeeker, writer, lines, filter, bufferSize, polltime, ack(readSeeker io.ReadSeeker, writer io.Writer, lines rtedFunc) *Tailer { readSeeker, writer, lines, filter, callback, bufferSize,
@@ -47,21 +52,24 @@
// lines are filtered. The matching lines are written to the passed
// Writer. The reading begins the specified number of matching lines
// from the end.
-func NewTailerBacktr
uint, filter TailerFilterFunc) *Tailer {
- return newTailer(
true)
+func NewTailerBacktr
uint,
+ filter TailerFilterFunc, callback TailerFilterSta
+ return newTailer(
polltime, true)
}
// NewTailer starts a Tailer which reads strings from the passed readSeeker io.ReadSeeker, writer io.Writer, filter readSeeker, writer, 0, filter, bufferSize, polltime, readSeeker io.ReadSeeker, writer io.Writer, rtedFunc) *Tailer { readSeeker, writer, 0, filter, callback, bufferSize,
// ReadSeeker line by line. If a filter function is specified the read
// lines are filtered. The matching lines are written to the passed
// Writer.
-func NewTailer(
TailerFilterFunc) *Tailer {
- return newTailer(
false)
+func NewTailer(
+ filter TailerFilterFunc, callback TailerFilterSta
+ return newTailer(
polltime, false)
}
// newTailer starts a Tailer like NewTailer but allows the setting of readSeeker io.ReadSeeker, writer io.Writer, lines uint, readSeeker io.ReadSeeker, writer io.Writer, lines uint, rtedFunc,
// the read buffer size and the time between pollings for testing.
-func newTailer(
filter TailerFilterFunc,
+func newTailer(
+ filter TailerFilterFunc, callback TailerFilterSta
bufferSize int, polltime time.Duration, lookBack bool) *Tailer {
t := &Tailer{
readSeeker: readSeeker,
@@ -72,6 +80,7 @@
bufferSize: bufferSize,
polltime: polltime,
lookBack: lookBack,
+ callback: callback,
}
go func() {
defer t.tomb.Done()
@@ -113,6 +122,9 @@
return err
}
}
+ if t.callback != nil {
+ t.callback()
+ }
// Start polling.
// TODO(mue) 2013-12-06
// Handling of read-seeker/files being truncated during
Index: utils/tailer/ tailer_ test.go tailer/ tailer_ test.go' tailer_ test.go 2014-04-04 03:46:13 +0000 tailer_ test.go 2014-04-11 01:57:45 +0000 esWritten, sigc) NewTestTailer( rs, writer, test.initialLin esRequested, NewTestTailer( rs, writer, test.initialLin esRequested,
=== modified file 'utils/
--- utils/tailer/
+++ utils/tailer/
@@ -337,7 +337,7 @@
reader, writer := io.Pipe()
sigc := make(chan struct{}, 1)
rs := startReadSeeker(c, test.data, test.initialLin
- tailer := tailer.
test.filter, bufferSize, 2*time.Millisecond, !test.fromStart)
+ tailer := tailer.
test.filter, nil, bufferSize, 2*time.Millisecond, !test.fromStart)
linec := startReading(c, tailer, reader, writer)
// Collect initial data.