]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | // Package logutils augments the standard log package with levels. |
2 | package logutils | |
3 | ||
4 | import ( | |
5 | "bytes" | |
6 | "io" | |
7 | "sync" | |
8 | ) | |
9 | ||
10 | type LogLevel string | |
11 | ||
12 | // LevelFilter is an io.Writer that can be used with a logger that | |
13 | // will filter out log messages that aren't at least a certain level. | |
14 | // | |
15 | // Once the filter is in use somewhere, it is not safe to modify | |
16 | // the structure. | |
17 | type LevelFilter struct { | |
18 | // Levels is the list of log levels, in increasing order of | |
19 | // severity. Example might be: {"DEBUG", "WARN", "ERROR"}. | |
20 | Levels []LogLevel | |
21 | ||
22 | // MinLevel is the minimum level allowed through | |
23 | MinLevel LogLevel | |
24 | ||
25 | // The underlying io.Writer where log messages that pass the filter | |
26 | // will be set. | |
27 | Writer io.Writer | |
28 | ||
29 | badLevels map[LogLevel]struct{} | |
30 | once sync.Once | |
31 | } | |
32 | ||
33 | // Check will check a given line if it would be included in the level | |
34 | // filter. | |
35 | func (f *LevelFilter) Check(line []byte) bool { | |
36 | f.once.Do(f.init) | |
37 | ||
38 | // Check for a log level | |
39 | var level LogLevel | |
40 | x := bytes.IndexByte(line, '[') | |
41 | if x >= 0 { | |
42 | y := bytes.IndexByte(line[x:], ']') | |
43 | if y >= 0 { | |
44 | level = LogLevel(line[x+1 : x+y]) | |
45 | } | |
46 | } | |
47 | ||
48 | _, ok := f.badLevels[level] | |
49 | return !ok | |
50 | } | |
51 | ||
52 | func (f *LevelFilter) Write(p []byte) (n int, err error) { | |
53 | // Note in general that io.Writer can receive any byte sequence | |
54 | // to write, but the "log" package always guarantees that we only | |
55 | // get a single line. We use that as a slight optimization within | |
56 | // this method, assuming we're dealing with a single, complete line | |
57 | // of log data. | |
58 | ||
59 | if !f.Check(p) { | |
60 | return len(p), nil | |
61 | } | |
62 | ||
63 | return f.Writer.Write(p) | |
64 | } | |
65 | ||
66 | // SetMinLevel is used to update the minimum log level | |
67 | func (f *LevelFilter) SetMinLevel(min LogLevel) { | |
68 | f.MinLevel = min | |
69 | f.init() | |
70 | } | |
71 | ||
72 | func (f *LevelFilter) init() { | |
73 | badLevels := make(map[LogLevel]struct{}) | |
74 | for _, level := range f.Levels { | |
75 | if level == f.MinLevel { | |
76 | break | |
77 | } | |
78 | badLevels[level] = struct{}{} | |
79 | } | |
80 | f.badLevels = badLevels | |
81 | } |