status-go/vendor/github.com/spacemonkeygo/spacelog/collection.go

272 lines
8.4 KiB
Go

// Copyright (C) 2014 Space Monkey, Inc.
//
// 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 spacelog
import (
"regexp"
"runtime"
"strings"
"sync"
"text/template"
)
var (
// If set, these prefixes will be stripped out of automatic logger names.
IgnoredPrefixes []string
badChars = regexp.MustCompile("[^a-zA-Z0-9_.-]")
slashes = regexp.MustCompile("[/]")
)
func callerName() string {
pc, _, _, ok := runtime.Caller(2)
if !ok {
return "unknown.unknown"
}
f := runtime.FuncForPC(pc)
if f == nil {
return "unknown.unknown"
}
name := f.Name()
for _, prefix := range IgnoredPrefixes {
name = strings.TrimPrefix(name, prefix)
}
return badChars.ReplaceAllLiteralString(
slashes.ReplaceAllLiteralString(name, "."), "_")
}
// LoggerCollections contain all of the loggers a program might use. Typically
// a codebase will just use the default logger collection.
type LoggerCollection struct {
mtx sync.Mutex
loggers map[string]*Logger
level LogLevel
handler Handler
}
// NewLoggerCollection creates a new logger collection. It's unlikely you will
// ever practically need this method. Use the DefaultLoggerCollection instead.
func NewLoggerCollection() *LoggerCollection {
return &LoggerCollection{
loggers: make(map[string]*Logger),
level: DefaultLevel,
handler: defaultHandler}
}
// GetLogger returns a new Logger with a name automatically generated using
// the callstack. If you want to avoid automatic name generation check out
// GetLoggerNamed
func (c *LoggerCollection) GetLogger() *Logger {
return c.GetLoggerNamed(callerName())
}
func (c *LoggerCollection) getLogger(name string, level LogLevel,
handler Handler) *Logger {
c.mtx.Lock()
defer c.mtx.Unlock()
logger, exists := c.loggers[name]
if !exists {
logger = &Logger{level: level,
collection: c,
name: name,
handler: handler}
c.loggers[name] = logger
}
return logger
}
// ConfigureLoggers configures loggers according to the given string
// specification, which specifies a set of loggers and their associated
// logging levels. Loggers are semicolon-separated; each
// configuration is specified as <logger>=<level>. White space outside of
// logger names and levels is ignored. The default level is specified
// with the name "DEFAULT".
//
// An example specification:
// `DEFAULT=ERROR; foo.bar=WARNING`
func (c *LoggerCollection) ConfigureLoggers(specification string) error {
confs := strings.Split(strings.TrimSpace(specification), ";")
for i := range confs {
conf := strings.SplitN(confs[i], "=", 2)
levelstr := strings.TrimSpace(conf[1])
name := strings.TrimSpace(conf[0])
level, err := LevelFromString(levelstr)
if err != nil {
return err
}
if name == "DEFAULT" {
c.SetLevel(nil, level)
continue
}
logger := c.GetLoggerNamed(name)
logger.setLevel(level)
}
return nil
}
// GetLoggerNamed returns a new Logger with the provided name. GetLogger is
// more frequently used.
func (c *LoggerCollection) GetLoggerNamed(name string) *Logger {
c.mtx.Lock()
defer c.mtx.Unlock()
logger, exists := c.loggers[name]
if !exists {
logger = &Logger{level: c.level,
collection: c,
name: name,
handler: c.handler}
c.loggers[name] = logger
}
return logger
}
// SetLevel will set the current log level for all loggers with names that
// match a provided regular expression. If the regular expression is nil, then
// all loggers match.
func (c *LoggerCollection) SetLevel(re *regexp.Regexp, level LogLevel) {
c.mtx.Lock()
defer c.mtx.Unlock()
if re == nil {
c.level = level
}
for name, logger := range c.loggers {
if re == nil || re.MatchString(name) {
logger.setLevel(level)
}
}
}
// SetHandler will set the current log handler for all loggers with names that
// match a provided regular expression. If the regular expression is nil, then
// all loggers match.
func (c *LoggerCollection) SetHandler(re *regexp.Regexp, handler Handler) {
c.mtx.Lock()
defer c.mtx.Unlock()
if re == nil {
c.handler = handler
}
for name, logger := range c.loggers {
if re == nil || re.MatchString(name) {
logger.setHandler(handler)
}
}
}
// SetTextTemplate will set the current text template for all loggers with
// names that match a provided regular expression. If the regular expression
// is nil, then all loggers match. Note that not every handler is guaranteed
// to support text templates and a text template will only apply to
// text-oriented and unstructured handlers.
func (c *LoggerCollection) SetTextTemplate(re *regexp.Regexp,
t *template.Template) {
c.mtx.Lock()
defer c.mtx.Unlock()
if re == nil {
c.handler.SetTextTemplate(t)
}
for name, logger := range c.loggers {
if re == nil || re.MatchString(name) {
logger.getHandler().SetTextTemplate(t)
}
}
}
// SetTextOutput will set the current output interface for all loggers with
// names that match a provided regular expression. If the regular expression
// is nil, then all loggers match. Note that not every handler is guaranteed
// to support text output and a text output interface will only apply to
// text-oriented and unstructured handlers.
func (c *LoggerCollection) SetTextOutput(re *regexp.Regexp,
output TextOutput) {
c.mtx.Lock()
defer c.mtx.Unlock()
if re == nil {
c.handler.SetTextOutput(output)
}
for name, logger := range c.loggers {
if re == nil || re.MatchString(name) {
logger.getHandler().SetTextOutput(output)
}
}
}
var (
// It's unlikely you'll need to use this directly
DefaultLoggerCollection = NewLoggerCollection()
)
// GetLogger returns an automatically-named logger on the default logger
// collection.
func GetLogger() *Logger {
return DefaultLoggerCollection.GetLoggerNamed(callerName())
}
// GetLoggerNamed returns a new Logger with the provided name on the default
// logger collection. GetLogger is more frequently used.
func GetLoggerNamed(name string) *Logger {
return DefaultLoggerCollection.GetLoggerNamed(name)
}
// ConfigureLoggers configures loggers according to the given string
// specification, which specifies a set of loggers and their associated
// logging levels. Loggers are colon- or semicolon-separated; each
// configuration is specified as <logger>=<level>. White space outside of
// logger names and levels is ignored. The DEFAULT module is specified
// with the name "DEFAULT".
//
// An example specification:
// `DEFAULT=ERROR; foo.bar=WARNING`
func ConfigureLoggers(specification string) error {
return DefaultLoggerCollection.ConfigureLoggers(specification)
}
// SetLevel will set the current log level for all loggers on the default
// collection with names that match a provided regular expression. If the
// regular expression is nil, then all loggers match.
func SetLevel(re *regexp.Regexp, level LogLevel) {
DefaultLoggerCollection.SetLevel(re, level)
}
// SetHandler will set the current log handler for all loggers on the default
// collection with names that match a provided regular expression. If the
// regular expression is nil, then all loggers match.
func SetHandler(re *regexp.Regexp, handler Handler) {
DefaultLoggerCollection.SetHandler(re, handler)
}
// SetTextTemplate will set the current text template for all loggers on the
// default collection with names that match a provided regular expression. If
// the regular expression is nil, then all loggers match. Note that not every
// handler is guaranteed to support text templates and a text template will
// only apply to text-oriented and unstructured handlers.
func SetTextTemplate(re *regexp.Regexp, t *template.Template) {
DefaultLoggerCollection.SetTextTemplate(re, t)
}
// SetTextOutput will set the current output interface for all loggers on the
// default collection with names that match a provided regular expression. If
// the regular expression is nil, then all loggers match. Note that not every
// handler is guaranteed to support text output and a text output interface
// will only apply to text-oriented and unstructured handlers.
func SetTextOutput(re *regexp.Regexp, output TextOutput) {
DefaultLoggerCollection.SetTextOutput(re, output)
}