-
Notifications
You must be signed in to change notification settings - Fork 8.9k
/
logging.go
233 lines (201 loc) · 6.81 KB
/
logging.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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flogging
import (
"io"
"os"
"regexp"
"strings"
"sync"
"github.com/op/go-logging"
)
const (
pkgLogID = "flogging"
defaultFormat = "%{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} -> %{level:.4s} %{id:03x}%{color:reset} %{message}"
defaultLevel = logging.INFO
)
var (
logger *logging.Logger
defaultOutput *os.File
modules map[string]string // Holds the map of all modules and their respective log level
peerStartModules map[string]string
lock sync.RWMutex
once sync.Once
)
func init() {
logger = logging.MustGetLogger(pkgLogID)
Reset()
initgrpclogger()
}
// Reset sets to logging to the defaults defined in this package.
func Reset() {
modules = make(map[string]string)
lock = sync.RWMutex{}
defaultOutput = os.Stderr
InitBackend(SetFormat(defaultFormat), defaultOutput)
InitFromSpec("")
}
// SetFormat sets the logging format.
func SetFormat(formatSpec string) logging.Formatter {
if formatSpec == "" {
formatSpec = defaultFormat
}
return logging.MustStringFormatter(formatSpec)
}
// InitBackend sets up the logging backend based on
// the provided logging formatter and I/O writer.
func InitBackend(formatter logging.Formatter, output io.Writer) {
backend := logging.NewLogBackend(output, "", 0)
backendFormatter := logging.NewBackendFormatter(backend, formatter)
logging.SetBackend(backendFormatter).SetLevel(defaultLevel, "")
}
// DefaultLevel returns the fallback value for loggers to use if parsing fails.
func DefaultLevel() string {
return defaultLevel.String()
}
// GetModuleLevel gets the current logging level for the specified module.
func GetModuleLevel(module string) string {
// logging.GetLevel() returns the logging level for the module, if defined.
// Otherwise, it returns the default logging level, as set by
// `flogging/logging.go`.
level := logging.GetLevel(module).String()
return level
}
// SetModuleLevel sets the logging level for the modules that match the supplied
// regular expression. Can be used to dynamically change the log level for the
// module.
func SetModuleLevel(moduleRegExp string, level string) (string, error) {
return setModuleLevel(moduleRegExp, level, true, false)
}
func setModuleLevel(moduleRegExp string, level string, isRegExp bool, revert bool) (string, error) {
var re *regexp.Regexp
logLevel, err := logging.LogLevel(level)
if err != nil {
logger.Warningf("Invalid logging level '%s' - ignored", level)
} else {
if !isRegExp || revert {
logging.SetLevel(logLevel, moduleRegExp)
logger.Debugf("Module '%s' logger enabled for log level '%s'", moduleRegExp, level)
} else {
re, err = regexp.Compile(moduleRegExp)
if err != nil {
logger.Warningf("Invalid regular expression: %s", moduleRegExp)
return "", err
}
lock.Lock()
defer lock.Unlock()
for module := range modules {
if re.MatchString(module) {
logging.SetLevel(logging.Level(logLevel), module)
modules[module] = logLevel.String()
logger.Debugf("Module '%s' logger enabled for log level '%s'", module, logLevel)
}
}
}
}
return logLevel.String(), err
}
// MustGetLogger is used in place of `logging.MustGetLogger` to allow us to
// store a map of all modules and submodules that have loggers in the system.
func MustGetLogger(module string) *logging.Logger {
l := logging.MustGetLogger(module)
lock.Lock()
defer lock.Unlock()
modules[module] = GetModuleLevel(module)
return l
}
// InitFromSpec initializes the logging based on the supplied spec. It is
// exposed externally so that consumers of the flogging package may parse their
// own logging specification. The logging specification has the following form:
// [<module>[,<module>...]=]<level>[:[<module>[,<module>...]=]<level>...]
func InitFromSpec(spec string) string {
levelAll := defaultLevel
var err error
if spec != "" {
fields := strings.Split(spec, ":")
for _, field := range fields {
split := strings.Split(field, "=")
switch len(split) {
case 1:
if levelAll, err = logging.LogLevel(field); err != nil {
logger.Warningf("Logging level '%s' not recognized, defaulting to '%s': %s", field, defaultLevel, err)
levelAll = defaultLevel // need to reset cause original value was overwritten
}
case 2:
// <module>[,<module>...]=<level>
levelSingle, err := logging.LogLevel(split[1])
if err != nil {
logger.Warningf("Invalid logging level in '%s' ignored", field)
continue
}
if split[0] == "" {
logger.Warningf("Invalid logging override specification '%s' ignored - no module specified", field)
} else {
modules := strings.Split(split[0], ",")
for _, module := range modules {
logger.Debugf("Setting logging level for module '%s' to '%s'", module, levelSingle)
logging.SetLevel(levelSingle, module)
}
}
default:
logger.Warningf("Invalid logging override '%s' ignored - missing ':'?", field)
}
}
}
logging.SetLevel(levelAll, "") // set the logging level for all modules
// iterate through modules to reload their level in the modules map based on
// the new default level
for k := range modules {
MustGetLogger(k)
}
// register flogging logger in the modules map
MustGetLogger(pkgLogID)
return levelAll.String()
}
// SetPeerStartupModulesMap saves the modules and their log levels.
// this function should only be called at the end of peer startup.
func SetPeerStartupModulesMap() {
lock.Lock()
defer lock.Unlock()
once.Do(func() {
peerStartModules = make(map[string]string)
for k, v := range modules {
peerStartModules[k] = v
}
})
}
// GetPeerStartupLevel returns the peer startup level for the specified module.
// It will return an empty string if the input parameter is empty or the module
// is not found
func GetPeerStartupLevel(module string) string {
if module != "" {
if level, ok := peerStartModules[module]; ok {
return level
}
}
return ""
}
// RevertToPeerStartupLevels reverts the log levels for all modules to the level
// defined at the end of peer startup.
func RevertToPeerStartupLevels() error {
lock.RLock()
defer lock.RUnlock()
for key := range peerStartModules {
_, err := setModuleLevel(key, peerStartModules[key], false, true)
if err != nil {
return err
}
}
logger.Info("Log levels reverted to the levels defined at the end of peer startup")
return nil
}