Merge lp:~thumper/juju-core/tailer-tweaks into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 2563
Proposed branch: lp:~thumper/juju-core/tailer-tweaks
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 150 lines (+45/-12)
3 files modified
container/lxc/clonetemplate.go (+1/-1)
utils/tailer/tailer.go (+25/-9)
utils/tailer/tailer_test.go (+19/-2)
To merge this branch: bzr merge lp:~thumper/juju-core/tailer-tweaks
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+214150@code.launchpad.net

Commit message

Extend the tailer utility.

When looking at the pyjuju debug-log, there was a replay
function that allowed getting the log from the start.
There was no way to tell the tailer to start from the
beginning of the file.

I changed the meaning of NewTailer to be one that doesn't
do any seeking. The NewTailerBacktrack does seek to the end,
and then goes back a certain number of lines.

There was a bug in the backtrack tailer when zero lines
were passed in. This is fixed and a test added.

The number of lines to look back is now a uint, as negative
values make no sense at all.

https://codereview.appspot.com/84290045/

Description of the change

Extend the tailer utility.

When looking at the pyjuju debug-log, there was a replay
function that allowed getting the log from the start.
There was no way to tell the tailer to start from the
beginning of the file.

I changed the meaning of NewTailer to be one that doesn't
do any seeking. The NewTailerBacktrack does seek to the end,
and then goes back a certain number of lines.

There was a bug in the backtrack tailer when zero lines
were passed in. This is fixed and a test added.

In order to provide a synchronization mechanism for other
tests, the Tailer has a public function param that is called
after the backtracking, and prior to starting tailing.

The number of lines to look back is now a unit, as negative
values make no sense at all.

https://codereview.appspot.com/84290045/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :
Download full text (7.2 KiB)

Reviewers: mp+214150_code.launchpad.net,

Message:
Please take a look.

Description:
Extend the tailer utility.

When looking at the pyjuju debug-log, there was a replay
function that allowed getting the log from the start.
There was no way to tell the tailer to start from the
beginning of the file.

I changed the meaning of NewTailer to be one that doesn't
do any seeking. The NewTailerBacktrack does seek to the end,
and then goes back a certain number of lines.

There was a bug in the backtrack tailer when zero lines
were passed in. This is fixed and a test added.

In order to provide a synchronization mechanism for other
tests, the Tailer has a public function param that is called
after the backtracking, and prior to starting tailing.

The number of lines to look back is now a unit, as negative
values make no sense at all.

https://code.launchpad.net/~thumper/juju-core/tailer-tweaks/+merge/214150

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/84290045/

Affected files (+53, -12 lines):
   A [revision details]
   M container/lxc/clonetemplate.go
   M utils/tailer/tailer.go
   M utils/tailer/tailer_test.go

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: tarmac-20140404003547-v7dzlndz9bq9h4qd
+New revision: <email address hidden>

Index: container/lxc/clonetemplate.go
=== modified file 'container/lxc/clonetemplate.go'
--- container/lxc/clonetemplate.go 2014-03-12 04:32:02 +0000
+++ container/lxc/clonetemplate.go 2014-04-04 03:33:01 +0000
@@ -179,7 +179,7 @@
   }

   tailWriter := &logTail{tick: time.Now()}
- consoleTailer := tailer.NewTailer(console, tailWriter, 0, nil)
+ consoleTailer := tailer.NewTailer(console, tailWriter, nil)
   defer consoleTailer.Stop()

   // We should wait maybe 1 minute between output?

Index: utils/tailer/tailer.go
=== modified file 'utils/tailer/tailer.go'
--- utils/tailer/tailer.go 2013-12-13 11:19:56 +0000
+++ utils/tailer/tailer.go 2014-04-04 03:20:53 +0000
@@ -35,25 +35,37 @@
   reader *bufio.Reader
   writeCloser io.WriteCloser
   writer *bufio.Writer
- lines int
+ lines uint
   filter TailerFilterFunc
   bufferSize int
   polltime time.Duration
+ lookBack bool
+ // StartedListening is purely a hook point for tests. It is a function
+ // that is called after the initial seek prior to reading.
+ StartedTailing func()
  }

-// NewTailer starts a Tailer which reads strings from the passed
+// NewTailerBacktrack 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. The reading begins the specified number of matching lines
  // from the end.
-func NewTailer(readSeeker io.ReadSeeker, writer io.Writer, lines int,
filter TailerFilterFunc) *Tailer {
- return newTailer(readSeeker, writer, lines, filter, bufferSize, polltime)
+func NewTailerBacktrack(readSeeker io.ReadSeeker, writer io.Writer, lines ...

Read more...

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/04/04 03:42:04, thumper wrote:
> Please take a look.

Looks fine in general. As discussed on IRC, I would prefer if we didn't
expose StartedTailing for testing, but instead you override Seek for
your testing purposes.

I'm not crazy about NewTailer doing this seeking business. It seems like
that should be pulled out, and just have NewTailer do one thing.
Just a suggestion, but how about something like:

     func SeekLastMatching(r io.ReadSeeker, n int, filter
TailerFilterFunc) error
     func NewTailer(r io.Reader, writer io.Writer, filter
TailerFilterFunc) *Tailer

I see that Tailer does use Seek, but I'm not sure that it needs to.

https://codereview.appspot.com/84290045/

Revision history for this message
Tim Penhey (thumper) wrote :

On 2014/04/04 04:21:34, axw wrote:
> On 2014/04/04 03:42:04, thumper wrote:
> > Please take a look.

> Looks fine in general. As discussed on IRC, I would prefer if we
didn't expose
> StartedTailing for testing, but instead you override Seek for your
testing
> purposes.

> I'm not crazy about NewTailer doing this seeking business. It seems
like that
> should be pulled out, and just have NewTailer do one thing.
> Just a suggestion, but how about something like:

> func SeekLastMatching(r io.ReadSeeker, n int, filter
TailerFilterFunc) error
> func NewTailer(r io.Reader, writer io.Writer, filter
TailerFilterFunc)
> *Tailer

> I see that Tailer does use Seek, but I'm not sure that it needs to.

I'd rather keep the Tailer as unchanged as possible for now.

I've removed the StartedTailing testing callback

https://codereview.appspot.com/84290045/

Revision history for this message
Tim Penhey (thumper) wrote :
Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/04/04 04:33:10, thumper wrote:
> Please take a look.

LGTM, thanks.

https://codereview.appspot.com/84290045/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'container/lxc/clonetemplate.go'
--- container/lxc/clonetemplate.go 2014-03-12 04:32:02 +0000
+++ container/lxc/clonetemplate.go 2014-04-04 04:31:14 +0000
@@ -179,7 +179,7 @@
179 }179 }
180180
181 tailWriter := &logTail{tick: time.Now()}181 tailWriter := &logTail{tick: time.Now()}
182 consoleTailer := tailer.NewTailer(console, tailWriter, 0, nil)182 consoleTailer := tailer.NewTailer(console, tailWriter, nil)
183 defer consoleTailer.Stop()183 defer consoleTailer.Stop()
184184
185 // We should wait maybe 1 minute between output?185 // We should wait maybe 1 minute between output?
186186
=== modified file 'utils/tailer/tailer.go'
--- utils/tailer/tailer.go 2013-12-13 11:19:56 +0000
+++ utils/tailer/tailer.go 2014-04-04 04:31:14 +0000
@@ -35,25 +35,34 @@
35 reader *bufio.Reader35 reader *bufio.Reader
36 writeCloser io.WriteCloser36 writeCloser io.WriteCloser
37 writer *bufio.Writer37 writer *bufio.Writer
38 lines int38 lines uint
39 filter TailerFilterFunc39 filter TailerFilterFunc
40 bufferSize int40 bufferSize int
41 polltime time.Duration41 polltime time.Duration
42 lookBack bool
42}43}
4344
44// NewTailer starts a Tailer which reads strings from the passed45// NewTailerBacktrack starts a Tailer which reads strings from the passed
45// ReadSeeker line by line. If a filter function is specified the read46// ReadSeeker line by line. If a filter function is specified the read
46// lines are filtered. The matching lines are written to the passed47// lines are filtered. The matching lines are written to the passed
47// Writer. The reading begins the specified number of matching lines48// Writer. The reading begins the specified number of matching lines
48// from the end.49// from the end.
49func NewTailer(readSeeker io.ReadSeeker, writer io.Writer, lines int, filter TailerFilterFunc) *Tailer {50func NewTailerBacktrack(readSeeker io.ReadSeeker, writer io.Writer, lines uint, filter TailerFilterFunc) *Tailer {
50 return newTailer(readSeeker, writer, lines, filter, bufferSize, polltime)51 return newTailer(readSeeker, writer, lines, filter, bufferSize, polltime, true)
52}
53
54// NewTailer starts a Tailer which reads strings from the passed
55// ReadSeeker line by line. If a filter function is specified the read
56// lines are filtered. The matching lines are written to the passed
57// Writer.
58func NewTailer(readSeeker io.ReadSeeker, writer io.Writer, filter TailerFilterFunc) *Tailer {
59 return newTailer(readSeeker, writer, 0, filter, bufferSize, polltime, false)
51}60}
5261
53// newTailer starts a Tailer like NewTailer but allows the setting of62// newTailer starts a Tailer like NewTailer but allows the setting of
54// the read buffer size and the time between pollings for testing.63// the read buffer size and the time between pollings for testing.
55func newTailer(readSeeker io.ReadSeeker, writer io.Writer, lines int, filter TailerFilterFunc,64func newTailer(readSeeker io.ReadSeeker, writer io.Writer, lines uint, filter TailerFilterFunc,
56 bufferSize int, polltime time.Duration) *Tailer {65 bufferSize int, polltime time.Duration, lookBack bool) *Tailer {
57 t := &Tailer{66 t := &Tailer{
58 readSeeker: readSeeker,67 readSeeker: readSeeker,
59 reader: bufio.NewReaderSize(readSeeker, bufferSize),68 reader: bufio.NewReaderSize(readSeeker, bufferSize),
@@ -62,6 +71,7 @@
62 filter: filter,71 filter: filter,
63 bufferSize: bufferSize,72 bufferSize: bufferSize,
64 polltime: polltime,73 polltime: polltime,
74 lookBack: lookBack,
65 }75 }
66 go func() {76 go func() {
67 defer t.tomb.Done()77 defer t.tomb.Done()
@@ -98,8 +108,10 @@
98// writer too.108// writer too.
99func (t *Tailer) loop() error {109func (t *Tailer) loop() error {
100 // Position the readSeeker.110 // Position the readSeeker.
101 if err := t.seekLastLines(); err != nil {111 if t.lookBack {
102 return err112 if err := t.seekLastLines(); err != nil {
113 return err
114 }
103 }115 }
104 // Start polling.116 // Start polling.
105 // TODO(mue) 2013-12-06117 // TODO(mue) 2013-12-06
@@ -139,8 +151,12 @@
139 if err != nil {151 if err != nil {
140 return err152 return err
141 }153 }
154 if t.lines == 0 {
155 // We are done, just seeking to the end is sufficient.
156 return nil
157 }
142 seekPos := int64(0)158 seekPos := int64(0)
143 found := 0159 found := uint(0)
144 buffer := make([]byte, t.bufferSize)160 buffer := make([]byte, t.bufferSize)
145SeekLoop:161SeekLoop:
146 for offset > 0 {162 for offset > 0 {
147163
=== modified file 'utils/tailer/tailer_test.go'
--- utils/tailer/tailer_test.go 2013-12-13 13:21:44 +0000
+++ utils/tailer/tailer_test.go 2014-04-04 04:31:14 +0000
@@ -59,12 +59,13 @@
59 description string59 description string
60 data []string60 data []string
61 initialLinesWritten int61 initialLinesWritten int
62 initialLinesRequested int62 initialLinesRequested uint
63 bufferSize int63 bufferSize int
64 filter tailer.TailerFilterFunc64 filter tailer.TailerFilterFunc
65 injector func(*tailer.Tailer, *readSeeker) func([]string)65 injector func(*tailer.Tailer, *readSeeker) func([]string)
66 initialCollectedData []string66 initialCollectedData []string
67 appendedCollectedData []string67 appendedCollectedData []string
68 fromStart bool
68 err string69 err string
69}{{70}{{
70 description: "lines are longer than buffer size",71 description: "lines are longer than buffer size",
@@ -182,6 +183,19 @@
182 },183 },
183 appendedCollectedData: alphabetData[5:],184 appendedCollectedData: alphabetData[5:],
184}, {185}, {
186 description: "ignore current lines",
187 data: alphabetData,
188 initialLinesWritten: 5,
189 bufferSize: 5,
190 appendedCollectedData: alphabetData[5:],
191}, {
192 description: "start from the start",
193 data: alphabetData,
194 initialLinesWritten: 5,
195 bufferSize: 5,
196 appendedCollectedData: alphabetData,
197 fromStart: true,
198}, {
185 description: "lines are longer than buffer size, less lines already written than initially requested",199 description: "lines are longer than buffer size, less lines already written than initially requested",
186 data: alphabetData,200 data: alphabetData,
187 initialLinesWritten: 3,201 initialLinesWritten: 3,
@@ -323,7 +337,7 @@
323 reader, writer := io.Pipe()337 reader, writer := io.Pipe()
324 sigc := make(chan struct{}, 1)338 sigc := make(chan struct{}, 1)
325 rs := startReadSeeker(c, test.data, test.initialLinesWritten, sigc)339 rs := startReadSeeker(c, test.data, test.initialLinesWritten, sigc)
326 tailer := tailer.NewTestTailer(rs, writer, test.initialLinesRequested, test.filter, bufferSize, 2*time.Millisecond)340 tailer := tailer.NewTestTailer(rs, writer, test.initialLinesRequested, test.filter, bufferSize, 2*time.Millisecond, !test.fromStart)
327 linec := startReading(c, tailer, reader, writer)341 linec := startReading(c, tailer, reader, writer)
328342
329 // Collect initial data.343 // Collect initial data.
@@ -386,6 +400,9 @@
386// linec is closed due to stopping or an error only the values so far care400// linec is closed due to stopping or an error only the values so far care
387// compared. Checking the reason for termination is done in the test.401// compared. Checking the reason for termination is done in the test.
388func assertCollected(c *gc.C, linec chan string, compare []string, injection func([]string)) {402func assertCollected(c *gc.C, linec chan string, compare []string, injection func([]string)) {
403 if len(compare) == 0 {
404 return
405 }
389 timeout := time.After(testing.LongWait)406 timeout := time.After(testing.LongWait)
390 lines := []string{}407 lines := []string{}
391 for {408 for {

Subscribers

People subscribed via source and target branches

to status/vote changes: