mirror of
https://github.com/Luzifer/cloudkeys-go.git
synced 2024-11-14 08:52:44 +00:00
199 lines
5.1 KiB
Go
199 lines
5.1 KiB
Go
|
// Copyright 2016 Canonical Ltd.
|
||
|
// Licensed under the LGPLv3, see LICENCE file for details.
|
||
|
|
||
|
package loggo
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
// Context produces loggers for a hierarchy of modules. The context holds
|
||
|
// a collection of hierarchical loggers and their writers.
|
||
|
type Context struct {
|
||
|
root *module
|
||
|
|
||
|
// Perhaps have one mutex?
|
||
|
modulesMutex sync.Mutex
|
||
|
modules map[string]*module
|
||
|
|
||
|
writersMutex sync.Mutex
|
||
|
writers map[string]Writer
|
||
|
|
||
|
// writeMuxtex is used to serialise write operations.
|
||
|
writeMutex sync.Mutex
|
||
|
}
|
||
|
|
||
|
// NewLoggers returns a new Context with no writers set.
|
||
|
// If the root level is UNSPECIFIED, WARNING is used.
|
||
|
func NewContext(rootLevel Level) *Context {
|
||
|
if rootLevel < TRACE || rootLevel > CRITICAL {
|
||
|
rootLevel = WARNING
|
||
|
}
|
||
|
context := &Context{
|
||
|
modules: make(map[string]*module),
|
||
|
writers: make(map[string]Writer),
|
||
|
}
|
||
|
context.root = &module{
|
||
|
level: rootLevel,
|
||
|
context: context,
|
||
|
}
|
||
|
context.modules[""] = context.root
|
||
|
return context
|
||
|
}
|
||
|
|
||
|
// GetLogger returns a Logger for the given module name, creating it and
|
||
|
// its parents if necessary.
|
||
|
func (c *Context) GetLogger(name string) Logger {
|
||
|
name = strings.TrimSpace(strings.ToLower(name))
|
||
|
c.modulesMutex.Lock()
|
||
|
defer c.modulesMutex.Unlock()
|
||
|
return Logger{c.getLoggerModule(name)}
|
||
|
}
|
||
|
|
||
|
func (c *Context) getLoggerModule(name string) *module {
|
||
|
if name == rootString {
|
||
|
name = ""
|
||
|
}
|
||
|
impl, found := c.modules[name]
|
||
|
if found {
|
||
|
return impl
|
||
|
}
|
||
|
parentName := ""
|
||
|
if i := strings.LastIndex(name, "."); i >= 0 {
|
||
|
parentName = name[0:i]
|
||
|
}
|
||
|
parent := c.getLoggerModule(parentName)
|
||
|
impl = &module{name, UNSPECIFIED, parent, c}
|
||
|
c.modules[name] = impl
|
||
|
return impl
|
||
|
}
|
||
|
|
||
|
// Config returns the current configuration of the Loggers. Loggers
|
||
|
// with UNSPECIFIED level will not be included.
|
||
|
func (c *Context) Config() Config {
|
||
|
result := make(Config)
|
||
|
c.modulesMutex.Lock()
|
||
|
defer c.modulesMutex.Unlock()
|
||
|
|
||
|
for name, module := range c.modules {
|
||
|
if module.level != UNSPECIFIED {
|
||
|
result[name] = module.level
|
||
|
}
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// CompleteConfig returns all the loggers and their defined levels,
|
||
|
// even if that level is UNSPECIFIED.
|
||
|
func (c *Context) CompleteConfig() Config {
|
||
|
result := make(Config)
|
||
|
c.modulesMutex.Lock()
|
||
|
defer c.modulesMutex.Unlock()
|
||
|
|
||
|
for name, module := range c.modules {
|
||
|
result[name] = module.level
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// ApplyConfig configures the logging modules according to the provided config.
|
||
|
func (c *Context) ApplyConfig(config Config) {
|
||
|
c.modulesMutex.Lock()
|
||
|
defer c.modulesMutex.Unlock()
|
||
|
for name, level := range config {
|
||
|
module := c.getLoggerModule(name)
|
||
|
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() {
|
||
|
c.modulesMutex.Lock()
|
||
|
defer c.modulesMutex.Unlock()
|
||
|
// Setting the root module to UNSPECIFIED will set it to WARNING.
|
||
|
for _, module := range c.modules {
|
||
|
module.setLevel(UNSPECIFIED)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *Context) write(entry Entry) {
|
||
|
c.writeMutex.Lock()
|
||
|
defer c.writeMutex.Unlock()
|
||
|
for _, writer := range c.getWriters() {
|
||
|
writer.Write(entry)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *Context) getWriters() []Writer {
|
||
|
c.writersMutex.Lock()
|
||
|
defer c.writersMutex.Unlock()
|
||
|
var result []Writer
|
||
|
for _, writer := range c.writers {
|
||
|
result = append(result, writer)
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// AddWriter adds a writer to the list to be called for each logging call.
|
||
|
// The name cannot be empty, and the writer cannot be nil. If an existing
|
||
|
// writer exists with the specified name, an error is returned.
|
||
|
func (c *Context) AddWriter(name string, writer Writer) error {
|
||
|
if name == "" {
|
||
|
return fmt.Errorf("name cannot be empty")
|
||
|
}
|
||
|
if writer == nil {
|
||
|
return fmt.Errorf("writer cannot be nil")
|
||
|
}
|
||
|
c.writersMutex.Lock()
|
||
|
defer c.writersMutex.Unlock()
|
||
|
if _, found := c.writers[name]; found {
|
||
|
return fmt.Errorf("context already has a writer named %q", name)
|
||
|
}
|
||
|
c.writers[name] = writer
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// RemoveWriter remotes the specified writer. If a writer is not found with
|
||
|
// the specified name an error is returned. The writer that was removed is also
|
||
|
// returned.
|
||
|
func (c *Context) RemoveWriter(name string) (Writer, error) {
|
||
|
c.writersMutex.Lock()
|
||
|
defer c.writersMutex.Unlock()
|
||
|
reg, found := c.writers[name]
|
||
|
if !found {
|
||
|
return nil, fmt.Errorf("context has no writer named %q", name)
|
||
|
}
|
||
|
delete(c.writers, name)
|
||
|
return reg, nil
|
||
|
}
|
||
|
|
||
|
// ReplaceWriter is a convenience method that does the equivalent of RemoveWriter
|
||
|
// followed by AddWriter with the same name. The replaced writer is returned.
|
||
|
func (c *Context) ReplaceWriter(name string, writer Writer) (Writer, error) {
|
||
|
if name == "" {
|
||
|
return nil, fmt.Errorf("name cannot be empty")
|
||
|
}
|
||
|
if writer == nil {
|
||
|
return nil, fmt.Errorf("writer cannot be nil")
|
||
|
}
|
||
|
c.writersMutex.Lock()
|
||
|
defer c.writersMutex.Unlock()
|
||
|
reg, found := c.writers[name]
|
||
|
if !found {
|
||
|
return nil, fmt.Errorf("context has no writer named %q", name)
|
||
|
}
|
||
|
oldWriter := reg
|
||
|
c.writers[name] = writer
|
||
|
return oldWriter, nil
|
||
|
}
|
||
|
|
||
|
// ResetWriters is generally only used in testing and removes all the writers.
|
||
|
func (c *Context) ResetWriters() {
|
||
|
c.writersMutex.Lock()
|
||
|
defer c.writersMutex.Unlock()
|
||
|
c.writers = make(map[string]Writer)
|
||
|
}
|