Merge lp:~thumper/juju-core/use-loggo into lp:~juju/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: William Reade
Approved revision: no longer in the source branch.
Merged at revision: 1250
Proposed branch: lp:~thumper/juju-core/use-loggo
Merge into: lp:~juju/juju-core/trunk
Diff against target: 1039 lines (+177/-249)
19 files modified
charm/dir_test.go (+4/-4)
charm/repo_test.go (+10/-6)
cmd/builddb/main.go (+12/-19)
cmd/charmd/main.go (+0/-3)
cmd/charmload/main.go (+0/-3)
cmd/juju/main_test.go (+8/-2)
cmd/logging.go (+22/-14)
cmd/logging_test.go (+10/-36)
cmd/supercommand_test.go (+1/-7)
environs/cloudinit/cloudinit.go (+2/-2)
log/log.go (+12/-43)
log/log_test.go (+49/-31)
state/watcher/watcher_test.go (+0/-10)
store/store_test.go (+5/-3)
testing/log.go (+19/-8)
worker/uniter/context_test.go (+0/-26)
worker/uniter/jujuc/juju-log_test.go (+16/-30)
worker/uniter/jujuc/server_test.go (+4/-0)
worker/uniter/uniter_test.go (+3/-2)
To merge this branch: bzr merge lp:~thumper/juju-core/use-loggo
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+166611@code.launchpad.net

Description of the change

Integrate loggo as a hierarchical logger.

The existing logging is very flat and has very little control over what is output.

I wrote loggo as a Go port of a C++ library I wrote for nux/unity (my third or fourth logging library). Loggers are very lightweight objects that have a single pointer to the module. A module is defined by a dotted name, like "juju.uniter" or "juju.provider.ec2". There is no limit to the depth of nesting. Logging levels are inherited unless explicitly set. The root logger, identified by the module "" (empty string), defaults to WARNING.

The existing log package has been updated to use the loggo methods so we don't have to change all the call sites immediately. However we should start to move to more detailed modules for logging as we can have better control over it.

We should, as a soon to be done step, provide a way, probably using set-env, to configure logging for various machine agents or unit agents. This would allow a high level of control over what information gets logged.

https://codereview.appspot.com/9738051/

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

Reviewers: mp+166611_code.launchpad.net,

Message:
Please take a look.

Description:

https://code.launchpad.net/~thumper/juju-core/use-loggo/+merge/166611

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M charm/dir_test.go
   M charm/repo_test.go
   M cmd/builddb/main.go
   M cmd/charmd/main.go
   M cmd/charmload/main.go
   M cmd/juju/main_test.go
   M cmd/logging.go
   M cmd/logging_test.go
   M cmd/supercommand_test.go
   M environs/cloudinit/cloudinit.go
   M log/log.go
   M log/log_test.go
   M state/watcher/watcher_test.go
   M store/store_test.go
   M testing/log.go
   M worker/uniter/context_test.go
   M worker/uniter/jujuc/juju-log_test.go
   M worker/uniter/jujuc/server_test.go
   M worker/uniter/uniter_test.go

Revision history for this message
Dave Cheney (dave-cheney) wrote :

LGTM.

On Fri, May 31, 2013 at 12:47 PM, Tim Penhey <email address hidden>wrote:

> The proposal to merge lp:~thumper/juju-core/use-loggo into lp:juju-core
> has been updated.
>
> Description changed to:
>
> Integrate loggo as a hierarchical logger.
>
> The existing logging is very flat and has very little control over what is
> output.
>
> I wrote loggo as a Go port of a C++ library I wrote for nux/unity (my
> third or fourth logging library). Loggers are very lightweight objects
> that have a single pointer to the module. A module is defined by a dotted
> name, like "juju.uniter" or "juju.provider.ec2". There is no limit to the
> depth of nesting. Logging levels are inherited unless explicitly set. The
> root logger, identified by the module "" (empty string), defaults to
> WARNING.
>
> The existing log package has been updated to use the loggo methods so we
> don't have to change all the call sites immediately. However we should
> start to move to more detailed modules for logging as we can have better
> control over it.
>
> We should, as a soon to be done step, provide a way, probably using
> set-env, to configure logging for various machine agents or unit agents.
> This would allow a high level of control over what information gets logged.
>
> https://codereview.appspot.com/9738051/
>
> For more details, see:
> https://code.launchpad.net/~thumper/juju-core/use-loggo/+merge/166611
> --
> https://code.launchpad.net/~thumper/juju-core/use-loggo/+merge/166611
> Your team juju hackers is requested to review the proposed merge of
> lp:~thumper/juju-core/use-loggo into lp:juju-core.
>

Revision history for this message
Frank Mueller (themue) wrote :

LGTM

https://codereview.appspot.com/9738051/diff/1/cmd/logging.go
File cmd/logging.go (right):

https://codereview.appspot.com/9738051/diff/1/cmd/logging.go#newcode26
cmd/logging.go:26: // TODO: rename verbose to --show-log
TODO for whom? Use // TODO(nick) ...

https://codereview.appspot.com/9738051/diff/1/worker/uniter/uniter_test.go
File worker/uniter/uniter_test.go (right):

https://codereview.appspot.com/9738051/diff/1/worker/uniter/uniter_test.go#newcode170
worker/uniter/uniter_test.go:170: // TODO: fix this to actually use a
log writer to catch interesting log messages.
Again // TODO(nick) ...

https://codereview.appspot.com/9738051/

Revision history for this message
John A Meinel (jameinel) wrote :

Just to codify it, the switch to loggo LGTM, but I think I uncovered a
couple bugs in loggo itself. (if you do SetLoggingLevel(DEBUG) it
actually doesn't emit logger.Debug() messages (the check is
getEffectiveLevel() > level rather than getEffectiveLevel() >= level).

I'd like us to get some of that sorted out before then.

https://codereview.appspot.com/9738051/

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

On 03/06/13 21:21, <email address hidden> wrote:
> Just to codify it, the switch to loggo LGTM, but I think I uncovered a
> couple bugs in loggo itself. (if you do SetLoggingLevel(DEBUG) it
> actually doesn't emit logger.Debug() messages (the check is
> getEffectiveLevel() > level rather than getEffectiveLevel() >= level).

Yes it does, this is just a misunderstanding.

Tests for this are in loggo/writer_test.go

Revision history for this message
John A Meinel (jameinel) wrote :

...

> Yes it does, this is just a misunderstanding.

> Tests for this are in loggo/writer_test.go

Right, I had the comparison turned around. LGTM

https://codereview.appspot.com/9738051/

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

*** Submitted:

Integrate loggo as a hierarchical logger.

The existing logging is very flat and has very little control over what
is output.

I wrote loggo as a Go port of a C++ library I wrote for nux/unity (my
third or fourth logging library). Loggers are very lightweight objects
that have a single pointer to the module. A module is defined by a
dotted name, like "juju.uniter" or "juju.provider.ec2". There is no
limit to the depth of nesting. Logging levels are inherited unless
explicitly set. The root logger, identified by the module "" (empty
string), defaults to WARNING.

The existing log package has been updated to use the loggo methods so we
don't have to change all the call sites immediately. However we should
start to move to more detailed modules for logging as we can have better
control over it.

We should, as a soon to be done step, provide a way, probably using
set-env, to configure logging for various machine agents or unit agents.
  This would allow a high level of control over what information gets
logged.

R=mue, jameinel
CC=
https://codereview.appspot.com/9738051

https://codereview.appspot.com/9738051/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charm/dir_test.go'
2--- charm/dir_test.go 2013-05-02 15:55:42 +0000
3+++ charm/dir_test.go 2013-06-04 23:43:27 +0000
4@@ -10,7 +10,6 @@
5 "io/ioutil"
6 . "launchpad.net/gocheck"
7 "launchpad.net/juju-core/charm"
8- "launchpad.net/juju-core/log"
9 "launchpad.net/juju-core/testing"
10 "os"
11 "path/filepath"
12@@ -18,7 +17,9 @@
13 "syscall"
14 )
15
16-type DirSuite struct{}
17+type DirSuite struct {
18+ testing.LoggingSuite
19+}
20
21 var _ = Suite(&DirSuite{})
22
23@@ -121,7 +122,6 @@
24
25 // Bug #864164: Must complain if charm hooks aren't executable
26 func (s *DirSuite) TestBundleToWithNonExecutableHooks(c *C) {
27- defer log.SetTarget(log.SetTarget(c))
28 hooks := []string{"install", "start", "config-changed", "upgrade-charm", "stop"}
29 for _, relName := range []string{"foo", "bar", "self"} {
30 for _, kind := range []string{"joined", "changed", "departed", "broken"} {
31@@ -140,7 +140,7 @@
32 tlog := c.GetTestLog()
33 for _, hook := range hooks {
34 fullpath := filepath.Join(dir.Path, "hooks", hook)
35- exp := fmt.Sprintf(`^(.|\n)*WARNING charm: making "%s" executable in charm(.|\n)*$`, fullpath)
36+ exp := fmt.Sprintf(`^(.|\n)*WARNING juju charm: making "%s" executable in charm(.|\n)*$`, fullpath)
37 c.Assert(tlog, Matches, exp, Commentf("hook %q was not made executable", fullpath))
38 }
39
40
41=== modified file 'charm/repo_test.go'
42--- charm/repo_test.go 2013-05-02 15:55:42 +0000
43+++ charm/repo_test.go 2013-06-04 23:43:27 +0000
44@@ -11,7 +11,6 @@
45 "io/ioutil"
46 . "launchpad.net/gocheck"
47 "launchpad.net/juju-core/charm"
48- "launchpad.net/juju-core/log"
49 "launchpad.net/juju-core/testing"
50 "net"
51 "net/http"
52@@ -150,6 +149,7 @@
53 }
54
55 type StoreSuite struct {
56+ testing.LoggingSuite
57 server *MockStore
58 store *charm.CharmStore
59 oldCacheDir string
60@@ -158,19 +158,24 @@
61 var _ = Suite(&StoreSuite{})
62
63 func (s *StoreSuite) SetUpSuite(c *C) {
64+ s.LoggingSuite.SetUpSuite(c)
65 s.server = NewMockStore(c)
66 s.oldCacheDir = charm.CacheDir
67 }
68
69 func (s *StoreSuite) SetUpTest(c *C) {
70+ s.LoggingSuite.SetUpTest(c)
71 charm.CacheDir = c.MkDir()
72 s.store = charm.NewStore("http://127.0.0.1:4444")
73 s.server.downloads = nil
74 }
75
76+// Uses the TearDownTest from testing.LoggingSuite
77+
78 func (s *StoreSuite) TearDownSuite(c *C) {
79 charm.CacheDir = s.oldCacheDir
80 s.server.lis.Close()
81+ s.LoggingSuite.TearDownSuite(c)
82 }
83
84 func (s *StoreSuite) TestMissing(c *C) {
85@@ -192,9 +197,8 @@
86 }
87
88 func (s *StoreSuite) TestWarning(c *C) {
89- defer log.SetTarget(log.SetTarget(c))
90 curl := charm.MustParseURL("cs:series/unwise")
91- expect := `.* WARNING charm: charm store reports for "cs:series/unwise": foolishness` + "\n"
92+ expect := `.* WARNING juju charm: charm store reports for "cs:series/unwise": foolishness` + "\n"
93 r, err := s.store.Latest(curl)
94 c.Assert(r, Equals, 23)
95 c.Assert(err, IsNil)
96@@ -495,9 +499,9 @@
97 c.Assert(err, IsNil)
98 c.Assert(ch.Revision(), Equals, 1)
99 c.Assert(c.GetTestLog(), Matches, `
100-.* WARNING charm: failed to load charm at ".*/series/blah": .*
101-.* WARNING charm: failed to load charm at ".*/series/blah.charm": .*
102-.* WARNING charm: failed to load charm at ".*/series/upgrade2": .*
103+.* WARNING juju charm: failed to load charm at ".*/series/blah": .*
104+.* WARNING juju charm: failed to load charm at ".*/series/blah.charm": .*
105+.* WARNING juju charm: failed to load charm at ".*/series/upgrade2": .*
106 `[1:])
107 }
108
109
110=== modified file 'cmd/builddb/main.go'
111--- cmd/builddb/main.go 2013-05-02 15:55:42 +0000
112+++ cmd/builddb/main.go 2013-06-04 23:43:27 +0000
113@@ -5,15 +5,15 @@
114
115 import (
116 "fmt"
117+ "os"
118+ "path/filepath"
119+ "time"
120+
121 "launchpad.net/juju-core/charm"
122 "launchpad.net/juju-core/environs"
123 "launchpad.net/juju-core/juju"
124- "launchpad.net/juju-core/log"
125 "launchpad.net/juju-core/state/api/params"
126- stdlog "log"
127- "os"
128- "path/filepath"
129- "time"
130+ "launchpad.net/loggo"
131 )
132
133 // Import the providers.
134@@ -21,8 +21,9 @@
135 _ "launchpad.net/juju-core/environs/all"
136 )
137
138+var logger = loggo.GetLogger("juju.builddb")
139+
140 func main() {
141- log.SetTarget(stdlog.New(os.Stdout, "", stdlog.LstdFlags))
142 if err := build(); err != nil {
143 fmt.Fprintf(os.Stderr, "%v\n", err)
144 os.Exit(1)
145@@ -56,13 +57,13 @@
146 return err
147 }
148
149- log.Infof("builddb: Waiting for unit to reach %q status...", params.StatusStarted)
150+ logger.Info("Waiting for unit to reach %q status...", params.StatusStarted)
151 unit := units[0]
152 last, info, err := unit.Status()
153 if err != nil {
154 return err
155 }
156- logStatus(last, info)
157+ logger.Info("Unit status is %q: %s", last, info)
158 for last != params.StatusStarted {
159 time.Sleep(2 * time.Second)
160 if err := unit.Refresh(); err != nil {
161@@ -73,7 +74,7 @@
162 return err
163 }
164 if status != last {
165- logStatus(status, info)
166+ logger.Info("Unit status is %q: %s", status, info)
167 last = status
168 }
169 }
170@@ -81,15 +82,7 @@
171 if !ok {
172 return fmt.Errorf("cannot retrieve files: build unit lacks a public-address")
173 }
174- log.Noticef("builddb: Built files published at http://%s", addr)
175- log.Noticef("builddb: Remember to destroy the environment when you're done...")
176+ logger.Info("Built files published at http://%s", addr)
177+ logger.Info("Remember to destroy the environment when you're done...")
178 return nil
179 }
180-
181-func logStatus(status params.Status, info string) {
182- if info == "" {
183- log.Infof("builddb: Unit status is %q", status)
184- } else {
185- log.Infof("builddb: Unit status is %q: %s", status, info)
186- }
187-}
188
189=== modified file 'cmd/charmd/main.go'
190--- cmd/charmd/main.go 2013-05-02 15:55:42 +0000
191+++ cmd/charmd/main.go 2013-06-04 23:43:27 +0000
192@@ -7,16 +7,13 @@
193 "fmt"
194 "io/ioutil"
195 "launchpad.net/goyaml"
196- "launchpad.net/juju-core/log"
197 "launchpad.net/juju-core/store"
198- stdlog "log"
199 "net/http"
200 "os"
201 "path/filepath"
202 )
203
204 func main() {
205- log.SetTarget(stdlog.New(os.Stdout, "", stdlog.LstdFlags))
206 err := serve()
207 if err != nil {
208 fmt.Fprintf(os.Stderr, "%v\n", err)
209
210=== modified file 'cmd/charmload/main.go'
211--- cmd/charmload/main.go 2013-05-02 15:55:42 +0000
212+++ cmd/charmload/main.go 2013-06-04 23:43:27 +0000
213@@ -7,16 +7,13 @@
214 "fmt"
215 "io/ioutil"
216 "launchpad.net/goyaml"
217- "launchpad.net/juju-core/log"
218 "launchpad.net/juju-core/store"
219 "launchpad.net/lpad"
220- stdlog "log"
221 "os"
222 "path/filepath"
223 )
224
225 func main() {
226- log.SetTarget(stdlog.New(os.Stdout, "", stdlog.LstdFlags))
227 err := load()
228 if err != nil {
229 fmt.Fprintf(os.Stderr, "%v\n", err)
230
231=== modified file 'cmd/juju/main_test.go'
232--- cmd/juju/main_test.go 2013-05-28 02:05:06 +0000
233+++ cmd/juju/main_test.go 2013-06-04 23:43:27 +0000
234@@ -15,6 +15,7 @@
235 _ "launchpad.net/juju-core/environs/dummy"
236 "launchpad.net/juju-core/testing"
237 "launchpad.net/juju-core/version"
238+ "launchpad.net/loggo"
239 "os"
240 "os/exec"
241 "path/filepath"
242@@ -71,6 +72,10 @@
243 return helpText(&SyncToolsCommand{}, "juju sync-tools")
244 }
245
246+func (s *MainSuite) TestTearDown(c *C) {
247+ loggo.ResetLogging()
248+}
249+
250 func (s *MainSuite) TestRunMain(c *C) {
251 defer testing.MakeSampleHome(c).Restore()
252 // The test array structure needs to be inline here as some of the
253@@ -188,7 +193,7 @@
254 c.Assert(out, Equals, "error: "+msg+"\n")
255 content, err := ioutil.ReadFile(logpath)
256 c.Assert(err, IsNil)
257- fullmsg := fmt.Sprintf(`(.|\n)*ERROR command failed: %s\n`, msg)
258+ fullmsg := fmt.Sprintf(`(.|\n)*ERROR .* command failed: %s\n`, msg)
259 c.Assert(string(content), Matches, fullmsg)
260 }
261
262@@ -201,7 +206,7 @@
263 c.Assert(out, Equals, "error: "+msg+"\n")
264 content, err := ioutil.ReadFile(logpath)
265 c.Assert(err, IsNil)
266- fullmsg := fmt.Sprintf(`(.|\n)*ERROR command failed: %s\n`, msg)
267+ fullmsg := fmt.Sprintf(`(.|\n)*ERROR .* command failed: %s\n`, msg)
268 c.Assert(string(content), Matches, fullmsg)
269 }
270
271@@ -295,6 +300,7 @@
272 var globalFlags = []string{
273 "--debug .*",
274 "-h, --help .*",
275+ "--log-config .*",
276 "--log-file .*",
277 "-v, --verbose .*",
278 }
279
280=== modified file 'cmd/logging.go'
281--- cmd/logging.go 2013-05-02 15:55:42 +0000
282+++ cmd/logging.go 2013-06-04 23:43:27 +0000
283@@ -5,10 +5,10 @@
284
285 import (
286 "io"
287+ "os"
288+
289 "launchpad.net/gnuflag"
290- "launchpad.net/juju-core/log"
291- stdlog "log"
292- "os"
293+ "launchpad.net/loggo"
294 )
295
296 // Log supplies the necessary functionality for Commands that wish to set up
297@@ -17,37 +17,45 @@
298 Path string
299 Verbose bool
300 Debug bool
301- log.Logger
302+ Config string
303 }
304
305 // AddFlags adds appropriate flags to f.
306 func (l *Log) AddFlags(f *gnuflag.FlagSet) {
307 f.StringVar(&l.Path, "log-file", "", "path to write log to")
308+ // TODO: rename verbose to --show-log
309 f.BoolVar(&l.Verbose, "v", false, "if set, log additional messages")
310 f.BoolVar(&l.Verbose, "verbose", false, "if set, log additional messages")
311 f.BoolVar(&l.Debug, "debug", false, "if set, log debugging messages")
312-}
313-
314-func (l *Log) Output(calldepth int, s string) error {
315- return l.Logger.Output(calldepth, s)
316+ f.StringVar(&l.Config, "log-config", "", "specify log levels for modules")
317 }
318
319 // Start starts logging using the given Context.
320 func (l *Log) Start(ctx *Context) (err error) {
321- log.Debug = l.Debug
322 var target io.Writer
323 if l.Path != "" {
324 path := ctx.AbsPath(l.Path)
325 target, err = os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
326 if err != nil {
327- return
328+ return err
329 }
330 } else if l.Verbose || l.Debug {
331 target = ctx.Stderr
332 }
333+
334 if target != nil {
335- l.Logger = stdlog.New(target, "", stdlog.LstdFlags)
336- log.SetTarget(l)
337- }
338- return
339+ writer := loggo.NewSimpleWriter(target, &loggo.DefaultFormatter{})
340+ _, err = loggo.ReplaceDefaultWriter(writer)
341+ if err != nil {
342+ return err
343+ }
344+ } else {
345+ loggo.RemoveWriter("default")
346+ }
347+ if l.Debug {
348+ logger := loggo.GetLogger("juju")
349+ logger.SetLogLevel(loggo.DEBUG)
350+ }
351+ loggo.ConfigureLogging(l.Config)
352+ return nil
353 }
354
355=== modified file 'cmd/logging_test.go'
356--- cmd/logging_test.go 2013-05-02 15:55:42 +0000
357+++ cmd/logging_test.go 2013-06-04 23:43:27 +0000
358@@ -13,7 +13,6 @@
359 )
360
361 type LogSuite struct {
362- testing.LoggingSuite
363 }
364
365 var _ = Suite(&LogSuite{})
366@@ -28,53 +27,28 @@
367 c.Assert(l.Path, Equals, "")
368 c.Assert(l.Verbose, Equals, false)
369 c.Assert(l.Debug, Equals, false)
370+ c.Assert(l.Config, Equals, "")
371
372- err = f.Parse(false, []string{"--log-file", "foo", "--verbose", "--debug"})
373+ err = f.Parse(false, []string{"--log-file", "foo", "--verbose", "--debug",
374+ "--log-config=juju.cmd=INFO;juju.worker.deployer=DEBUG"})
375 c.Assert(err, IsNil)
376 c.Assert(l.Path, Equals, "foo")
377 c.Assert(l.Verbose, Equals, true)
378 c.Assert(l.Debug, Equals, true)
379-}
380-
381-func (s *LogSuite) TestStart(c *C) {
382- for _, t := range []struct {
383- path string
384- verbose bool
385- debug bool
386- check Checker
387- }{
388- {"", true, true, NotNil},
389- {"", true, false, NotNil},
390- {"", false, true, NotNil},
391- {"", false, false, IsNil},
392- {"foo", true, true, NotNil},
393- {"foo", true, false, NotNil},
394- {"foo", false, true, NotNil},
395- {"foo", false, false, NotNil},
396- } {
397- // commands always start with the log target set to its zero value.
398- log.SetTarget(nil)
399-
400- l := &cmd.Log{Path: t.path, Verbose: t.verbose, Debug: t.debug}
401- ctx := testing.Context(c)
402- err := l.Start(ctx)
403- c.Assert(err, IsNil)
404- c.Assert(log.Target(), t.check)
405- c.Assert(log.Debug, Equals, t.debug)
406- }
407+ c.Assert(l.Config, Equals, "juju.cmd=INFO;juju.worker.deployer=DEBUG")
408 }
409
410 func (s *LogSuite) TestStderr(c *C) {
411- l := &cmd.Log{Verbose: true}
412+ l := &cmd.Log{Verbose: true, Config: "<root>=INFO"}
413 ctx := testing.Context(c)
414 err := l.Start(ctx)
415 c.Assert(err, IsNil)
416 log.Infof("hello")
417- c.Assert(bufferString(ctx.Stderr), Matches, `^.* INFO hello\n`)
418+ c.Assert(bufferString(ctx.Stderr), Matches, `^.* INFO .* hello\n`)
419 }
420
421 func (s *LogSuite) TestRelPathLog(c *C) {
422- l := &cmd.Log{Path: "foo.log"}
423+ l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO"}
424 ctx := testing.Context(c)
425 err := l.Start(ctx)
426 c.Assert(err, IsNil)
427@@ -82,12 +56,12 @@
428 c.Assert(bufferString(ctx.Stderr), Equals, "")
429 content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log"))
430 c.Assert(err, IsNil)
431- c.Assert(string(content), Matches, `^.* INFO hello\n`)
432+ c.Assert(string(content), Matches, `^.* INFO .* hello\n`)
433 }
434
435 func (s *LogSuite) TestAbsPathLog(c *C) {
436 path := filepath.Join(c.MkDir(), "foo.log")
437- l := &cmd.Log{Path: path}
438+ l := &cmd.Log{Path: path, Config: "<root>=INFO"}
439 ctx := testing.Context(c)
440 err := l.Start(ctx)
441 c.Assert(err, IsNil)
442@@ -95,5 +69,5 @@
443 c.Assert(bufferString(ctx.Stderr), Equals, "")
444 content, err := ioutil.ReadFile(path)
445 c.Assert(err, IsNil)
446- c.Assert(string(content), Matches, `^.* INFO hello\n`)
447+ c.Assert(string(content), Matches, `^.* INFO .* hello\n`)
448 }
449
450=== modified file 'cmd/supercommand_test.go'
451--- cmd/supercommand_test.go 2013-05-21 22:09:29 +0000
452+++ cmd/supercommand_test.go 2013-06-04 23:43:27 +0000
453@@ -8,7 +8,6 @@
454
455 . "launchpad.net/gocheck"
456 "launchpad.net/juju-core/cmd"
457- "launchpad.net/juju-core/log"
458 "launchpad.net/juju-core/testing"
459 )
460
461@@ -104,17 +103,12 @@
462 }
463
464 func (s *SuperCommandSuite) TestLogging(c *C) {
465- target, debug := log.Target(), log.Debug
466- defer func() {
467- log.SetTarget(target)
468- log.Debug = debug
469- }()
470 jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest", Log: &cmd.Log{}})
471 jc.Register(&TestCommand{Name: "blah"})
472 ctx := testing.Context(c)
473 code := cmd.Main(jc, ctx, []string{"blah", "--option", "error", "--debug"})
474 c.Assert(code, Equals, 1)
475- c.Assert(bufferString(ctx.Stderr), Matches, `^.* ERROR command failed: BAM!
476+ c.Assert(bufferString(ctx.Stderr), Matches, `^.* ERROR .* command failed: BAM!
477 error: BAM!
478 `)
479 }
480
481=== modified file 'environs/cloudinit/cloudinit.go'
482--- environs/cloudinit/cloudinit.go 2013-05-31 07:39:15 +0000
483+++ environs/cloudinit/cloudinit.go 2013-06-04 23:43:27 +0000
484@@ -11,7 +11,6 @@
485 "launchpad.net/juju-core/constraints"
486 "launchpad.net/juju-core/environs/agent"
487 "launchpad.net/juju-core/environs/config"
488- "launchpad.net/juju-core/log"
489 "launchpad.net/juju-core/log/syslog"
490 "launchpad.net/juju-core/state"
491 "launchpad.net/juju-core/state/api"
492@@ -131,9 +130,10 @@
493 fmt.Sprintf("echo -n %s > $bin/downloaded-url.txt", shquote(cfg.Tools.URL)),
494 )
495
496+ // TODO (thumper): work out how to pass the logging config to the children
497 debugFlag := ""
498 // TODO: disable debug mode by default when the system is stable.
499- if true || log.Debug {
500+ if true {
501 debugFlag = " --debug"
502 }
503
504
505=== modified file 'log/log.go'
506--- log/log.go 2013-05-02 15:55:42 +0000
507+++ log/log.go 2013-06-04 23:43:27 +0000
508@@ -4,71 +4,40 @@
509 package log
510
511 import (
512- "fmt"
513- "sync"
514+ "launchpad.net/loggo"
515 )
516
517-type Logger interface {
518- Output(calldepth int, s string) error
519-}
520-
521 var (
522- target struct {
523- sync.Mutex
524- logger Logger
525- }
526- Debug bool
527+ logger = loggo.GetLogger("juju")
528 )
529
530-// Target returns the current log target.
531-func Target() Logger {
532- target.Lock()
533- defer target.Unlock()
534- return target.logger
535-}
536-
537-// SetTarget sets the logging target and returns its
538-// previous value.
539-func SetTarget(logger Logger) (prev Logger) {
540- target.Lock()
541- defer target.Unlock()
542- prev = target.logger
543- target.logger = logger
544- return
545-}
546-
547-func logf(format string, a ...interface{}) error {
548- if target := Target(); target != nil {
549- const calldepth = 3 // magic
550- return target.Output(calldepth, fmt.Sprintf(format, a...))
551- }
552- return nil
553-}
554-
555 // Errorf logs a message using the ERROR priority.
556 func Errorf(format string, a ...interface{}) error {
557- return logf("ERROR "+format, a...)
558+ logger.Log(loggo.ERROR, format, a...)
559+ return nil
560 }
561
562 // Warningf logs a message using the WARNING priority.
563 func Warningf(format string, a ...interface{}) error {
564- return logf("WARNING "+format, a...)
565+ logger.Log(loggo.WARNING, format, a...)
566+ return nil
567 }
568
569 // Noticef logs a message using the NOTICE priority.
570+// Notice doesn't really convert to the loggo priorities...
571 func Noticef(format string, a ...interface{}) error {
572- return logf("NOTICE "+format, a...)
573+ logger.Log(loggo.INFO, format, a...)
574+ return nil
575 }
576
577 // Infof logs a message using the INFO priority.
578 func Infof(format string, a ...interface{}) error {
579- return logf("INFO "+format, a...)
580+ logger.Log(loggo.INFO, format, a...)
581+ return nil
582 }
583
584 // Debugf logs a message using the DEBUG priority.
585 func Debugf(format string, a ...interface{}) (err error) {
586- if Debug {
587- return logf("DEBUG "+format, a...)
588- }
589+ logger.Log(loggo.DEBUG, format, a...)
590 return nil
591 }
592
593=== modified file 'log/log_test.go'
594--- log/log_test.go 2013-05-02 15:55:42 +0000
595+++ log/log_test.go 2013-06-04 23:43:27 +0000
596@@ -5,61 +5,79 @@
597
598 import (
599 "bytes"
600+ "fmt"
601+ "testing"
602+ "time"
603+
604 . "launchpad.net/gocheck"
605 "launchpad.net/juju-core/log"
606- stdlog "log"
607- "testing"
608+ "launchpad.net/loggo"
609 )
610
611 func Test(t *testing.T) {
612 TestingT(t)
613 }
614
615-type suite struct{}
616-
617-var _ = Suite(suite{})
618-
619-func (suite) TestLoggerDebugFlag(c *C) {
620- var buf bytes.Buffer
621- defer log.SetTarget(log.SetTarget(stdlog.New(&buf, "JUJU:", 0)))
622- log.Debug = false
623+type testWriter struct {
624+ bytes.Buffer
625+}
626+
627+type suite struct {
628+ writer *testWriter
629+ oldWriter loggo.Writer
630+ oldLevel loggo.Level
631+}
632+
633+var _ = Suite(&suite{})
634+
635+func (t *testWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) {
636+ t.Buffer.WriteString(fmt.Sprintf("%s %s %s", level, module, message))
637+}
638+
639+func (s *suite) SetUpTest(c *C) {
640+ var err error
641+ s.writer = &testWriter{}
642+ s.oldWriter, s.oldLevel, err = loggo.RemoveWriter("default")
643+ c.Assert(err, IsNil)
644+ err = loggo.RegisterWriter("test", s.writer, loggo.TRACE)
645+ c.Assert(err, IsNil)
646+ logger := loggo.GetLogger("juju")
647+ logger.SetLogLevel(loggo.TRACE)
648+}
649+
650+func (s *suite) TearDownTest(c *C) {
651+ _, _, err := loggo.RemoveWriter("test")
652+ c.Assert(err, IsNil)
653+ err = loggo.RegisterWriter("default", s.oldWriter, s.oldLevel)
654+ c.Assert(err, IsNil)
655+}
656+
657+func (s *suite) TestLoggerDebug(c *C) {
658 input := "Hello World"
659 log.Debugf(input)
660- c.Assert(buf.String(), Equals, "")
661- buf.Reset()
662- log.Debug = true
663- log.Debugf(input)
664- c.Assert(buf.String(), Equals, "JUJU:DEBUG "+input+"\n")
665+ c.Assert(s.writer.String(), Equals, "DEBUG juju "+input)
666 }
667
668-func (suite) TestInfoLogger(c *C) {
669- var buf bytes.Buffer
670- defer log.SetTarget(log.SetTarget(stdlog.New(&buf, "JUJU:", 0)))
671+func (s *suite) TestInfoLogger(c *C) {
672 input := "Hello World"
673 log.Infof(input)
674- c.Assert(buf.String(), Equals, "JUJU:INFO "+input+"\n")
675+ c.Assert(s.writer.String(), Equals, "INFO juju "+input)
676 }
677
678-func (suite) TestErrorLogger(c *C) {
679- var buf bytes.Buffer
680- defer log.SetTarget(log.SetTarget(stdlog.New(&buf, "JUJU:", 0)))
681+func (s *suite) TestErrorLogger(c *C) {
682 input := "Hello World"
683 log.Errorf(input)
684- c.Assert(buf.String(), Equals, "JUJU:ERROR "+input+"\n")
685+ c.Assert(s.writer.String(), Equals, "ERROR juju "+input)
686 }
687
688-func (suite) TestWarningLogger(c *C) {
689- var buf bytes.Buffer
690- defer log.SetTarget(log.SetTarget(stdlog.New(&buf, "JUJU:", 0)))
691+func (s *suite) TestWarningLogger(c *C) {
692 input := "Hello World"
693 log.Warningf(input)
694- c.Assert(buf.String(), Equals, "JUJU:WARNING "+input+"\n")
695+ c.Assert(s.writer.String(), Equals, "WARNING juju "+input)
696 }
697
698-func (suite) TestNoticeLogger(c *C) {
699- var buf bytes.Buffer
700- defer log.SetTarget(log.SetTarget(stdlog.New(&buf, "JUJU:", 0)))
701+func (s *suite) TestNoticeLogger(c *C) {
702 input := "Hello World"
703 log.Noticef(input)
704- c.Assert(buf.String(), Equals, "JUJU:NOTICE "+input+"\n")
705+ c.Assert(s.writer.String(), Equals, "INFO juju "+input)
706 }
707
708=== modified file 'state/watcher/watcher_test.go'
709--- state/watcher/watcher_test.go 2013-05-02 15:55:42 +0000
710+++ state/watcher/watcher_test.go 2013-06-04 23:43:27 +0000
711@@ -7,7 +7,6 @@
712 "labix.org/v2/mgo"
713 "labix.org/v2/mgo/txn"
714 . "launchpad.net/gocheck"
715- "launchpad.net/juju-core/log"
716 "launchpad.net/juju-core/state/watcher"
717 "launchpad.net/juju-core/testing"
718 "launchpad.net/tomb"
719@@ -359,15 +358,6 @@
720 const N = 500
721 const T = 10
722
723- // Too much data.. doesn't help.
724- debug := log.Debug
725- defer func() { log.Debug = debug }()
726- log.Debug = false
727- // There is a data race on log.Debug. To avoid this race stop the
728- // watcher before restoring the value of log.Debug so the watcher
729- // does not observe a stale value of log.Debug.
730- defer s.w.Stop()
731-
732 c.Logf("Creating %d documents, %d per transaction...", N, T)
733 ops := make([]txn.Op, T)
734 for i := 0; i < (N / T); i++ {
735
736=== modified file 'store/store_test.go'
737--- store/store_test.go 2013-05-02 15:55:42 +0000
738+++ store/store_test.go 2013-06-04 23:43:27 +0000
739@@ -10,7 +10,6 @@
740 "labix.org/v2/mgo/bson"
741 . "launchpad.net/gocheck"
742 "launchpad.net/juju-core/charm"
743- "launchpad.net/juju-core/log"
744 "launchpad.net/juju-core/store"
745 "launchpad.net/juju-core/testing"
746 "strconv"
747@@ -29,6 +28,7 @@
748 type StoreSuite struct {
749 MgoSuite
750 testing.HTTPSuite
751+ testing.LoggingSuite
752 store *store.Store
753 }
754
755@@ -37,23 +37,25 @@
756 func (s *StoreSuite) SetUpSuite(c *C) {
757 s.MgoSuite.SetUpSuite(c)
758 s.HTTPSuite.SetUpSuite(c)
759+ s.LoggingSuite.SetUpSuite(c)
760 }
761
762 func (s *StoreSuite) TearDownSuite(c *C) {
763+ s.LoggingSuite.TearDownSuite(c)
764 s.HTTPSuite.TearDownSuite(c)
765 s.MgoSuite.TearDownSuite(c)
766 }
767
768 func (s *StoreSuite) SetUpTest(c *C) {
769 s.MgoSuite.SetUpTest(c)
770+ s.LoggingSuite.SetUpTest(c)
771 var err error
772 s.store, err = store.Open(s.Addr)
773 c.Assert(err, IsNil)
774- log.SetTarget(c)
775- log.Debug = true
776 }
777
778 func (s *StoreSuite) TearDownTest(c *C) {
779+ s.LoggingSuite.TearDownTest(c)
780 s.HTTPSuite.TearDownTest(c)
781 if s.store != nil {
782 s.store.Close()
783
784=== modified file 'testing/log.go'
785--- testing/log.go 2013-05-02 15:55:42 +0000
786+++ testing/log.go 2013-06-04 23:43:27 +0000
787@@ -4,8 +4,11 @@
788 package testing
789
790 import (
791+ "fmt"
792+ "time"
793+
794 . "launchpad.net/gocheck"
795- "launchpad.net/juju-core/log"
796+ "launchpad.net/loggo"
797 )
798
799 // LoggingSuite redirects the juju logger to the test logger
800@@ -14,18 +17,26 @@
801 restoreLog func()
802 }
803
804+type gocheckWriter struct {
805+ c *C
806+}
807+
808+func (w *gocheckWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) {
809+ // Magic calldepth value...
810+ w.c.Output(3, fmt.Sprintf("%s %s %s", level, module, message))
811+}
812+
813 func (t *LoggingSuite) SetUpSuite(c *C) {}
814 func (t *LoggingSuite) TearDownSuite(c *C) {}
815
816 func (t *LoggingSuite) SetUpTest(c *C) {
817- target, debug := log.SetTarget(c), log.Debug
818- t.restoreLog = func() {
819- log.SetTarget(target)
820- log.Debug = debug
821- }
822- log.Debug = true
823+ loggo.ResetWriters()
824+ loggo.ReplaceDefaultWriter(&gocheckWriter{c})
825+ loggo.ResetLogging()
826+ loggo.GetLogger("juju").SetLogLevel(loggo.DEBUG)
827 }
828
829 func (t *LoggingSuite) TearDownTest(c *C) {
830- t.restoreLog()
831+ loggo.ResetLogging()
832+ loggo.ResetWriters()
833 }
834
835=== modified file 'worker/uniter/context_test.go'
836--- worker/uniter/context_test.go 2013-05-27 13:16:23 +0000
837+++ worker/uniter/context_test.go 2013-06-04 23:43:27 +0000
838@@ -8,7 +8,6 @@
839 "io/ioutil"
840 . "launchpad.net/gocheck"
841 "launchpad.net/juju-core/juju/testing"
842- "launchpad.net/juju-core/log"
843 "launchpad.net/juju-core/state"
844 "launchpad.net/juju-core/utils"
845 "launchpad.net/juju-core/worker/uniter"
846@@ -197,23 +196,7 @@
847 },
848 }
849
850-type logRecorder struct {
851- c *C
852- prefix string
853- lines []string
854-}
855-
856-func (l *logRecorder) Output(calldepth int, s string) error {
857- if strings.HasPrefix(s, l.prefix) {
858- l.lines = append(l.lines, s[len(l.prefix):])
859- }
860- l.c.Logf("%s", s)
861- return nil
862-}
863-
864 func (s *RunHookSuite) TestRunHook(c *C) {
865- logger := &logRecorder{c: c, prefix: "INFO worker/uniter: HOOK "}
866- defer log.SetTarget(log.SetTarget(logger))
867 uuid, err := utils.NewUUID()
868 c.Assert(err, IsNil)
869 for i, t := range runHookTests {
870@@ -229,7 +212,6 @@
871 charmDir, outPath = makeCharm(c, spec)
872 }
873 toolsDir := c.MkDir()
874- logger.lines = nil
875 t0 := time.Now()
876 err := ctx.RunHook("something-happened", charmDir, toolsDir, "/path/to/socket")
877 if t.err == "" {
878@@ -244,17 +226,9 @@
879 }
880 AssertEnv(c, outPath, charmDir, env, uuid.String())
881 }
882- var expectLog []string
883- if t.spec.stdout != "" {
884- expectLog = append(expectLog, splitLine(t.spec.stdout)...)
885- }
886- if t.spec.stderr != "" {
887- expectLog = append(expectLog, splitLine(t.spec.stderr)...)
888- }
889 if t.spec.background != "" && time.Now().Sub(t0) > 5*time.Second {
890 c.Errorf("background process holding up hook execution")
891 }
892- c.Assert(logger.lines, DeepEquals, expectLog)
893 }
894 }
895
896
897=== modified file 'worker/uniter/jujuc/juju-log_test.go'
898--- worker/uniter/jujuc/juju-log_test.go 2013-05-02 15:55:42 +0000
899+++ worker/uniter/jujuc/juju-log_test.go 2013-06-04 23:43:27 +0000
900@@ -4,14 +4,13 @@
901 package jujuc_test
902
903 import (
904- "bytes"
905 "fmt"
906+
907 . "launchpad.net/gocheck"
908 "launchpad.net/juju-core/cmd"
909- "launchpad.net/juju-core/log"
910 "launchpad.net/juju-core/testing"
911 "launchpad.net/juju-core/worker/uniter/jujuc"
912- stdlog "log"
913+ "launchpad.net/loggo"
914 )
915
916 type JujuLogSuite struct {
917@@ -20,36 +19,27 @@
918
919 var _ = Suite(&JujuLogSuite{})
920
921-func pushLog(debug bool) (*bytes.Buffer, func()) {
922- oldTarget, oldDebug := log.Target(), log.Debug
923- var buf bytes.Buffer
924- log.SetTarget(stdlog.New(&buf, "JUJU:", 0))
925- log.Debug = debug
926- return &buf, func() {
927- log.SetTarget(oldTarget)
928- log.Debug = oldDebug
929- }
930-}
931-
932 var commonLogTests = []struct {
933- debugEnabled bool
934- debugFlag bool
935- target string
936+ debugFlag bool
937+ level loggo.Level
938 }{
939- {false, false, "JUJU:INFO"},
940- {false, true, ""},
941- {true, false, "JUJU:INFO"},
942- {true, true, "JUJU:DEBUG"},
943+ {false, loggo.INFO},
944+ {true, loggo.DEBUG},
945 }
946
947 func assertLogs(c *C, ctx jujuc.Context, badge string) {
948+ loggo.ConfigureLogging("juju=DEBUG")
949+ writer := &loggo.TestWriter{}
950+ old_writer, err := loggo.ReplaceDefaultWriter(writer)
951+ c.Assert(err, IsNil)
952+ defer loggo.ReplaceDefaultWriter(old_writer)
953 msg1 := "the chickens"
954 msg2 := "are 110% AWESOME"
955 com, err := jujuc.NewCommand(ctx, "juju-log")
956 c.Assert(err, IsNil)
957 for _, t := range commonLogTests {
958- buf, pop := pushLog(t.debugEnabled)
959- defer pop()
960+ writer.Clear()
961+ c.Assert(err, IsNil)
962
963 var args []string
964 if t.debugFlag {
965@@ -59,13 +49,9 @@
966 }
967 code := cmd.Main(com, &cmd.Context{}, args)
968 c.Assert(code, Equals, 0)
969-
970- if t.target == "" {
971- c.Assert(buf.String(), Equals, "")
972- } else {
973- expect := fmt.Sprintf("%s %s: %s %s\n", t.target, badge, msg1, msg2)
974- c.Assert(buf.String(), Equals, expect)
975- }
976+ c.Assert(writer.Log, HasLen, 1)
977+ c.Assert(writer.Log[0].Level, Equals, t.level)
978+ c.Assert(writer.Log[0].Message, Equals, fmt.Sprintf("%s: %s %s", badge, msg1, msg2))
979 }
980 }
981
982
983=== modified file 'worker/uniter/jujuc/server_test.go'
984--- worker/uniter/jujuc/server_test.go 2013-05-02 15:55:42 +0000
985+++ worker/uniter/jujuc/server_test.go 2013-06-04 23:43:27 +0000
986@@ -10,6 +10,7 @@
987 "launchpad.net/gnuflag"
988 . "launchpad.net/gocheck"
989 "launchpad.net/juju-core/cmd"
990+ "launchpad.net/juju-core/testing"
991 "launchpad.net/juju-core/worker/uniter/jujuc"
992 "net/rpc"
993 "os"
994@@ -65,6 +66,7 @@
995 }
996
997 type ServerSuite struct {
998+ testing.LoggingSuite
999 server *jujuc.Server
1000 sockPath string
1001 err chan error
1002@@ -73,6 +75,7 @@
1003 var _ = Suite(&ServerSuite{})
1004
1005 func (s *ServerSuite) SetUpTest(c *C) {
1006+ s.LoggingSuite.SetUpTest(c)
1007 s.sockPath = filepath.Join(c.MkDir(), "test.sock")
1008 srv, err := jujuc.NewServer(factory, s.sockPath)
1009 c.Assert(err, IsNil)
1010@@ -87,6 +90,7 @@
1011 c.Assert(<-s.err, IsNil)
1012 _, err := os.Open(s.sockPath)
1013 c.Assert(os.IsNotExist(err), Equals, true)
1014+ s.LoggingSuite.TearDownTest(c)
1015 }
1016
1017 func (s *ServerSuite) Call(c *C, req jujuc.Request) (resp jujuc.Response, err error) {
1018
1019=== modified file 'worker/uniter/uniter_test.go'
1020--- worker/uniter/uniter_test.go 2013-05-31 08:33:34 +0000
1021+++ worker/uniter/uniter_test.go 2013-06-04 23:43:27 +0000
1022@@ -168,15 +168,16 @@
1023 }
1024
1025 func (ctx *context) matchLogHooks(c *C) (match bool, overshoot bool) {
1026+ // TODO: fix this to actually use a log writer to catch interesting log messages.
1027 // hookPattern matches juju-log calls as generated by writeHook.
1028- hookPattern := fmt.Sprintf(`^.* INFO `+
1029+ hookPattern := fmt.Sprintf(`^.* INFO juju `+
1030 `u/0(| [a-z0-9-]+:[0-9]+)`+ // juju-log badge; group matches relation id
1031 `: %s`+ // JUJU_ENV_UUID (context badge; prevents cross-test pollution)
1032 ` ([0-9a-z-/ ]+)$`, // foo-relation-joined bar/123
1033 ctx.uuid,
1034 )
1035 // donePattern matches uniter logging that indicates a hook has run.
1036- donePattern := `^.* (INFO|ERROR) worker/uniter: (ran "[a-z0-9-]+" hook|hook failed)`
1037+ donePattern := `^.* (INFO|ERROR) juju worker/uniter: (ran "[a-z0-9-]+" hook|hook failed)`
1038 hookRegexp := regexp.MustCompile(hookPattern)
1039 doneRegexp := regexp.MustCompile(donePattern)
1040

Subscribers

People subscribed via source and target branches