-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloggerlevels.go
174 lines (147 loc) · 4.78 KB
/
loggerlevels.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package flogging
import (
"fmt"
"regexp"
"sort"
"strings"
"sync"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
)
// LoggerLevels tracks the logging level of named loggers.
type LoggerLevels struct {
mutex sync.RWMutex
levelCache map[string]zapcore.Level
specs map[string]zapcore.Level
defaultLevel zapcore.Level
minLevel zapcore.Level
}
// DefaultLevel returns the default logging level for loggers that do not have
// an explicit level set.
func (l *LoggerLevels) DefaultLevel() zapcore.Level {
l.mutex.RLock()
lvl := l.defaultLevel
l.mutex.RUnlock()
return lvl
}
// ActivateSpec is used to modify logging levels.
//
// The logging specification has the following form:
// [<logger>[,<logger>...]=]<level>[:[<logger>[,<logger>...]=]<level>...]
func (l *LoggerLevels) ActivateSpec(spec string) error {
l.mutex.Lock()
defer l.mutex.Unlock()
defaultLevel := zapcore.InfoLevel
specs := map[string]zapcore.Level{}
for _, field := range strings.Split(spec, ":") {
split := strings.Split(field, "=")
switch len(split) {
case 1: // level
if field != "" && !IsValidLevel(field) {
return errors.Errorf("invalid logging specification '%s': bad segment '%s'", spec, field)
}
defaultLevel = NameToLevel(field)
case 2: // <logger>[,<logger>...]=<level>
if split[0] == "" {
return errors.Errorf("invalid logging specification '%s': no logger specified in segment '%s'", spec, field)
}
if field != "" && !IsValidLevel(split[1]) {
return errors.Errorf("invalid logging specification '%s': bad segment '%s'", spec, field)
}
level := NameToLevel(split[1])
loggers := strings.Split(split[0], ",")
for _, logger := range loggers {
// check if the logger name in the spec is valid. The
// trailing period is trimmed as logger names in specs
// ending with a period signifies that this part of the
// spec refers to the exact logger name (i.e. is not a prefix)
if !isValidLoggerName(strings.TrimSuffix(logger, ".")) {
return errors.Errorf("invalid logging specification '%s': bad logger name '%s'", spec, logger)
}
specs[logger] = level
}
default:
return errors.Errorf("invalid logging specification '%s': bad segment '%s'", spec, field)
}
}
minLevel := defaultLevel
for _, lvl := range specs {
if lvl < minLevel {
minLevel = lvl
}
}
l.minLevel = minLevel
l.defaultLevel = defaultLevel
l.specs = specs
l.levelCache = map[string]zapcore.Level{}
return nil
}
// logggerNameRegexp defines the valid logger names
var loggerNameRegexp = regexp.MustCompile(`^[[:alnum:]_#:-]+(\.[[:alnum:]_#:-]+)*$`)
// isValidLoggerName checks whether a logger name contains only valid
// characters. Names that begin/end with periods or contain special
// characters (other than periods, underscores, pound signs, colons
// and dashes) are invalid.
func isValidLoggerName(loggerName string) bool {
return loggerNameRegexp.MatchString(loggerName)
}
// Level returns the effective logging level for a logger. If a level has not
// been explicitly set for the logger, the default logging level will be
// returned.
func (l *LoggerLevels) Level(loggerName string) zapcore.Level {
if level, ok := l.cachedLevel(loggerName); ok {
return level
}
l.mutex.Lock()
level := l.calculateLevel(loggerName)
l.levelCache[loggerName] = level
l.mutex.Unlock()
return level
}
// calculateLevel walks the logger name back to find the appropriate
// log level from the current spec.
func (l *LoggerLevels) calculateLevel(loggerName string) zapcore.Level {
candidate := loggerName + "."
for {
if lvl, ok := l.specs[candidate]; ok {
return lvl
}
idx := strings.LastIndex(candidate, ".")
if idx <= 0 {
return l.defaultLevel
}
candidate = candidate[:idx]
}
}
// cachedLevel attempts to retrieve the effective log level for a logger from the
// cache. If the logger is not found, ok will be false.
func (l *LoggerLevels) cachedLevel(loggerName string) (lvl zapcore.Level, ok bool) {
l.mutex.RLock()
level, ok := l.levelCache[loggerName]
l.mutex.RUnlock()
return level, ok
}
// Spec returns a normalized version of the active logging spec.
func (l *LoggerLevels) Spec() string {
l.mutex.RLock()
defer l.mutex.RUnlock()
var fields []string
for k, v := range l.specs {
fields = append(fields, fmt.Sprintf("%s=%s", k, v))
}
sort.Strings(fields)
fields = append(fields, l.defaultLevel.String())
return strings.Join(fields, ":")
}
// Enabled function is an enabled check that evaluates the minimum active logging level.
// It serves as a fast check before the (relatively) expensive Check call in the core.
func (l *LoggerLevels) Enabled(lvl zapcore.Level) bool {
l.mutex.RLock()
enabled := l.minLevel.Enabled(lvl)
l.mutex.RUnlock()
return enabled
}