Merge lp:~rogpeppe/loggo/minor-fixes into lp:loggo
- minor-fixes
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Penhey | Pending | ||
Review via email: mp+167832@code.launchpad.net |
Commit message
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 ReplaceDefaultW
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!
Roger Peppe (rogpeppe) wrote : | # |
Preview Diff
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] = ®isteredWriter{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) |
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 ReplaceDefaultW riter 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