forked from mystiq/dex
240 lines
5.8 KiB
Go
240 lines
5.8 KiB
Go
// Copyright 2015 CoreOS, 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 capnslog
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// LogLevel is the set of all log levels.
|
|
type LogLevel int8
|
|
|
|
const (
|
|
// CRITICAL is the lowest log level; only errors which will end the program will be propagated.
|
|
CRITICAL LogLevel = iota - 1
|
|
// ERROR is for errors that are not fatal but lead to troubling behavior.
|
|
ERROR
|
|
// WARNING is for errors which are not fatal and not errors, but are unusual. Often sourced from misconfigurations.
|
|
WARNING
|
|
// NOTICE is for normal but significant conditions.
|
|
NOTICE
|
|
// INFO is a log level for common, everyday log updates.
|
|
INFO
|
|
// DEBUG is the default hidden level for more verbose updates about internal processes.
|
|
DEBUG
|
|
// TRACE is for (potentially) call by call tracing of programs.
|
|
TRACE
|
|
)
|
|
|
|
// Char returns a single-character representation of the log level.
|
|
func (l LogLevel) Char() string {
|
|
switch l {
|
|
case CRITICAL:
|
|
return "C"
|
|
case ERROR:
|
|
return "E"
|
|
case WARNING:
|
|
return "W"
|
|
case NOTICE:
|
|
return "N"
|
|
case INFO:
|
|
return "I"
|
|
case DEBUG:
|
|
return "D"
|
|
case TRACE:
|
|
return "T"
|
|
default:
|
|
panic("Unhandled loglevel")
|
|
}
|
|
}
|
|
|
|
// String returns a multi-character representation of the log level.
|
|
func (l LogLevel) String() string {
|
|
switch l {
|
|
case CRITICAL:
|
|
return "CRITICAL"
|
|
case ERROR:
|
|
return "ERROR"
|
|
case WARNING:
|
|
return "WARNING"
|
|
case NOTICE:
|
|
return "NOTICE"
|
|
case INFO:
|
|
return "INFO"
|
|
case DEBUG:
|
|
return "DEBUG"
|
|
case TRACE:
|
|
return "TRACE"
|
|
default:
|
|
panic("Unhandled loglevel")
|
|
}
|
|
}
|
|
|
|
// Update using the given string value. Fulfills the flag.Value interface.
|
|
func (l *LogLevel) Set(s string) error {
|
|
value, err := ParseLevel(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*l = value
|
|
return nil
|
|
}
|
|
|
|
// ParseLevel translates some potential loglevel strings into their corresponding levels.
|
|
func ParseLevel(s string) (LogLevel, error) {
|
|
switch s {
|
|
case "CRITICAL", "C":
|
|
return CRITICAL, nil
|
|
case "ERROR", "0", "E":
|
|
return ERROR, nil
|
|
case "WARNING", "1", "W":
|
|
return WARNING, nil
|
|
case "NOTICE", "2", "N":
|
|
return NOTICE, nil
|
|
case "INFO", "3", "I":
|
|
return INFO, nil
|
|
case "DEBUG", "4", "D":
|
|
return DEBUG, nil
|
|
case "TRACE", "5", "T":
|
|
return TRACE, nil
|
|
}
|
|
return CRITICAL, errors.New("couldn't parse log level " + s)
|
|
}
|
|
|
|
type RepoLogger map[string]*PackageLogger
|
|
|
|
type loggerStruct struct {
|
|
sync.Mutex
|
|
repoMap map[string]RepoLogger
|
|
formatter Formatter
|
|
}
|
|
|
|
// logger is the global logger
|
|
var logger = new(loggerStruct)
|
|
|
|
// SetGlobalLogLevel sets the log level for all packages in all repositories
|
|
// registered with capnslog.
|
|
func SetGlobalLogLevel(l LogLevel) {
|
|
logger.Lock()
|
|
defer logger.Unlock()
|
|
for _, r := range logger.repoMap {
|
|
r.setRepoLogLevelInternal(l)
|
|
}
|
|
}
|
|
|
|
// GetRepoLogger may return the handle to the repository's set of packages' loggers.
|
|
func GetRepoLogger(repo string) (RepoLogger, error) {
|
|
logger.Lock()
|
|
defer logger.Unlock()
|
|
r, ok := logger.repoMap[repo]
|
|
if !ok {
|
|
return nil, errors.New("no packages registered for repo " + repo)
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
// MustRepoLogger returns the handle to the repository's packages' loggers.
|
|
func MustRepoLogger(repo string) RepoLogger {
|
|
r, err := GetRepoLogger(repo)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return r
|
|
}
|
|
|
|
// SetRepoLogLevel sets the log level for all packages in the repository.
|
|
func (r RepoLogger) SetRepoLogLevel(l LogLevel) {
|
|
logger.Lock()
|
|
defer logger.Unlock()
|
|
r.setRepoLogLevelInternal(l)
|
|
}
|
|
|
|
func (r RepoLogger) setRepoLogLevelInternal(l LogLevel) {
|
|
for _, v := range r {
|
|
v.level = l
|
|
}
|
|
}
|
|
|
|
// ParseLogLevelConfig parses a comma-separated string of "package=loglevel", in
|
|
// order, and returns a map of the results, for use in SetLogLevel.
|
|
func (r RepoLogger) ParseLogLevelConfig(conf string) (map[string]LogLevel, error) {
|
|
setlist := strings.Split(conf, ",")
|
|
out := make(map[string]LogLevel)
|
|
for _, setstring := range setlist {
|
|
setting := strings.Split(setstring, "=")
|
|
if len(setting) != 2 {
|
|
return nil, errors.New("oddly structured `pkg=level` option: " + setstring)
|
|
}
|
|
l, err := ParseLevel(setting[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out[setting[0]] = l
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// SetLogLevel takes a map of package names within a repository to their desired
|
|
// loglevel, and sets the levels appropriately. Unknown packages are ignored.
|
|
// "*" is a special package name that corresponds to all packages, and will be
|
|
// processed first.
|
|
func (r RepoLogger) SetLogLevel(m map[string]LogLevel) {
|
|
logger.Lock()
|
|
defer logger.Unlock()
|
|
if l, ok := m["*"]; ok {
|
|
r.setRepoLogLevelInternal(l)
|
|
}
|
|
for k, v := range m {
|
|
l, ok := r[k]
|
|
if !ok {
|
|
continue
|
|
}
|
|
l.level = v
|
|
}
|
|
}
|
|
|
|
// SetFormatter sets the formatting function for all logs.
|
|
func SetFormatter(f Formatter) {
|
|
logger.Lock()
|
|
defer logger.Unlock()
|
|
logger.formatter = f
|
|
}
|
|
|
|
// NewPackageLogger creates a package logger object.
|
|
// This should be defined as a global var in your package, referencing your repo.
|
|
func NewPackageLogger(repo string, pkg string) (p *PackageLogger) {
|
|
logger.Lock()
|
|
defer logger.Unlock()
|
|
if logger.repoMap == nil {
|
|
logger.repoMap = make(map[string]RepoLogger)
|
|
}
|
|
r, rok := logger.repoMap[repo]
|
|
if !rok {
|
|
logger.repoMap[repo] = make(RepoLogger)
|
|
r = logger.repoMap[repo]
|
|
}
|
|
p, pok := r[pkg]
|
|
if !pok {
|
|
r[pkg] = &PackageLogger{
|
|
pkg: pkg,
|
|
level: INFO,
|
|
}
|
|
p = r[pkg]
|
|
}
|
|
return
|
|
}
|