Skip to content

Commit 1e30d9d

Browse files
wlahtiyacovm
authored andcommitted
Add methods for setting log levels via regexp
This change adds the methods to allow log levels to be set dynamically using regular expressions instead of supplying one module name at a time. Future CRs will 1) store the module map at the end at the end of peer startup and add a method to revert all modules to those levels, and 2) update all peer code to use flogging.MustGetLogger instead of logging.MustGetLogger to enable these new features. https://jira.hyperledger.org/browse/FAB-2351 Change-Id: I173ee502c4f3666447212fb3c36e06f7ee88a8f1 Signed-off-by: Will Lahti <[email protected]>
1 parent 51b7e85 commit 1e30d9d

File tree

2 files changed

+205
-17
lines changed

2 files changed

+205
-17
lines changed

common/flogging/logging.go

+62-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package flogging
1919
import (
2020
"io"
2121
"os"
22+
"regexp"
2223
"strings"
24+
"sync"
2325

2426
"github.com/op/go-logging"
2527
"github.com/spf13/viper"
@@ -35,7 +37,22 @@ var defaultOutput = os.Stderr
3537
// case of configuration errors.
3638
var fallbackDefaultLevel = logging.INFO
3739

38-
// LoggingInitFromViper is a 'hook' called at the beginning of command processing to
40+
var lock = sync.Mutex{}
41+
42+
// modules holds the map of all modules and the current log level
43+
var modules map[string]string
44+
45+
// IsSetLevelByRegExpEnabled disables the code that will allow setting log
46+
// levels using a regular expression instead of one module/submodule at a time.
47+
// TODO - remove once peer code has switched over to using
48+
// flogging.MustGetLogger in place of logging.MustGetLogger
49+
var IsSetLevelByRegExpEnabled = false
50+
51+
func init() {
52+
modules = make(map[string]string)
53+
}
54+
55+
// InitFromViper is a 'hook' called at the beginning of command processing to
3956
// parse logging-related options specified either on the command-line or in
4057
// config files. Command-line options take precedence over config file
4158
// options, and can also be passed as suitably-named environment variables. To
@@ -53,7 +70,7 @@ func InitFromViper(command string) {
5370
logger.Debugf("Setting default logging level to %s for command '%s'", defaultLevel, command)
5471
}
5572

56-
// LoggingInit initializes the logging based on the supplied spec, it is exposed externally
73+
// InitFromSpec initializes the logging based on the supplied spec, it is exposed externally
5774
// so that consumers of the flogging package who do not wish to use the default config structure
5875
// may parse their own logging specification
5976
func InitFromSpec(spec string) logging.Level {
@@ -136,20 +153,57 @@ func GetModuleLevel(module string) (string, error) {
136153
return level, nil
137154
}
138155

139-
// SetModuleLevel sets the logging level for the specified module. This is
140-
// currently only called from admin.go but can be called from anywhere in the
141-
// code on a running peer to dynamically change the log level for the module.
142-
func SetModuleLevel(module string, logLevel string) (string, error) {
156+
// SetModuleLevel sets the logging level for the modules that match the
157+
// supplied regular expression. This is can be called from anywhere in the
158+
// code on a running peer to dynamically change log levels.
159+
func SetModuleLevel(moduleRegExp string, logLevel string) (string, error) {
143160
level, err := logging.LogLevel(logLevel)
144161

145162
if err != nil {
146163
logger.Warningf("Invalid logging level: %s - ignored", logLevel)
147164
} else {
148-
logging.SetLevel(logging.Level(level), module)
149-
logger.Debugf("Module '%s' logger enabled for log level: %s", module, level)
165+
// TODO - this check is in here temporarily until all modules have been
166+
// converted to using flogging.MustGetLogger. until that point, this flag
167+
// keeps the previous behavior in place.
168+
if !IsSetLevelByRegExpEnabled {
169+
logging.SetLevel(logging.Level(level), moduleRegExp)
170+
logger.Infof("Module '%s' logger enabled for log level: %s", moduleRegExp, level)
171+
} else {
172+
re, err := regexp.Compile(moduleRegExp)
173+
if err != nil {
174+
logger.Warningf("Invalid regular expression for module: %s", moduleRegExp)
175+
return "", err
176+
}
177+
178+
lock.Lock()
179+
defer lock.Unlock()
180+
for module := range modules {
181+
if re.MatchString(module) {
182+
logging.SetLevel(logging.Level(level), module)
183+
modules[module] = logLevel
184+
logger.Infof("Module '%s' logger enabled for log level: %s", module, level)
185+
}
186+
}
187+
}
150188
}
151189

152190
logLevelString := level.String()
153191

154192
return logLevelString, err
155193
}
194+
195+
// MustGetLogger is used in place of logging.MustGetLogger to allow us to store
196+
// a map of all modules and submodules that have loggers in the system
197+
func MustGetLogger(module string) *logging.Logger {
198+
logger := logging.MustGetLogger(module)
199+
200+
// retrieve the current log level for the logger
201+
level := logging.GetLevel(module).String()
202+
203+
// store the module's name as well as the current log level for the logger
204+
lock.Lock()
205+
defer lock.Unlock()
206+
modules[module] = level
207+
208+
return logger
209+
}

common/flogging/logging_test.go

+143-9
Original file line numberDiff line numberDiff line change
@@ -148,29 +148,24 @@ func TestGetModuleLevelDefault(t *testing.T) {
148148
level, _ := flogging.GetModuleLevel("peer")
149149

150150
// peer should be using the default log level at this point
151-
if level != "INFO" {
152-
t.FailNow()
153-
}
151+
assertEquals(t, logging.INFO.String(), level)
152+
154153
}
155154

156155
func TestGetModuleLevelDebug(t *testing.T) {
157156
flogging.SetModuleLevel("peer", "DEBUG")
158157
level, _ := flogging.GetModuleLevel("peer")
159158

160159
// ensure that the log level has changed to debug
161-
if level != "DEBUG" {
162-
t.FailNow()
163-
}
160+
assertEquals(t, logging.DEBUG.String(), level)
164161
}
165162

166163
func TestGetModuleLevelInvalid(t *testing.T) {
167164
flogging.SetModuleLevel("peer", "invalid")
168165
level, _ := flogging.GetModuleLevel("peer")
169166

170167
// ensure that the log level didn't change after invalid log level specified
171-
if level != "DEBUG" {
172-
t.FailNow()
173-
}
168+
assertEquals(t, logging.DEBUG.String(), level)
174169
}
175170

176171
func TestSetModuleLevel(t *testing.T) {
@@ -217,6 +212,145 @@ func ExampleSetLoggingFormat_second() {
217212
// 00:00:00.000 [floggingTest] ExampleSetLoggingFormat_second -> INFO 001 test
218213
}
219214

215+
// TestSetModuleLevel_moduleWithSubmodule and the following tests use the new
216+
// flogging.MustGetLogger to initialize their loggers. flogging.MustGetLogger
217+
// adds a modules map to track which module names have been defined and their
218+
// current log level
219+
func TestSetModuleLevel_moduleWithSubmodule(t *testing.T) {
220+
// enable setting the level by regular expressions
221+
// TODO - remove this once all modules have been updated to use
222+
// flogging.MustGetLogger
223+
flogging.IsSetLevelByRegExpEnabled = true
224+
225+
flogging.MustGetLogger("module")
226+
flogging.MustGetLogger("module/subcomponent")
227+
flogging.MustGetLogger("sub")
228+
229+
// ensure that the log level is the default level, INFO
230+
assertModuleLevel(t, "module", logging.INFO)
231+
assertModuleLevel(t, "module/subcomponent", logging.INFO)
232+
assertModuleLevel(t, "sub", logging.INFO)
233+
234+
// set level for modules that contain 'module'
235+
flogging.SetModuleLevel("module", "warning")
236+
237+
// ensure that the log level has changed to WARNING for the modules
238+
// that match the regular expression
239+
assertModuleLevel(t, "module", logging.WARNING)
240+
assertModuleLevel(t, "module/subcomponent", logging.WARNING)
241+
assertModuleLevel(t, "sub", logging.INFO)
242+
243+
flogging.IsSetLevelByRegExpEnabled = false
244+
}
245+
246+
func TestSetModuleLevel_regExpOr(t *testing.T) {
247+
flogging.IsSetLevelByRegExpEnabled = true
248+
249+
flogging.MustGetLogger("module2")
250+
flogging.MustGetLogger("module2/subcomponent")
251+
flogging.MustGetLogger("sub2")
252+
flogging.MustGetLogger("randomLogger2")
253+
254+
// ensure that the log level is the default level, INFO
255+
assertModuleLevel(t, "module2", logging.INFO)
256+
assertModuleLevel(t, "module2/subcomponent", logging.INFO)
257+
assertModuleLevel(t, "sub2", logging.INFO)
258+
assertModuleLevel(t, "randomLogger2", logging.INFO)
259+
260+
// set level for modules that contain 'mod' OR 'sub'
261+
flogging.SetModuleLevel("mod|sub", "DEBUG")
262+
263+
// ensure that the log level has changed to DEBUG for the modules that match
264+
// the regular expression
265+
assertModuleLevel(t, "module2", logging.DEBUG)
266+
assertModuleLevel(t, "module2/subcomponent", logging.DEBUG)
267+
assertModuleLevel(t, "sub2", logging.DEBUG)
268+
assertModuleLevel(t, "randomLogger2", logging.INFO)
269+
270+
flogging.IsSetLevelByRegExpEnabled = false
271+
}
272+
273+
func TestSetModuleLevel_regExpSuffix(t *testing.T) {
274+
flogging.IsSetLevelByRegExpEnabled = true
275+
276+
flogging.MustGetLogger("module3")
277+
flogging.MustGetLogger("module3/subcomponent")
278+
flogging.MustGetLogger("sub3")
279+
flogging.MustGetLogger("sub3/subcomponent")
280+
flogging.MustGetLogger("randomLogger3")
281+
282+
// ensure that the log level is the default level, INFO
283+
assertModuleLevel(t, "module3", logging.INFO)
284+
assertModuleLevel(t, "module3/subcomponent", logging.INFO)
285+
assertModuleLevel(t, "sub3", logging.INFO)
286+
assertModuleLevel(t, "sub3/subcomponent", logging.INFO)
287+
assertModuleLevel(t, "randomLogger3", logging.INFO)
288+
289+
// set level for modules that contain component
290+
flogging.SetModuleLevel("component$", "ERROR")
291+
292+
// ensure that the log level has changed to ERROR for the modules that match
293+
// the regular expression
294+
assertModuleLevel(t, "module3", logging.INFO)
295+
assertModuleLevel(t, "module3/subcomponent", logging.ERROR)
296+
assertModuleLevel(t, "sub3", logging.INFO)
297+
assertModuleLevel(t, "sub3/subcomponent", logging.ERROR)
298+
assertModuleLevel(t, "randomLogger3", logging.INFO)
299+
300+
flogging.IsSetLevelByRegExpEnabled = false
301+
}
302+
303+
func TestSetModuleLevel_regExpComplex(t *testing.T) {
304+
flogging.IsSetLevelByRegExpEnabled = true
305+
306+
flogging.MustGetLogger("gossip/util")
307+
flogging.MustGetLogger("orderer/util")
308+
flogging.MustGetLogger("gossip/gossip#0.0.0.0:7051")
309+
flogging.MustGetLogger("gossip/conn#-1")
310+
flogging.MustGetLogger("orderer/conn#0.0.0.0:7051")
311+
312+
// ensure that the log level is the default level, INFO
313+
assertModuleLevel(t, "gossip/util", logging.INFO)
314+
assertModuleLevel(t, "orderer/util", logging.INFO)
315+
assertModuleLevel(t, "gossip/gossip#0.0.0.0:7051", logging.INFO)
316+
assertModuleLevel(t, "gossip/conn#-1", logging.INFO)
317+
assertModuleLevel(t, "orderer/conn#0.0.0.0:7051", logging.INFO)
318+
319+
// set level for modules that match the regular rexpression
320+
flogging.SetModuleLevel("^[a-z]+\\/[a-z]+#.+$", "WARNING")
321+
322+
// ensure that the log level has changed to WARNING for the modules that match
323+
// the regular expression
324+
assertModuleLevel(t, "gossip/util", logging.INFO)
325+
assertModuleLevel(t, "orderer/util", logging.INFO)
326+
assertModuleLevel(t, "gossip/gossip#0.0.0.0:7051", logging.WARNING)
327+
assertModuleLevel(t, "gossip/conn#-1", logging.WARNING)
328+
assertModuleLevel(t, "orderer/conn#0.0.0.0:7051", logging.WARNING)
329+
330+
flogging.IsSetLevelByRegExpEnabled = false
331+
}
332+
333+
func TestSetModuleLevel_invalidRegExp(t *testing.T) {
334+
flogging.IsSetLevelByRegExpEnabled = true
335+
336+
flogging.MustGetLogger("(module")
337+
338+
// ensure that the log level is the default level, INFO
339+
assertModuleLevel(t, "(module", logging.INFO)
340+
341+
level, err := flogging.SetModuleLevel("(", "info")
342+
343+
assertEquals(t, "", level)
344+
if err == nil {
345+
t.FailNow()
346+
}
347+
348+
// ensure that the log level hasn't changed
349+
assertModuleLevel(t, "(module", logging.INFO)
350+
351+
flogging.IsSetLevelByRegExpEnabled = false
352+
}
353+
220354
func assertDefaultLevel(t *testing.T, expectedLevel logging.Level) {
221355
assertModuleLevel(t, "", expectedLevel)
222356
}

0 commit comments

Comments
 (0)