Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config loggers by labels #50

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 42 additions & 16 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ func (c *Context) GetAllLoggerTags() []string {
names[k] = v
}
}
labels := make([]string, 0, len(names))
tags := make([]string, 0, len(names))
for name := range names {
labels = append(labels, name)
tags = append(tags, name)
}
sort.Strings(labels)
return labels
sort.Strings(tags)
return tags
}

func (c *Context) getLoggerModule(name string, tags []string) *module {
Expand Down Expand Up @@ -136,14 +136,14 @@ func (c *Context) getLoggerModule(name string, tags []string) *module {
}

// getLoggerModulesByTag returns modules that have the associated tag.
func (c *Context) getLoggerModulesByTag(label string) []*module {
func (c *Context) getLoggerModulesByTag(tag string) []*module {
var modules []*module
for _, mod := range c.modules {
if len(mod.tags) == 0 {
continue
}

if _, ok := mod.tagsLookup[label]; ok {
if _, ok := mod.tagsLookup[tag]; ok {
modules = append(modules, mod)
}
}
Expand Down Expand Up @@ -179,15 +179,22 @@ func (c *Context) CompleteConfig() Config {
}

// ApplyConfig configures the logging modules according to the provided config.
func (c *Context) ApplyConfig(config Config) {
func (c *Context) ApplyConfig(config Config, labels ...Labels) {
label := mergeLabels(labels)

c.modulesMutex.Lock()
defer c.modulesMutex.Unlock()

for name, level := range config {
tag := extractConfigTag(name)
if tag == "" {
module := c.getLoggerModule(name, nil)

// If the module doesn't have the label, then we skip it.
if !module.hasLabelIntersection(label) {
continue
}
module.setLevel(level)
continue
}

// Ensure that we save the config for lazy loggers to pick up correctly.
Expand All @@ -196,22 +203,34 @@ func (c *Context) ApplyConfig(config Config) {
// Config contains a named tag, use that for selecting the loggers.
modules := c.getLoggerModulesByTag(tag)
for _, module := range modules {
// If the module doesn't have the label, then we skip it.
if !module.hasLabelIntersection(label) {
continue
}

module.setLevel(level)
}
}
}

// ResetLoggerLevels iterates through the known logging modules and sets the
// levels of all to UNSPECIFIED, except for <root> which is set to WARNING.
func (c *Context) ResetLoggerLevels() {
// If labels are provided, then only loggers that have the provided labels
// will be reset.
func (c *Context) ResetLoggerLevels(labels ...Labels) {
label := mergeLabels(labels)

c.modulesMutex.Lock()
defer c.modulesMutex.Unlock()

// Setting the root module to UNSPECIFIED will set it to WARNING.
for _, module := range c.modules {
if !module.hasLabelIntersection(label) {
continue
}

module.setLevel(UNSPECIFIED)
}
// We can safely just wipe everything here.
c.modulesTagConfig = make(map[string]Level)
}

func (c *Context) write(entry Entry) {
Expand Down Expand Up @@ -302,19 +321,26 @@ func (c *Context) ResetWriters() {

// ConfigureLoggers configures loggers according to the given string
// specification, which specifies a set of modules and their associated
// logging levels. Loggers are colon- or semicolon-separated; each
// logging levels. Loggers are colon- or semicolon-separated; each
// module is specified as <modulename>=<level>. White space outside of
// module names and levels is ignored. The root module is specified
// module names and levels is ignored. The root module is specified
// with the name "<root>".
//
// An example specification:
//
// `<root>=ERROR; foo.bar=WARNING`
func (c *Context) ConfigureLoggers(specification string) error {
// <root>=ERROR; foo.bar=WARNING
//
// Label matching can be applied to the loggers by providing a set of labels
// to the function. If a logger has a label that matches the provided labels,
// then the logger will be configured with the provided level. If the logger
// does not have a label that matches the provided labels, then the logger
// will not be configured. No labels will configure all loggers in the
// specification.
func (c *Context) ConfigureLoggers(specification string, labels ...Labels) error {
config, err := ParseConfigString(specification)
if err != nil {
return err
}
c.ApplyConfig(config)
c.ApplyConfig(config, labels...)
return nil
}
94 changes: 90 additions & 4 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func (*ContextSuite) TestApplyConfigTags(c *gc.C) {
})
}

func (*ContextSuite) TestApplyConfigLabelsAppliesToNewLoggers(c *gc.C) {
func (*ContextSuite) TestApplyConfigTagsAppliesToNewLoggers(c *gc.C) {
context := loggo.NewContext(loggo.WARNING)

context.ApplyConfig(loggo.Config{"#one": loggo.TRACE})
Expand Down Expand Up @@ -289,7 +289,7 @@ func (*ContextSuite) TestApplyConfigLabelsAppliesToNewLoggers(c *gc.C) {
})
}

func (*ContextSuite) TestApplyConfigLabelsAppliesToNewLoggersWithMultipleTags(c *gc.C) {
func (*ContextSuite) TestApplyConfigTagsAppliesToNewLoggersWithMultipleTags(c *gc.C) {
context := loggo.NewContext(loggo.WARNING)

// Invert the order here, to ensure that the config order doesn't matter,
Expand All @@ -312,7 +312,7 @@ func (*ContextSuite) TestApplyConfigLabelsAppliesToNewLoggersWithMultipleTags(c
})
}

func (*ContextSuite) TestApplyConfigLabelsResetLoggerLevels(c *gc.C) {
func (*ContextSuite) TestApplyConfigTagsResetLoggerLevels(c *gc.C) {
context := loggo.NewContext(loggo.WARNING)

context.ApplyConfig(loggo.Config{"#one": loggo.TRACE})
Expand All @@ -339,7 +339,93 @@ func (*ContextSuite) TestApplyConfigLabelsResetLoggerLevels(c *gc.C) {
})
}

func (*ContextSuite) TestApplyConfigTagsAddative(c *gc.C) {
func (*ContextSuite) TestApplyConfigTagsResetLoggerLevelsUsingLabels(c *gc.C) {
context := loggo.NewContext(loggo.WARNING)

context.ApplyConfig(loggo.Config{"#one": loggo.TRACE})
context.ApplyConfig(loggo.Config{"#two": loggo.DEBUG})

context.GetLogger("a", "one").ChildWithLabels("b", loggo.Labels{"x": "y"})
context.GetLogger("c.d", "one")
context.GetLogger("e", "two")

// If a label is available on a logger, then resetting the levels should
// not remove the label.

context.ResetLoggerLevels()

c.Assert(context.Config(), gc.DeepEquals,
loggo.Config{
"": loggo.WARNING,
})
c.Assert(context.CompleteConfig(), gc.DeepEquals,
loggo.Config{
"": loggo.WARNING,
"a": loggo.UNSPECIFIED,
"a.b": loggo.UNSPECIFIED,
"c": loggo.UNSPECIFIED,
"c.d": loggo.UNSPECIFIED,
"e": loggo.UNSPECIFIED,
})
}

func (*ContextSuite) TestApplyConfigTagsResetLoggerLevelsUsingLabelsRemoval(c *gc.C) {
context := loggo.NewContext(loggo.WARNING)

context.ApplyConfig(loggo.Config{"#one": loggo.TRACE})
context.ApplyConfig(loggo.Config{"#two": loggo.DEBUG})

context.GetLogger("a", "one").ChildWithLabels("b", loggo.Labels{"x": "y"}).ChildWithTags("g", "one")
context.GetLogger("c.d", "one")
context.GetLogger("e", "two")
context.GetLogger("f")

// Ensure that the logger that matches exactly the label is removed,
// including it's children. So we observe hierarchy in the removal.

c.Assert(context.Config(), gc.DeepEquals,
loggo.Config{
"": loggo.WARNING,
"a": loggo.TRACE,
"a.b.g": loggo.TRACE,
"c.d": loggo.TRACE,
"e": loggo.DEBUG,
})
c.Assert(context.CompleteConfig(), gc.DeepEquals,
loggo.Config{
"": loggo.WARNING,
"a": loggo.TRACE,
"a.b": loggo.UNSPECIFIED,
"a.b.g": loggo.TRACE,
"c": loggo.UNSPECIFIED,
"c.d": loggo.TRACE,
"e": loggo.DEBUG,
"f": loggo.UNSPECIFIED,
})

context.ResetLoggerLevels(loggo.Labels{"x": "y"})

c.Assert(context.Config(), gc.DeepEquals,
loggo.Config{
"": loggo.WARNING,
"a": loggo.TRACE,
"c.d": loggo.TRACE,
"e": loggo.DEBUG,
})
c.Assert(context.CompleteConfig(), gc.DeepEquals,
loggo.Config{
"": loggo.WARNING,
"a": loggo.TRACE,
"a.b": loggo.UNSPECIFIED,
"a.b.g": loggo.UNSPECIFIED,
"c": loggo.UNSPECIFIED,
"c.d": loggo.TRACE,
"e": loggo.DEBUG,
"f": loggo.UNSPECIFIED,
})
}

func (*ContextSuite) TestApplyConfigTagsAdditive(c *gc.C) {
context := loggo.NewContext(loggo.WARNING)
context.ApplyConfig(loggo.Config{"#one": loggo.TRACE})
context.ApplyConfig(loggo.Config{"#two": loggo.DEBUG})
Expand Down
12 changes: 12 additions & 0 deletions labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,15 @@ const (

// Labels represents key values which are assigned to a log entry.
type Labels map[string]string

// mergeLabels merges multiple sets of labels into a single set.
// Later sets of labels take precedence over earlier sets.
func mergeLabels(labels []Labels) Labels {
result := make(Labels)
for _, l := range labels {
for k, v := range l {
result[k] = v
}
}
return result
}
53 changes: 8 additions & 45 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,11 @@ const (
// The zero Logger value is usable - any messages logged
// to it will be sent to the root Logger.
type Logger struct {
impl *module
labels Labels
impl *module

// CallDepth is the number of stack frames to ascend to find the caller.
callDepth int
}

// WithLabels returns a logger whose module is the same as this logger and
// the returned logger will add the specified labels to each log entry.
// WithLabels only target a specific logger with labels. Children of the logger
// will not inherit the labels.
// To add labels to all child loggers, use ChildWithLabels.
func (logger Logger) WithLabels(labels Labels) Logger {
if len(labels) == 0 {
return logger
}

result := logger
result.labels = make(Labels)
for k, v := range labels {
result.labels[k] = v
}
return result
}

// WithCallDepth returns a logger whose call depth is set to the specified
// value.
func (logger Logger) WithCallDepth(callDepth int) Logger {
Expand Down Expand Up @@ -151,6 +131,11 @@ func (logger Logger) Tags() []string {
return logger.getModule().tags
}

// Labels returns the configured labels of the logger's module.
func (logger Logger) Labels() Labels {
return logger.getModule().labels
}

// EffectiveLogLevel returns the effective min log level of
// the receiver - that is, messages with a lesser severity
// level will be discarded.
Expand Down Expand Up @@ -179,15 +164,6 @@ func (logger Logger) Logf(level Level, message string, args ...interface{}) {
logger.LogCallf(logger.callDepth, level, message, args...)
}

// LogWithlabelsf logs a printf-formatted message at the given level with extra
// labels. The given labels will be added to the log entry.
// A message will be discarded if level is less than the the effective log level
// of the logger. Note that the writers may also filter out messages that are
// less than their registered minimum severity level.
func (logger Logger) LogWithLabelsf(level Level, message string, extraLabels map[string]string, args ...interface{}) {
logger.logCallf(logger.callDepth, level, message, extraLabels, args...)
}

// LogCallf logs a printf-formatted message at the given level.
// The location of the call is indicated by the calldepth argument.
// A calldepth of 1 means the function that called this function.
Expand All @@ -196,12 +172,12 @@ func (logger Logger) LogWithLabelsf(level Level, message string, extraLabels map
// Note that the writers may also filter out messages that
// are less than their registered minimum severity level.
func (logger Logger) LogCallf(calldepth int, level Level, message string, args ...interface{}) {
logger.logCallf(calldepth+1, level, message, nil, args...)
logger.logCallf(calldepth+1, level, message, args...)
}

// logCallf is a private method for logging a printf-formatted message at the
// given level. Used by LogWithLabelsf and LogCallf.
func (logger Logger) logCallf(calldepth int, level Level, message string, extraLabels map[string]string, args ...interface{}) {
func (logger Logger) logCallf(calldepth int, level Level, message string, args ...interface{}) {
module := logger.getModule()
if !module.willWrite(level) {
return
Expand Down Expand Up @@ -244,13 +220,6 @@ func (logger Logger) logCallf(calldepth int, level Level, message string, extraL
for k, v := range module.labels {
entry.Labels[k] = v
}
for k, v := range logger.labels {
entry.Labels[k] = v
}
// Add extra labels if there's any given.
for k, v := range extraLabels {
entry.Labels[k] = v
}
module.write(entry)
}

Expand All @@ -274,12 +243,6 @@ func (logger Logger) Infof(message string, args ...interface{}) {
logger.Logf(INFO, message, args...)
}

// InfoWithLabelsf logs the printf-formatted message at info level with extra
// labels.
func (logger Logger) InfoWithLabelsf(message string, extraLabels map[string]string, args ...interface{}) {
logger.LogWithLabelsf(INFO, message, extraLabels, args...)
}

// Debugf logs the printf-formatted message at debug level.
func (logger Logger) Debugf(message string, args ...interface{}) {
logger.Logf(DEBUG, message, args...)
Expand Down
Loading