Merge lp:~rogpeppe/loggo/minor-fixes into lp:loggo

Proposed by Roger Peppe
Status: Merged
Merged at revision: 36
Proposed branch: lp:~rogpeppe/loggo/minor-fixes
Merge into: lp:loggo
Diff against target: 1242 lines (+475/-283)
5 files modified
formatter.go (+6/-6)
logger.go (+195/-99)
logger_test.go (+219/-134)
writer.go (+34/-23)
writer_test.go (+21/-21)
To merge this branch: bzr merge lp:~rogpeppe/loggo/minor-fixes
Reviewer Review Type Date Requested Status
Tim Penhey Pending
Review via email: mp+167832@code.launchpad.net

Description of the change

loggo: lots of minor changes

I've made quite a few changes here, mostly to make
things more Go idiomatic, to add documentation,
and hopefully to make things a bit cleaner and
in one or two cases a little more efficient.

I've added a final "f" onto all the Error, Warning, etc
functions so that they are amenable to checking with
govet. I've also taken out the special casing for no
arguments - if the first argument is a printf-like
format string, it should work as one regardless of
whether there are arguments or not.
If we want a more efficient version, we could add
Logger.Error(msg string) function or similar.

There seemed to be a general mixing of the term
"logger" and "module" so I have changed things
to standardise on "Logger" (as that's the type name)
and changed the Module method and some documentation
accordingly.

I've also made renamed ConfigureLogging to ConfigureLoggers
and DumpLoggingLevels to LoggerInfo and made ConfigureLoggers
understand the format produced by LoggerInfo, which seems
to me a fairly obvious thing to do. I've also made ConfigureLoggers
more tolerant of white space. I haven't in this branch, but in
a subsequent branch I am tempted to propose case insensitivity
throughout - I think that's actually a benefit, as well as
being more efficient. I think it would be best to standardise
on a single separator too - probably semicolon.

To go along with usual Go standards, I've also made functions
that parse stuff return an error if the parsing fails. I think it's
useful to be able to know if ConfigureLoggers is given an
invalid specification, for example.

I've changed the default formatting to print the time in UTC - if
we're not printing the time zone, I think that's important.
I'd quite like the default formatter to print millisecond resolution,
but that can wait.

Things I've thought of but have not done:
- We could make Logger a pointer type and lose the module type. It's
reasonable to say that a nil logger is equivalent to the root logger,
I think and get the same ok-to-use zero Logger semantics. YMMV about
that, so I left it for discussion.

- We could make ReplaceDefaultWriter more general, say ReplaceWriter,
because the functionality it provides (atomically replacing a writer)
is not possible to get otherwise - it's more than a convenience method.

Please feel free to say which things you don't like and I'll revert
those changes. In general I like the package quite a bit - thanks for doing it!

https://codereview.appspot.com/10043045/

To post a comment you must log in.
Revision history for this message
Roger Peppe (rogpeppe) wrote :

Reviewers: mp+167832_code.launchpad.net,

Message:
Please take a look.

Description:
loggo: lots of minor changes

I've made quite a few changes here, mostly to make
things more Go idiomatic, to add documentation,
and hopefully to make things a bit cleaner and
in one or two cases a little more efficient.

I've added a final "f" onto all the Error, Warning, etc
functions so that they are amenable to checking with
govet. I've also taken out the special casing for no
arguments - if the first argument is a printf-like
format string, it should work as one regardless of
whether there are arguments or not.
If we want a more efficient version, we could add
Logger.Error(msg string) function or similar.

There seemed to be a general mixing of the term
"logger" and "module" so I have changed things
to standardise on "Logger" (as that's the type name)
and changed the Module method and some documentation
accordingly.

I've also made renamed ConfigureLogging to ConfigureLoggers
and DumpLoggingLevels to LoggerInfo and made ConfigureLoggers
understand the format produced by LoggerInfo, which seems
to me a fairly obvious thing to do. I've also made ConfigureLoggers
more tolerant of white space. I haven't in this branch, but in
a subsequent branch I am tempted to propose case insensitivity
throughout - I think that's actually a benefit, as well as
being more efficient. I think it would be best to standardise
on a single separator too - probably semicolon.

To go along with usual Go standards, I've also made functions
that parse stuff return an error if the parsing fails. I think it's
useful to be able to know if ConfigureLoggers is given an
invalid specification, for example.

I've changed the default formatting to print the time in UTC - if
we're not printing the time zone, I think that's important.
I'd quite like the default formatter to print millisecond resolution,
but that can wait.

Things I've thought of but have not done:
- We could make Logger a pointer type and lose the module type. It's
reasonable to say that a nil logger is equivalent to the root logger,
I think and get the same ok-to-use zero Logger semantics. YMMV about
that, so I left it for discussion.

- We could make ReplaceDefaultWriter more general, say ReplaceWriter,
because the functionality it provides (atomically replacing a writer)
is not possible to get otherwise - it's more than a convenience method.

Please feel free to say which things you don't like and I'll revert
those changes. In general I like the package quite a bit - thanks for
doing it!

https://code.launchpad.net/~rogpeppe/loggo/minor-fixes/+merge/167832

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M formatter.go
   M logger.go
   M logger_test.go
   M writer.go
   M writer_test.go

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'formatter.go'
2--- formatter.go 2013-05-09 15:55:53 +0000
3+++ formatter.go 2013-06-06 19:14:25 +0000
4@@ -2,7 +2,7 @@
5
6 import (
7 "fmt"
8- "path"
9+ "path/filepath"
10 "time"
11 )
12
13@@ -12,15 +12,15 @@
14 Format(level Level, module, filename string, line int, timestamp time.Time, message string) string
15 }
16
17-// DefaultFormatter provides a simple concatination of all the components.
18+// DefaultFormatter provides a simple concatenation of all the components.
19 type DefaultFormatter struct{}
20
21 // Format returns the parameters separated by spaces except for filename and
22-// line which are separated by a colon. The timestamp is only shown to second
23-// resolution, and no timezone is shown.
24+// line which are separated by a colon. The timestamp is shown to second
25+// resolution in UTC.
26 func (*DefaultFormatter) Format(level Level, module, filename string, line int, timestamp time.Time, message string) string {
27- ts := timestamp.Format("2006-01-02 15:04:05")
28+ ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05")
29 // Just get the basename from the filename
30- filename = path.Base(filename)
31+ filename = filepath.Base(filename)
32 return fmt.Sprintf("%s %s %s %s:%d %s", ts, level, module, filename, line, message)
33 }
34
35=== modified file 'logger.go'
36--- logger.go 2013-06-04 05:02:31 +0000
37+++ logger.go 2013-06-06 19:14:25 +0000
38@@ -3,14 +3,14 @@
39 // This package provides an alternative to the standard library log package.
40 //
41 // The actual logging functions never return errors. If you are logging
42-// something, you really don't want to be worried about the actual logging
43+// something, you really don't want to be worried about the logging
44 // having trouble.
45 //
46 // Modules have names that are defined by dotted strings.
47 // e.g. "first.second.third"
48 //
49-// There is a root module that is reflected by the name "". Each module
50-// (except the root module) has a parent, that is identified by the part of
51+// There is a root module that has the name "". Each module
52+// (except the root module) has a parent, identified by the part of
53 // the name without the last dotted value.
54 // e.g. the parent of "first.second.third" is "first.second"
55 // the parent of "first.second" is "first"
56@@ -23,8 +23,9 @@
57 // Loggers are created using the GetLogger function.
58 // e.g. logger := loggo.GetLogger("foo.bar")
59 //
60-// By default there is one writer registered, which will write to Stderr, and
61-// only emit warnings and above. If you want to continue using the default
62+// By default there is one writer registered, which will write to Stderr,
63+// and the root module, which will only emit warnings and above.
64+// If you want to continue using the default
65 // logger, but have it emit all logging levels you need to do the following.
66 //
67 // writer, _, err := loggo.RemoveWriter("default")
68@@ -42,10 +43,13 @@
69 "time"
70 )
71
72+// Level holds a severity level.
73 type Level uint32
74
75+// The severity levels. Higher values are more considered more
76+// important.
77 const (
78- NOT_SPECIFIED Level = iota
79+ UNSPECIFIED Level = iota
80 TRACE
81 DEBUG
82 INFO
83@@ -54,7 +58,13 @@
84 CRITICAL
85 )
86
87-// A Logger with a null impl pointer is effectively a root logger.
88+// A Logger represents a logging module. It has an associated logging
89+// level which can be changed; messages of lesser severity will
90+// be dropped. Loggers have a hierarchical relationship - see
91+// the package documentation.
92+//
93+// The zero Logger value is usable - any messages logged
94+// to it will be sent to the root Logger.
95 type Logger struct {
96 impl *module
97 }
98@@ -67,7 +77,7 @@
99
100 // Initially the modules map only contains the root module.
101 var (
102- root = &module{"", WARNING, nil}
103+ root = &module{level: WARNING}
104 modulesMutex sync.Mutex
105 modules = map[string]*module{
106 "": root,
107@@ -76,8 +86,8 @@
108
109 func (level Level) String() string {
110 switch level {
111- case NOT_SPECIFIED:
112- return "NOT_SPECIFIED"
113+ case UNSPECIFIED:
114+ return "UNSPECIFIED"
115 case TRACE:
116 return "TRACE"
117 case DEBUG:
118@@ -94,24 +104,35 @@
119 return "<unknown>"
120 }
121
122+// get atomically gets the value of the given level.
123+func (level *Level) get() Level {
124+ return Level(atomic.LoadUint32((*uint32)(level)))
125+}
126+
127+// set atomically sets the value of the receiver
128+// to the given level.
129+func (level *Level) set(newLevel Level) {
130+ atomic.StoreUint32((*uint32)(level), uint32(newLevel))
131+}
132+
133 // getLoggerInternal assumes that the modulesMutex is locked.
134 func getLoggerInternal(name string) Logger {
135 impl, found := modules[name]
136 if found {
137 return Logger{impl}
138 }
139-
140- parent := root
141- // Really wanting rsplit.
142- parts := strings.Split(name, ".")
143- if len(parts) > 1 {
144- parent = getLoggerInternal(strings.Join(parts[:len(parts)-1], ".")).impl
145+ parentName := ""
146+ if i := strings.LastIndex(name, "."); i >= 0 {
147+ parentName = name[0:i]
148 }
149- impl = &module{name, NOT_SPECIFIED, parent}
150+ parent := getLoggerInternal(parentName).impl
151+ impl = &module{name, UNSPECIFIED, parent}
152 modules[name] = impl
153 return Logger{impl}
154 }
155
156+// GetLogger returns a Logger for the given module name,
157+// creating it and its parents if necessary.
158 func GetLogger(name string) Logger {
159 // Lowercase the module name, and look for it in the modules map.
160 name = strings.ToLower(name)
161@@ -120,7 +141,11 @@
162 return getLoggerInternal(name)
163 }
164
165-func DumpLoggingLevels() string {
166+// LoggerInfo returns information about the configured loggers and their logging
167+// levels. The information is returned in the format expected by
168+// ConfigureModules. Loggers with UNSPECIFIED level will not
169+// be included.
170+func LoggerInfo() string {
171 output := []string{}
172 // output in alphabetical order.
173 keys := []string{}
174@@ -133,67 +158,86 @@
175 for _, name := range keys {
176 mod := modules[name]
177 severity := mod.level
178- if severity == NOT_SPECIFIED {
179+ if severity == UNSPECIFIED {
180 continue
181 }
182- module_name := mod.name
183- if module_name == "" {
184- module_name = "<root>"
185- }
186- output = append(output, fmt.Sprintf("%s %s", module_name, severity))
187+ output = append(output, fmt.Sprintf("%s=%s", mod.Name(), severity))
188 }
189- return strings.Join(output, "\n")
190+ return strings.Join(output, ";")
191 }
192
193-func ConfigureLogging(configString string) {
194- if configString == "" {
195- return
196+// ConfigureLoggers configures loggers according to the given string
197+// specification, which specifies a set of modules and their associated
198+// logging levels. Loggers are colon- or semicolon-separated; each
199+// module is specified as <modulename>=<level>. White space outside of
200+// module names and levels is ignored. The root module is specified
201+// with the name "<root>".
202+//
203+// An example specification:
204+// `<root>=ERROR; foo.bar=WARNING`
205+func ConfigureLoggers(specification string) error {
206+ if specification == "" {
207+ return nil
208 }
209-
210- values := strings.FieldsFunc(configString, func(r rune) bool { return r == ';' || r == ':' })
211+ values := strings.FieldsFunc(specification, func(r rune) bool { return r == ';' || r == ':' })
212+ levels := make(map[string]Level)
213 for _, value := range values {
214 s := strings.SplitN(value, "=", 2)
215 if len(s) < 2 {
216- continue
217- }
218- name := s[0]
219- level := s[1]
220+ return fmt.Errorf("logger specification expected '=', found %q", value)
221+ }
222+ name := strings.TrimSpace(s[0])
223+ levelStr := strings.TrimSpace(s[1])
224+ if name == "" || levelStr == "" {
225+ return fmt.Errorf("logger specification %q has blank name or level", value)
226+ }
227 if name == "<root>" {
228 name = ""
229 }
230- logger := GetLogger(name)
231- logger.SetLogLevel(GetLoggingLevel(level))
232- }
233+ level, ok := ParseLevel(levelStr)
234+ if !ok {
235+ return fmt.Errorf("unknown severity level %q", levelStr)
236+ }
237+ levels[name] = level
238+ }
239+ for name, level := range levels {
240+ GetLogger(name).SetLogLevel(level)
241+ }
242+ return nil
243 }
244
245 // ResetLogging iterates through the known modules and sets the levels of all
246-// to NOT_SPECIFIED, except for <root> which is set to WARNING.
247-func ResetLogging() {
248+// to UNSPECIFIED, except for <root> which is set to WARNING.
249+func ResetLoggers() {
250 modulesMutex.Lock()
251 defer modulesMutex.Unlock()
252 for _, module := range modules {
253- module.level = NOT_SPECIFIED
254+ module.level = UNSPECIFIED
255 }
256 root.level = WARNING
257 }
258
259-// GetLoggingLevel converts a string representation of a logging level to the
260-// enumerated value.
261-func GetLoggingLevel(level string) Level {
262+// ParseLevel converts a string representation of a logging level to a
263+// Level. It returns the level and whether it was valid or not.
264+func ParseLevel(level string) (Level, bool) {
265 level = strings.ToUpper(level)
266 switch level {
267+ case "UNSPECIFIED":
268+ return UNSPECIFIED, true
269 case "TRACE":
270- return TRACE
271+ return TRACE, true
272 case "DEBUG":
273- return DEBUG
274+ return DEBUG, true
275 case "INFO":
276- return INFO
277+ return INFO, true
278 case "WARN", "WARNING":
279- return WARNING
280+ return WARNING, true
281 case "ERROR":
282- return ERROR
283+ return ERROR, true
284+ case "CRITICAL":
285+ return CRITICAL, true
286 }
287- return WARNING
288+ return UNSPECIFIED, false
289 }
290
291 func (logger Logger) getModule() *module {
292@@ -203,36 +247,66 @@
293 return logger.impl
294 }
295
296-func (logger Logger) Module() string {
297- return logger.getModule().name
298+// Name returns the logger's module name.
299+func (logger Logger) Name() string {
300+ return logger.getModule().Name()
301 }
302
303-func (logger Logger) GetLogLevel() Level {
304- return Level(atomic.LoadUint32((*uint32)(&logger.getModule().level)))
305+// LogLevel returns the configured log level of the logger.
306+func (logger Logger) LogLevel() Level {
307+ return logger.getModule().level.get()
308 }
309
310 func (module *module) getEffectiveLogLevel() Level {
311- level := Level(atomic.LoadUint32((*uint32)(&module.level)))
312- if level == NOT_SPECIFIED && module.parent != nil {
313- return module.parent.getEffectiveLogLevel()
314- }
315- return level
316-}
317-
318-func (logger Logger) GetEffectiveLogLevel() Level {
319+ // Note: the root module is guaranteed to have a
320+ // specified logging level, so acts as a suitable sentinel
321+ // for this loop.
322+ for {
323+ if level := module.level.get(); level != UNSPECIFIED {
324+ return level
325+ }
326+ module = module.parent
327+ }
328+ panic("unreachable")
329+}
330+
331+func (module *module) Name() string {
332+ if module.name == "" {
333+ return "<root>"
334+ }
335+ return module.name
336+}
337+
338+// EffectiveLogLevel returns the effective log level of
339+// the receiver - that is, messages with a lesser severity
340+// level will be discarded.
341+//
342+// If the log level of the receiver is unspecified,
343+// it will be taken from the effective log level of its
344+// parent.
345+func (logger Logger) EffectiveLogLevel() Level {
346 return logger.getModule().getEffectiveLogLevel()
347 }
348
349+// SetLogLevel sets the severity level of the given logger.
350+// The root logger cannot be set to UNSPECIFIED level.
351+// See EffectiveLogLevel for how this affects the
352+// actual messages logged.
353 func (logger Logger) SetLogLevel(level Level) {
354 module := logger.getModule()
355 // The root module can't be unspecified.
356- if module.name == "" && level == NOT_SPECIFIED {
357+ if module.name == "" && level == UNSPECIFIED {
358 level = WARNING
359 }
360- atomic.StoreUint32((*uint32)(&module.level), uint32(level))
361+ module.level.set(level)
362 }
363
364-func (logger Logger) Log(level Level, message string, args ...interface{}) {
365+// Logf logs a printf-formatted message at the given level.
366+// A message will be discarded if level is less than the
367+// the effective log level of the logger.
368+// Note that the writers may also filter out messages that
369+// are less than their registered minimum severity level.
370+func (logger Logger) Logf(level Level, message string, args ...interface{}) {
371 if logger.getModule().getEffectiveLogLevel() > level ||
372 !WillWrite(level) ||
373 level < TRACE ||
374@@ -241,8 +315,6 @@
375 }
376 // Gather time, and filename, line number.
377 now := time.Now() // get this early.
378- var file string
379- var line int
380 // Param to Caller is the call depth. Since this method is called from
381 // the Logger methods, we want the place that those were called from.
382 _, file, line, ok := runtime.Caller(2)
383@@ -250,54 +322,78 @@
384 file = "???"
385 line = 0
386 }
387+ // Trim newline off format string, following usual
388+ // Go logging conventions.
389+ if len(message) > 0 && message[len(message)-1] == '\n' {
390+ message = message[0 : len(message)-1]
391+ }
392
393- formattedMessage := message
394- if len(args) > 0 {
395- formattedMessage = fmt.Sprintf(message, args...)
396- }
397+ formattedMessage := fmt.Sprintf(message, args...)
398 writeToWriters(level, logger.impl.name, file, line, now, formattedMessage)
399 }
400
401-func (logger Logger) Critical(message string, args ...interface{}) {
402- logger.Log(CRITICAL, message, args...)
403-}
404-
405-func (logger Logger) Error(message string, args ...interface{}) {
406- logger.Log(ERROR, message, args...)
407-}
408-
409-func (logger Logger) Warning(message string, args ...interface{}) {
410- logger.Log(WARNING, message, args...)
411-}
412-
413-func (logger Logger) Info(message string, args ...interface{}) {
414- logger.Log(INFO, message, args...)
415-}
416-
417-func (logger Logger) Debug(message string, args ...interface{}) {
418- logger.Log(DEBUG, message, args...)
419-}
420-
421-func (logger Logger) Trace(message string, args ...interface{}) {
422- logger.Log(TRACE, message, args...)
423-}
424-
425+// Criticalf logs the printf-formatted message at critical level.
426+func (logger Logger) Criticalf(message string, args ...interface{}) {
427+ logger.Logf(CRITICAL, message, args...)
428+}
429+
430+// Criticalf logs the printf-formatted message at error level.
431+func (logger Logger) Errorf(message string, args ...interface{}) {
432+ logger.Logf(ERROR, message, args...)
433+}
434+
435+// Criticalf logs the printf-formatted message at warning level.
436+func (logger Logger) Warningf(message string, args ...interface{}) {
437+ logger.Logf(WARNING, message, args...)
438+}
439+
440+// Criticalf logs the printf-formatted message at info level.
441+func (logger Logger) Infof(message string, args ...interface{}) {
442+ logger.Logf(INFO, message, args...)
443+}
444+
445+// Criticalf logs the printf-formatted message at debug level.
446+func (logger Logger) Debugf(message string, args ...interface{}) {
447+ logger.Logf(DEBUG, message, args...)
448+}
449+
450+// Criticalf logs the printf-formatted message at trace level.
451+func (logger Logger) Tracef(message string, args ...interface{}) {
452+ logger.Logf(TRACE, message, args...)
453+}
454+
455+// IsLevelEnabled returns whether debugging is enabled
456+// for the given log level.
457+func (logger Logger) IsLevelEnabled(level Level) bool {
458+ return logger.getModule().getEffectiveLogLevel() <= level
459+}
460+
461+// IsErrorEnabled returns whether debugging is enabled
462+// at error level.
463 func (logger Logger) IsErrorEnabled() bool {
464- return logger.getModule().getEffectiveLogLevel() <= ERROR
465+ return logger.IsLevelEnabled(ERROR)
466 }
467
468+// IsWarningEnabled returns whether debugging is enabled
469+// at warning level.
470 func (logger Logger) IsWarningEnabled() bool {
471- return logger.getModule().getEffectiveLogLevel() <= WARNING
472+ return logger.IsLevelEnabled(WARNING)
473 }
474
475+// IsInfoEnabled returns whether debugging is enabled
476+// at info level.
477 func (logger Logger) IsInfoEnabled() bool {
478- return logger.getModule().getEffectiveLogLevel() <= INFO
479+ return logger.IsLevelEnabled(INFO)
480 }
481
482+// IsDebugEnabled returns whether debugging is enabled
483+// at debug level.
484 func (logger Logger) IsDebugEnabled() bool {
485- return logger.getModule().getEffectiveLogLevel() <= DEBUG
486+ return logger.IsLevelEnabled(DEBUG)
487 }
488
489+// IsTraceEnabled returns whether debugging is enabled
490+// at trace level.
491 func (logger Logger) IsTraceEnabled() bool {
492- return logger.getModule().getEffectiveLogLevel() <= TRACE
493+ return logger.IsLevelEnabled(TRACE)
494 }
495
496=== modified file 'logger_test.go'
497--- logger_test.go 2013-06-04 03:37:54 +0000
498+++ logger_test.go 2013-06-06 19:14:25 +0000
499@@ -1,10 +1,8 @@
500 package loggo_test
501
502 import (
503- "fmt"
504 "io/ioutil"
505 "os"
506- "strings"
507 "testing"
508
509 . "launchpad.net/gocheck"
510@@ -20,12 +18,12 @@
511 var _ = Suite(&loggerSuite{})
512
513 func (*loggerSuite) SetUpTest(c *C) {
514- loggo.ResetLogging()
515+ loggo.ResetLoggers()
516 }
517
518 func (*loggerSuite) TestRootLogger(c *C) {
519 root := loggo.Logger{}
520- c.Assert(root.Module(), Equals, "")
521+ c.Assert(root.Name(), Equals, "<root>")
522 c.Assert(root.IsErrorEnabled(), Equals, true)
523 c.Assert(root.IsWarningEnabled(), Equals, true)
524 c.Assert(root.IsInfoEnabled(), Equals, false)
525@@ -35,54 +33,54 @@
526
527 func (*loggerSuite) TestModuleName(c *C) {
528 logger := loggo.GetLogger("loggo.testing")
529- c.Assert(logger.Module(), Equals, "loggo.testing")
530+ c.Assert(logger.Name(), Equals, "loggo.testing")
531 }
532
533 func (*loggerSuite) TestSetLevel(c *C) {
534 logger := loggo.GetLogger("testing")
535
536- c.Assert(logger.GetLogLevel(), Equals, loggo.NOT_SPECIFIED)
537- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.WARNING)
538+ c.Assert(logger.LogLevel(), Equals, loggo.UNSPECIFIED)
539+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.WARNING)
540 c.Assert(logger.IsErrorEnabled(), Equals, true)
541 c.Assert(logger.IsWarningEnabled(), Equals, true)
542 c.Assert(logger.IsInfoEnabled(), Equals, false)
543 c.Assert(logger.IsDebugEnabled(), Equals, false)
544 c.Assert(logger.IsTraceEnabled(), Equals, false)
545 logger.SetLogLevel(loggo.TRACE)
546- c.Assert(logger.GetLogLevel(), Equals, loggo.TRACE)
547- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.TRACE)
548+ c.Assert(logger.LogLevel(), Equals, loggo.TRACE)
549+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.TRACE)
550 c.Assert(logger.IsErrorEnabled(), Equals, true)
551 c.Assert(logger.IsWarningEnabled(), Equals, true)
552 c.Assert(logger.IsInfoEnabled(), Equals, true)
553 c.Assert(logger.IsDebugEnabled(), Equals, true)
554 c.Assert(logger.IsTraceEnabled(), Equals, true)
555 logger.SetLogLevel(loggo.DEBUG)
556- c.Assert(logger.GetLogLevel(), Equals, loggo.DEBUG)
557- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.DEBUG)
558+ c.Assert(logger.LogLevel(), Equals, loggo.DEBUG)
559+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.DEBUG)
560 c.Assert(logger.IsErrorEnabled(), Equals, true)
561 c.Assert(logger.IsWarningEnabled(), Equals, true)
562 c.Assert(logger.IsInfoEnabled(), Equals, true)
563 c.Assert(logger.IsDebugEnabled(), Equals, true)
564 c.Assert(logger.IsTraceEnabled(), Equals, false)
565 logger.SetLogLevel(loggo.INFO)
566- c.Assert(logger.GetLogLevel(), Equals, loggo.INFO)
567- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.INFO)
568+ c.Assert(logger.LogLevel(), Equals, loggo.INFO)
569+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.INFO)
570 c.Assert(logger.IsErrorEnabled(), Equals, true)
571 c.Assert(logger.IsWarningEnabled(), Equals, true)
572 c.Assert(logger.IsInfoEnabled(), Equals, true)
573 c.Assert(logger.IsDebugEnabled(), Equals, false)
574 c.Assert(logger.IsTraceEnabled(), Equals, false)
575 logger.SetLogLevel(loggo.WARNING)
576- c.Assert(logger.GetLogLevel(), Equals, loggo.WARNING)
577- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.WARNING)
578+ c.Assert(logger.LogLevel(), Equals, loggo.WARNING)
579+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.WARNING)
580 c.Assert(logger.IsErrorEnabled(), Equals, true)
581 c.Assert(logger.IsWarningEnabled(), Equals, true)
582 c.Assert(logger.IsInfoEnabled(), Equals, false)
583 c.Assert(logger.IsDebugEnabled(), Equals, false)
584 c.Assert(logger.IsTraceEnabled(), Equals, false)
585 logger.SetLogLevel(loggo.ERROR)
586- c.Assert(logger.GetLogLevel(), Equals, loggo.ERROR)
587- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.ERROR)
588+ c.Assert(logger.LogLevel(), Equals, loggo.ERROR)
589+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.ERROR)
590 c.Assert(logger.IsErrorEnabled(), Equals, true)
591 c.Assert(logger.IsWarningEnabled(), Equals, false)
592 c.Assert(logger.IsInfoEnabled(), Equals, false)
593@@ -90,16 +88,16 @@
594 c.Assert(logger.IsTraceEnabled(), Equals, false)
595 // This is added for completeness, but not really expected to be used.
596 logger.SetLogLevel(loggo.CRITICAL)
597- c.Assert(logger.GetLogLevel(), Equals, loggo.CRITICAL)
598- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.CRITICAL)
599+ c.Assert(logger.LogLevel(), Equals, loggo.CRITICAL)
600+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.CRITICAL)
601 c.Assert(logger.IsErrorEnabled(), Equals, false)
602 c.Assert(logger.IsWarningEnabled(), Equals, false)
603 c.Assert(logger.IsInfoEnabled(), Equals, false)
604 c.Assert(logger.IsDebugEnabled(), Equals, false)
605 c.Assert(logger.IsTraceEnabled(), Equals, false)
606- logger.SetLogLevel(loggo.NOT_SPECIFIED)
607- c.Assert(logger.GetLogLevel(), Equals, loggo.NOT_SPECIFIED)
608- c.Assert(logger.GetEffectiveLogLevel(), Equals, loggo.WARNING)
609+ logger.SetLogLevel(loggo.UNSPECIFIED)
610+ c.Assert(logger.LogLevel(), Equals, loggo.UNSPECIFIED)
611+ c.Assert(logger.EffectiveLogLevel(), Equals, loggo.WARNING)
612 }
613
614 func (*loggerSuite) TestLevelsSharedForSameModule(c *C) {
615@@ -115,8 +113,8 @@
616 logger1 := loggo.GetLogger("TESTING.MODULE")
617 logger2 := loggo.GetLogger("Testing")
618
619- c.Assert(logger1.Module(), Equals, "testing.module")
620- c.Assert(logger2.Module(), Equals, "testing")
621+ c.Assert(logger1.Name(), Equals, "testing.module")
622+ c.Assert(logger2.Name(), Equals, "testing")
623 }
624
625 func (*loggerSuite) TestLevelsInherited(c *C) {
626@@ -125,107 +123,193 @@
627 second := loggo.GetLogger("first.second")
628
629 root.SetLogLevel(loggo.ERROR)
630- c.Assert(root.GetLogLevel(), Equals, loggo.ERROR)
631- c.Assert(root.GetEffectiveLogLevel(), Equals, loggo.ERROR)
632- c.Assert(first.GetLogLevel(), Equals, loggo.NOT_SPECIFIED)
633- c.Assert(first.GetEffectiveLogLevel(), Equals, loggo.ERROR)
634- c.Assert(second.GetLogLevel(), Equals, loggo.NOT_SPECIFIED)
635- c.Assert(second.GetEffectiveLogLevel(), Equals, loggo.ERROR)
636+ c.Assert(root.LogLevel(), Equals, loggo.ERROR)
637+ c.Assert(root.EffectiveLogLevel(), Equals, loggo.ERROR)
638+ c.Assert(first.LogLevel(), Equals, loggo.UNSPECIFIED)
639+ c.Assert(first.EffectiveLogLevel(), Equals, loggo.ERROR)
640+ c.Assert(second.LogLevel(), Equals, loggo.UNSPECIFIED)
641+ c.Assert(second.EffectiveLogLevel(), Equals, loggo.ERROR)
642
643 first.SetLogLevel(loggo.DEBUG)
644- c.Assert(root.GetLogLevel(), Equals, loggo.ERROR)
645- c.Assert(root.GetEffectiveLogLevel(), Equals, loggo.ERROR)
646- c.Assert(first.GetLogLevel(), Equals, loggo.DEBUG)
647- c.Assert(first.GetEffectiveLogLevel(), Equals, loggo.DEBUG)
648- c.Assert(second.GetLogLevel(), Equals, loggo.NOT_SPECIFIED)
649- c.Assert(second.GetEffectiveLogLevel(), Equals, loggo.DEBUG)
650+ c.Assert(root.LogLevel(), Equals, loggo.ERROR)
651+ c.Assert(root.EffectiveLogLevel(), Equals, loggo.ERROR)
652+ c.Assert(first.LogLevel(), Equals, loggo.DEBUG)
653+ c.Assert(first.EffectiveLogLevel(), Equals, loggo.DEBUG)
654+ c.Assert(second.LogLevel(), Equals, loggo.UNSPECIFIED)
655+ c.Assert(second.EffectiveLogLevel(), Equals, loggo.DEBUG)
656
657 second.SetLogLevel(loggo.INFO)
658- c.Assert(root.GetLogLevel(), Equals, loggo.ERROR)
659- c.Assert(root.GetEffectiveLogLevel(), Equals, loggo.ERROR)
660- c.Assert(first.GetLogLevel(), Equals, loggo.DEBUG)
661- c.Assert(first.GetEffectiveLogLevel(), Equals, loggo.DEBUG)
662- c.Assert(second.GetLogLevel(), Equals, loggo.INFO)
663- c.Assert(second.GetEffectiveLogLevel(), Equals, loggo.INFO)
664-
665- first.SetLogLevel(loggo.NOT_SPECIFIED)
666- c.Assert(root.GetLogLevel(), Equals, loggo.ERROR)
667- c.Assert(root.GetEffectiveLogLevel(), Equals, loggo.ERROR)
668- c.Assert(first.GetLogLevel(), Equals, loggo.NOT_SPECIFIED)
669- c.Assert(first.GetEffectiveLogLevel(), Equals, loggo.ERROR)
670- c.Assert(second.GetLogLevel(), Equals, loggo.INFO)
671- c.Assert(second.GetEffectiveLogLevel(), Equals, loggo.INFO)
672-}
673-
674-func (*loggerSuite) TestGetLoggingLevel(c *C) {
675- c.Assert(loggo.GetLoggingLevel("trace"), Equals, loggo.TRACE)
676- c.Assert(loggo.GetLoggingLevel("TrAce"), Equals, loggo.TRACE)
677- c.Assert(loggo.GetLoggingLevel("TRACE"), Equals, loggo.TRACE)
678- c.Assert(loggo.GetLoggingLevel("debug"), Equals, loggo.DEBUG)
679- c.Assert(loggo.GetLoggingLevel("DEBUG"), Equals, loggo.DEBUG)
680- c.Assert(loggo.GetLoggingLevel("info"), Equals, loggo.INFO)
681- c.Assert(loggo.GetLoggingLevel("INFO"), Equals, loggo.INFO)
682- c.Assert(loggo.GetLoggingLevel("warn"), Equals, loggo.WARNING)
683- c.Assert(loggo.GetLoggingLevel("WARN"), Equals, loggo.WARNING)
684- c.Assert(loggo.GetLoggingLevel("warning"), Equals, loggo.WARNING)
685- c.Assert(loggo.GetLoggingLevel("WARNING"), Equals, loggo.WARNING)
686- c.Assert(loggo.GetLoggingLevel("error"), Equals, loggo.ERROR)
687- c.Assert(loggo.GetLoggingLevel("ERROR"), Equals, loggo.ERROR)
688- // Unknown levels result in WARNING
689- c.Assert(loggo.GetLoggingLevel("critical"), Equals, loggo.WARNING)
690- c.Assert(loggo.GetLoggingLevel("not_specified"), Equals, loggo.WARNING)
691- c.Assert(loggo.GetLoggingLevel("other"), Equals, loggo.WARNING)
692+ c.Assert(root.LogLevel(), Equals, loggo.ERROR)
693+ c.Assert(root.EffectiveLogLevel(), Equals, loggo.ERROR)
694+ c.Assert(first.LogLevel(), Equals, loggo.DEBUG)
695+ c.Assert(first.EffectiveLogLevel(), Equals, loggo.DEBUG)
696+ c.Assert(second.LogLevel(), Equals, loggo.INFO)
697+ c.Assert(second.EffectiveLogLevel(), Equals, loggo.INFO)
698+
699+ first.SetLogLevel(loggo.UNSPECIFIED)
700+ c.Assert(root.LogLevel(), Equals, loggo.ERROR)
701+ c.Assert(root.EffectiveLogLevel(), Equals, loggo.ERROR)
702+ c.Assert(first.LogLevel(), Equals, loggo.UNSPECIFIED)
703+ c.Assert(first.EffectiveLogLevel(), Equals, loggo.ERROR)
704+ c.Assert(second.LogLevel(), Equals, loggo.INFO)
705+ c.Assert(second.EffectiveLogLevel(), Equals, loggo.INFO)
706+}
707+
708+var parseLevelTests = []struct {
709+ str string
710+ level loggo.Level
711+ fail bool
712+}{{
713+ str: "trace",
714+ level: loggo.TRACE,
715+}, {
716+ str: "TrAce",
717+ level: loggo.TRACE,
718+}, {
719+ str: "TRACE",
720+ level: loggo.TRACE,
721+}, {
722+ str: "debug",
723+ level: loggo.DEBUG,
724+}, {
725+ str: "DEBUG",
726+ level: loggo.DEBUG,
727+}, {
728+ str: "info",
729+ level: loggo.INFO,
730+}, {
731+ str: "INFO",
732+ level: loggo.INFO,
733+}, {
734+ str: "warn",
735+ level: loggo.WARNING,
736+}, {
737+ str: "WARN",
738+ level: loggo.WARNING,
739+}, {
740+ str: "warning",
741+ level: loggo.WARNING,
742+}, {
743+ str: "WARNING",
744+ level: loggo.WARNING,
745+}, {
746+ str: "error",
747+ level: loggo.ERROR,
748+}, {
749+ str: "ERROR",
750+ level: loggo.ERROR,
751+}, {
752+ str: "critical",
753+ level: loggo.CRITICAL,
754+}, {
755+ str: "not_specified",
756+ fail: true,
757+}, {
758+ str: "other",
759+ fail: true,
760+}, {
761+ str: "",
762+ fail: true,
763+}}
764+
765+func (*loggerSuite) TestParseLevel(c *C) {
766+ for _, test := range parseLevelTests {
767+ level, ok := loggo.ParseLevel(test.str)
768+ c.Assert(level, Equals, test.level)
769+ c.Assert(ok, Equals, !test.fail)
770+ }
771+}
772+
773+var levelStringValueTests = map[loggo.Level]string{
774+ loggo.UNSPECIFIED: "UNSPECIFIED",
775+ loggo.DEBUG: "DEBUG",
776+ loggo.TRACE: "TRACE",
777+ loggo.INFO: "INFO",
778+ loggo.WARNING: "WARNING",
779+ loggo.ERROR: "ERROR",
780+ loggo.CRITICAL: "CRITICAL",
781+ loggo.Level(42): "<unknown>", // other values are unknown
782 }
783
784 func (*loggerSuite) TestLevelStringValue(c *C) {
785- c.Assert(fmt.Sprint(loggo.NOT_SPECIFIED), Equals, "NOT_SPECIFIED")
786- c.Assert(fmt.Sprint(loggo.TRACE), Equals, "TRACE")
787- c.Assert(fmt.Sprint(loggo.DEBUG), Equals, "DEBUG")
788- c.Assert(fmt.Sprint(loggo.INFO), Equals, "INFO")
789- c.Assert(fmt.Sprint(loggo.WARNING), Equals, "WARNING")
790- c.Assert(fmt.Sprint(loggo.ERROR), Equals, "ERROR")
791- c.Assert(fmt.Sprint(loggo.CRITICAL), Equals, "CRITICAL")
792- // other values are unknown
793- c.Assert(fmt.Sprint(loggo.Level(42)), Equals, "<unknown>")
794-}
795-
796-func (*loggerSuite) TestConfigureLoggingEmpty(c *C) {
797- root := loggo.GetLogger("")
798- root.SetLogLevel(loggo.DEBUG)
799- logger := loggo.GetLogger("test.module")
800- logger.SetLogLevel(loggo.INFO)
801- // Configure passed a null pointer does nothing.
802- loggo.ConfigureLogging("")
803- expected := `
804-<root> DEBUG
805-test.module INFO
806-`
807- c.Assert(loggo.DumpLoggingLevels(), Equals, strings.TrimSpace(expected))
808-}
809-
810-func (*loggerSuite) TestConfigureLoggingRoot(c *C) {
811- loggo.ConfigureLogging("<root>=debug")
812- c.Assert(loggo.DumpLoggingLevels(), Equals, "<root> DEBUG")
813-}
814-
815-func (*loggerSuite) TestConfigureLoggingSingleModule(c *C) {
816- loggo.ConfigureLogging("test.module=debug")
817- expected := `
818-<root> WARNING
819-test.module DEBUG
820-`
821- c.Assert(loggo.DumpLoggingLevels(), Equals, strings.TrimSpace(expected))
822-}
823-
824-func (*loggerSuite) TestConfigureLoggingMultipleModules(c *C) {
825- loggo.ConfigureLogging("module=info;sub.module=debug:other.module=warning")
826- expected := `
827-<root> WARNING
828-module INFO
829-other.module WARNING
830-sub.module DEBUG
831-`
832- c.Assert(loggo.DumpLoggingLevels(), Equals, strings.TrimSpace(expected))
833+ for level, str := range levelStringValueTests {
834+ c.Assert(level.String(), Equals, str)
835+ }
836+}
837+
838+var configureLoggersTests = []struct {
839+ spec string
840+ info string
841+ err string
842+}{{
843+ spec: "",
844+ info: "<root>=WARNING",
845+}, {
846+ spec: "<root>=UNSPECIFIED",
847+ info: "<root>=WARNING",
848+}, {
849+ spec: "<root>=DEBUG",
850+ info: "<root>=DEBUG",
851+}, {
852+ spec: "test.module=debug",
853+ info: "<root>=WARNING;test.module=DEBUG",
854+}, {
855+ spec: "module=info; sub.module=debug; other.module=warning",
856+ info: "<root>=WARNING;module=INFO;other.module=WARNING;sub.module=DEBUG",
857+}, {
858+ spec: " foo.bar \t\r\n= \t\r\nCRITICAL \t\r\n; \t\r\nfoo \r\t\n = DEBUG",
859+ info: "<root>=WARNING;foo=DEBUG;foo.bar=CRITICAL",
860+}, {
861+ spec: "foo;bar",
862+ info: "<root>=WARNING",
863+ err: `logger specification expected '=', found "foo"`,
864+}, {
865+ spec: "=foo",
866+ info: "<root>=WARNING",
867+ err: `logger specification "=foo" has blank name or level`,
868+}, {
869+ spec: "foo=",
870+ info: "<root>=WARNING",
871+ err: `logger specification "foo=" has blank name or level`,
872+}, {
873+ spec: "=",
874+ info: "<root>=WARNING",
875+ err: `logger specification "=" has blank name or level`,
876+}, {
877+ spec: "foo=unknown",
878+ info: "<root>=WARNING",
879+ err: `unknown severity level "unknown"`,
880+}, {
881+ // Test that nothing is changed even when the
882+ // first part of the specification parses ok.
883+ spec: "module=info; foo=unknown",
884+ info: "<root>=WARNING",
885+ err: `unknown severity level "unknown"`,
886+}}
887+
888+func (*loggerSuite) TestConfigureLoggers(c *C) {
889+ for i, test := range configureLoggersTests {
890+ c.Logf("test %d: %q", i, test.spec)
891+ loggo.ResetLoggers()
892+ err := loggo.ConfigureLoggers(test.spec)
893+ c.Check(loggo.LoggerInfo(), Equals, test.info)
894+ if test.err != "" {
895+ c.Assert(err, ErrorMatches, test.err)
896+ continue
897+ }
898+ c.Assert(err, IsNil)
899+
900+ // Test that it's idempotent.
901+ err = loggo.ConfigureLoggers(test.spec)
902+ c.Assert(err, IsNil)
903+ c.Assert(loggo.LoggerInfo(), Equals, test.info)
904+
905+ // Test that calling ConfigureLoggers with the
906+ // output of LoggerInfo works too.
907+ err = loggo.ConfigureLoggers(test.info)
908+ c.Assert(err, IsNil)
909+ c.Assert(loggo.LoggerInfo(), Equals, test.info)
910+ }
911 }
912
913 type logwriterSuite struct {
914@@ -236,7 +320,7 @@
915 var _ = Suite(&logwriterSuite{})
916
917 func (s *logwriterSuite) SetUpTest(c *C) {
918- loggo.ResetLogging()
919+ loggo.ResetLoggers()
920 loggo.RemoveWriter("default")
921 s.writer = &loggo.TestWriter{}
922 err := loggo.RegisterWriter("test", s.writer, loggo.TRACE)
923@@ -251,27 +335,28 @@
924 }
925
926 func (s *logwriterSuite) TestLogDoesntLogWeirdLevels(c *C) {
927- s.logger.Log(loggo.NOT_SPECIFIED, "message")
928- c.Assert(s.writer.Log, HasLen, 0)
929-
930- s.logger.Log(loggo.Level(42), "message")
931- c.Assert(s.writer.Log, HasLen, 0)
932-
933- s.logger.Log(loggo.CRITICAL+loggo.Level(1), "message")
934+ s.logger.Logf(loggo.UNSPECIFIED, "message")
935+ c.Assert(s.writer.Log, HasLen, 0)
936+
937+ s.logger.Logf(loggo.Level(42), "message")
938+ c.Assert(s.writer.Log, HasLen, 0)
939+
940+ s.logger.Logf(loggo.CRITICAL+loggo.Level(1), "message")
941 c.Assert(s.writer.Log, HasLen, 0)
942 }
943
944 func (s *logwriterSuite) TestMessageFormatting(c *C) {
945- s.logger.Log(loggo.INFO, "some % included")
946+ s.logger.Logf(loggo.INFO, "some %s included", "formatting")
947 c.Assert(s.writer.Log, HasLen, 1)
948- c.Assert(s.writer.Log[0].Message, Equals, "some % included")
949+ c.Assert(s.writer.Log[0].Message, Equals, "some formatting included")
950+ c.Assert(s.writer.Log[0].Level, Equals, loggo.INFO)
951 }
952
953 func (s *logwriterSuite) BenchmarkLoggingNoWriters(c *C) {
954 // No writers
955 loggo.RemoveWriter("test")
956 for i := 0; i < c.N; i++ {
957- s.logger.Warning("just a simple warning for %d", i)
958+ s.logger.Warningf("just a simple warning for %d", i)
959 }
960 }
961
962@@ -279,13 +364,13 @@
963 // No writers
964 loggo.RemoveWriter("test")
965 for i := 0; i < c.N; i++ {
966- s.logger.Warning("just a simple warning")
967+ s.logger.Warningf("just a simple warning")
968 }
969 }
970
971 func (s *logwriterSuite) BenchmarkLoggingTestWriters(c *C) {
972 for i := 0; i < c.N; i++ {
973- s.logger.Warning("just a simple warning for %d", i)
974+ s.logger.Warningf("just a simple warning for %d", i)
975 }
976 c.Assert(s.writer.Log, HasLen, c.N)
977 }
978@@ -309,7 +394,7 @@
979 defer cleanup()
980 msg := "just a simple warning for %d"
981 for i := 0; i < c.N; i++ {
982- s.logger.Warning(msg, i)
983+ s.logger.Warningf(msg, i)
984 }
985 offset, err := logFile.Seek(0, os.SEEK_CUR)
986 c.Assert(err, IsNil)
987@@ -326,7 +411,7 @@
988 loggo.RegisterWriter("testfile", writer, loggo.WARNING)
989 msg := "just a simple warning for %d"
990 for i := 0; i < c.N; i++ {
991- s.logger.Debug(msg, i)
992+ s.logger.Debugf(msg, i)
993 }
994 offset, err := logFile.Seek(0, os.SEEK_CUR)
995 c.Assert(err, IsNil)
996@@ -341,7 +426,7 @@
997 s.logger.SetLogLevel(loggo.WARNING)
998 msg := "just a simple warning for %d"
999 for i := 0; i < c.N; i++ {
1000- s.logger.Debug(msg, i)
1001+ s.logger.Debugf(msg, i)
1002 }
1003 offset, err := logFile.Seek(0, os.SEEK_CUR)
1004 c.Assert(err, IsNil)
1005
1006=== modified file 'writer.go'
1007--- writer.go 2013-06-04 04:43:14 +0000
1008+++ writer.go 2013-06-06 19:14:25 +0000
1009@@ -5,12 +5,18 @@
1010 "io"
1011 "os"
1012 "sync"
1013- "sync/atomic"
1014 "time"
1015 )
1016
1017+// Writer is implemented by any recipient of log messages.
1018 type Writer interface {
1019- Write(level Level, module, filename string, line int, timestamp time.Time, message string)
1020+ // Write writes a message to the Writer with the given
1021+ // level and module name. The filename and line hold
1022+ // the file name and line number of the code that is
1023+ // generating the log message; the time stamp holds
1024+ // the time the log message was generated, and
1025+ // message holds the log message itself.
1026+ Write(level Level, name, filename string, line int, timestamp time.Time, message string)
1027 }
1028
1029 type registeredWriter struct {
1030@@ -18,17 +24,8 @@
1031 level Level
1032 }
1033
1034-type SimpleWriter struct {
1035- writer io.Writer
1036- formatter Formatter
1037-}
1038-
1039-// NewSimpleWriter creates a new simple writer with the specified log formatter.
1040-func NewSimpleWriter(writer io.Writer, formatter Formatter) Writer {
1041- return &SimpleWriter{writer, formatter}
1042-}
1043-
1044-// The registered writers is an application level singleton.
1045+// defaultName is the name of a writer that is registered
1046+// by default that writes to stderr.
1047 const defaultName = "default"
1048
1049 var (
1050@@ -56,8 +53,8 @@
1051 }
1052
1053 // ReplaceDefaultWriter is a convenience method that does the equivalent of
1054-// RemoveWriter and then RegisterWriter with the name "default". The existing
1055-// default writer is returned.
1056+// RemoveWriter and then RegisterWriter with the name "default". The previous
1057+// default writer, if any is returned.
1058 func ReplaceDefaultWriter(writer Writer) (Writer, error) {
1059 if writer == nil {
1060 return nil, fmt.Errorf("Writer cannot be nil")
1061@@ -66,7 +63,7 @@
1062 defer writerMutex.Unlock()
1063 reg, found := writers[defaultName]
1064 if !found {
1065- return nil, fmt.Errorf("There is no %q writer", defaultName)
1066+ return nil, fmt.Errorf("there is no %q writer", defaultName)
1067 }
1068 oldWriter := reg.writer
1069 reg.writer = writer
1070@@ -77,7 +74,7 @@
1071 // RegisterWriter adds the writer to the list of writers that get notified
1072 // when logging. When registering, the caller specifies the minimum logging
1073 // level that will be written, and a name for the writer. If there is already
1074-// a registered writer with that name, and error is returned.
1075+// a registered writer with that name, an error is returned.
1076 func RegisterWriter(name string, writer Writer, minLevel Level) error {
1077 if writer == nil {
1078 return fmt.Errorf("Writer cannot be nil")
1079@@ -85,7 +82,7 @@
1080 writerMutex.Lock()
1081 defer writerMutex.Unlock()
1082 if _, found := writers[name]; found {
1083- return fmt.Errorf("There is already a Writer registerd with the name %q", name)
1084+ return fmt.Errorf("there is already a Writer registered with the name %q", name)
1085 }
1086 writers[name] = &registeredWriter{writer: writer, level: minLevel}
1087 findMinLevel()
1088@@ -99,7 +96,7 @@
1089 defer writerMutex.Unlock()
1090 registered, found := writers[name]
1091 if !found {
1092- return nil, NOT_SPECIFIED, fmt.Errorf("No registered Writer called %q", name)
1093+ return nil, UNSPECIFIED, fmt.Errorf("Writer %q is not registered", name)
1094 }
1095 delete(writers, name)
1096 findMinLevel()
1097@@ -114,12 +111,14 @@
1098 minLevel = registered.level
1099 }
1100 }
1101- atomic.StoreUint32((*uint32)(&globalMinLevel), uint32(minLevel))
1102+ globalMinLevel.set(minLevel)
1103 }
1104
1105-// Are there any writers that are interested in this log level?
1106+// WillWrite returns whether there are any writers registered
1107+// at or above the given severity level. If it returns
1108+// false, a log message at the given level will be discarded.
1109 func WillWrite(level Level) bool {
1110- return level >= Level(atomic.LoadUint32((*uint32)(&globalMinLevel)))
1111+ return level >= globalMinLevel.get()
1112 }
1113
1114 func writeToWriters(level Level, module, filename string, line int, timestamp time.Time, message string) {
1115@@ -132,7 +131,19 @@
1116 }
1117 }
1118
1119-func (simple *SimpleWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) {
1120+type simpleWriter struct {
1121+ writer io.Writer
1122+ formatter Formatter
1123+}
1124+
1125+// NewSimpleWriter returns a new writer that writes
1126+// log messages to the given io.Writer formatting the
1127+// messages with the given formatter.
1128+func NewSimpleWriter(writer io.Writer, formatter Formatter) Writer {
1129+ return &simpleWriter{writer, formatter}
1130+}
1131+
1132+func (simple *simpleWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) {
1133 logLine := simple.formatter.Format(level, module, filename, line, timestamp, message)
1134 fmt.Fprintln(simple.writer, logLine)
1135 }
1136
1137=== modified file 'writer_test.go'
1138--- writer_test.go 2013-06-02 10:40:23 +0000
1139+++ writer_test.go 2013-06-06 19:14:25 +0000
1140@@ -24,14 +24,14 @@
1141
1142 // Trying again fails.
1143 defaultWriter, level, err = loggo.RemoveWriter("default")
1144- c.Assert(err, ErrorMatches, `No registered Writer called "default"`)
1145- c.Assert(level, Equals, loggo.NOT_SPECIFIED)
1146+ c.Assert(err, ErrorMatches, `Writer "default" is not registered`)
1147+ c.Assert(level, Equals, loggo.UNSPECIFIED)
1148 c.Assert(defaultWriter, IsNil)
1149 }
1150
1151 func (*writerBasicsSuite) TestRegisterWriterExistingName(c *C) {
1152 err := loggo.RegisterWriter("default", &loggo.TestWriter{}, loggo.INFO)
1153- c.Assert(err, ErrorMatches, `There is already a Writer registerd with the name "default"`)
1154+ c.Assert(err, ErrorMatches, `there is already a Writer registered with the name "default"`)
1155 }
1156
1157 func (*writerBasicsSuite) TestRegisterNilWriter(c *C) {
1158@@ -62,7 +62,7 @@
1159 loggo.RemoveWriter("default")
1160 oldWriter, err := loggo.ReplaceDefaultWriter(&loggo.TestWriter{})
1161 c.Assert(oldWriter, IsNil)
1162- c.Assert(err, ErrorMatches, `There is no "default" writer`)
1163+ c.Assert(err, ErrorMatches, `there is no "default" writer`)
1164 }
1165
1166 func (s *writerBasicsSuite) TestWillWrite(c *C) {
1167@@ -89,7 +89,7 @@
1168 var _ = Suite(&writerSuite{})
1169
1170 func (s *writerSuite) SetUpTest(c *C) {
1171- loggo.ResetLogging()
1172+ loggo.ResetLoggers()
1173 loggo.RemoveWriter("default")
1174 s.logger = loggo.GetLogger("test.writer")
1175 // Make it so the logger itself writes all messages.
1176@@ -101,7 +101,7 @@
1177 }
1178
1179 func (s *writerSuite) TearDownSuite(c *C) {
1180- loggo.ResetLogging()
1181+ loggo.ResetLoggers()
1182 }
1183
1184 func (s *writerSuite) TestWritingCapturesFileAndLineAndModule(c *C) {
1185@@ -109,7 +109,7 @@
1186 err := loggo.RegisterWriter("test", writer, loggo.INFO)
1187 c.Assert(err, IsNil)
1188
1189- s.logger.Info("Info message")
1190+ s.logger.Infof("Info message")
1191
1192 // WARNING: test checks the line number of the above logger lines, this
1193 // will mean that if the above line moves, the test will fail unless
1194@@ -126,11 +126,11 @@
1195 c.Assert(err, IsNil)
1196
1197 start := time.Now()
1198- s.logger.Critical("Something critical.")
1199- s.logger.Error("An error.")
1200- s.logger.Warning("A warning message")
1201- s.logger.Info("Info message")
1202- s.logger.Trace("Trace the function")
1203+ s.logger.Criticalf("Something critical.")
1204+ s.logger.Errorf("An error.")
1205+ s.logger.Warningf("A warning message")
1206+ s.logger.Infof("Info message")
1207+ s.logger.Tracef("Trace the function")
1208 end := time.Now()
1209
1210 c.Assert(writer.Log, HasLen, 3)
1211@@ -153,11 +153,11 @@
1212 c.Assert(err, IsNil)
1213
1214 start := time.Now()
1215- s.logger.Critical("Something critical.")
1216- s.logger.Error("An error.")
1217- s.logger.Warning("A warning message")
1218- s.logger.Info("Info message")
1219- s.logger.Trace("Trace the function")
1220+ s.logger.Criticalf("Something critical.")
1221+ s.logger.Errorf("An error.")
1222+ s.logger.Warningf("A warning message")
1223+ s.logger.Infof("Info message")
1224+ s.logger.Tracef("Trace the function")
1225 end := time.Now()
1226
1227 c.Assert(writer.Log, HasLen, 5)
1228@@ -196,10 +196,10 @@
1229 err = loggo.RegisterWriter("trace", traceWriter, loggo.TRACE)
1230 c.Assert(err, IsNil)
1231
1232- s.logger.Error("An error.")
1233- s.logger.Warning("A warning message")
1234- s.logger.Info("Info message")
1235- s.logger.Trace("Trace the function")
1236+ s.logger.Errorf("An error.")
1237+ s.logger.Warningf("A warning message")
1238+ s.logger.Infof("Info message")
1239+ s.logger.Tracef("Trace the function")
1240
1241 c.Assert(errorWriter.Log, HasLen, 1)
1242 c.Assert(warningWriter.Log, HasLen, 2)

Subscribers

People subscribed via source and target branches

to all changes: