275 lines
6.4 KiB
Go
275 lines
6.4 KiB
Go
// Package logging provides domain-based logging in the same style as
|
|
// sylog. Domains are some name for which logging can be selectively
|
|
// enabled or disabled. Logging also differentiates between normal
|
|
// messages (which are sent to standard output) and errors, which are
|
|
// sent to standard error; debug messages will also include the file
|
|
// and line number.
|
|
//
|
|
// Domains are intended for identifying logging subystems. A domain
|
|
// can be suppressed with Suppress, and re-enabled with Enable. There
|
|
// are prefixed versions of these as well.
|
|
//
|
|
// Packages (e.g. those meant to be imported by programs) using the
|
|
// loggers here should observe a few etiquette guides:
|
|
//
|
|
// 1. A package should never suppress or enable other loggers except
|
|
// via an exported function that should be called by the end user.
|
|
//
|
|
// 2. A package should never call the global `SetLevel`; this is
|
|
// reserved for the end user.
|
|
//
|
|
// 3. Packages should use consistent, sane domains: preferably,
|
|
// related packages should use an unsurprising common prefix in their
|
|
// domains.
|
|
//
|
|
// This package was adapted from the CFSSL logging code.
|
|
package logging
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/kisom/goutils/mwc"
|
|
)
|
|
|
|
var logConfig = struct {
|
|
registered map[string]*Logger
|
|
lock *sync.Mutex
|
|
}{
|
|
registered: map[string]*Logger{},
|
|
lock: new(sync.Mutex),
|
|
}
|
|
|
|
// SetLevel sets the logging level for all loggers.
|
|
func SetLevel(level Level) {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
|
|
for _, l := range logConfig.registered {
|
|
l.SetLevel(level)
|
|
}
|
|
}
|
|
|
|
// DefaultLevel defaults to the notice level of logging.
|
|
const DefaultLevel = LevelNotice
|
|
|
|
// Init returns a new default logger. The domain is set to the
|
|
// program's name, and the default logging level is used.
|
|
func Init() *Logger {
|
|
l, _ := New(filepath.Base(os.Args[0]), DefaultLevel)
|
|
return l
|
|
}
|
|
|
|
// A Logger writes logs on behalf of a particular domain at a certain
|
|
// level.
|
|
type Logger struct {
|
|
enabled bool
|
|
lock *sync.Mutex
|
|
domain string
|
|
level Level
|
|
out io.WriteCloser
|
|
err io.WriteCloser
|
|
}
|
|
|
|
// Close closes the log's writers and suppresses the logger.
|
|
func (l *Logger) Close() error {
|
|
Suppress(l.domain)
|
|
err := l.out.Close()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
return l.err.Close()
|
|
}
|
|
|
|
// Suppress ignores logs from a specific domain.
|
|
func Suppress(domain string) {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
l, ok := logConfig.registered[domain]
|
|
if ok {
|
|
l.Suppress()
|
|
}
|
|
}
|
|
|
|
// SuppressPrefix suppress logs whose domain is prefixed with the
|
|
// prefix.
|
|
func SuppressPrefix(prefix string) {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
for domain, l := range logConfig.registered {
|
|
if strings.HasPrefix(domain, prefix) {
|
|
l.Suppress()
|
|
}
|
|
}
|
|
}
|
|
|
|
// SuppressAll suppresses all logging output.
|
|
func SuppressAll() {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
for _, l := range logConfig.registered {
|
|
l.Suppress()
|
|
}
|
|
}
|
|
|
|
// Enable enables logs from a specific domain.
|
|
func Enable(domain string) {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
l, ok := logConfig.registered[domain]
|
|
if ok {
|
|
l.Enable()
|
|
}
|
|
}
|
|
|
|
// EnablePrefix enables logs whose domain is prefixed with prefix.
|
|
func EnablePrefix(prefix string) {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
for domain, l := range logConfig.registered {
|
|
if strings.HasPrefix(domain, prefix) {
|
|
l.Enable()
|
|
}
|
|
}
|
|
}
|
|
|
|
// EnableAll enables all domains.
|
|
func EnableAll() {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
for _, l := range logConfig.registered {
|
|
l.Enable()
|
|
}
|
|
}
|
|
|
|
// New returns a new logger that writes to standard output for Notice
|
|
// and below and standard error for levels above Notice. If a logger
|
|
// with the same domain exists, the logger will set its level to level
|
|
// and return the logger; in this case, the registered return value
|
|
// will be true.
|
|
func New(domain string, level Level) (l *Logger, registered bool) {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
|
|
l = logConfig.registered[domain]
|
|
if l != nil {
|
|
l.SetLevel(level)
|
|
return l, true
|
|
}
|
|
|
|
l = &Logger{
|
|
domain: domain,
|
|
level: level,
|
|
out: os.Stdout,
|
|
err: os.Stderr,
|
|
lock: new(sync.Mutex),
|
|
}
|
|
|
|
l.Enable()
|
|
logConfig.registered[domain] = l
|
|
return l, false
|
|
}
|
|
|
|
// NewFromWriters returns a new logger that writes to the w io.WriteCloser
|
|
// for Notice and below and to the e io.WriteCloser for levels above
|
|
// Notice. If e is nil, w will be used. If a logger with the same
|
|
// domain exists, the logger will set its level to level and return
|
|
// the logger; in this case, the registered return value will be true.
|
|
func NewFromWriters(domain string, level Level, w, e io.WriteCloser) (l *Logger, registered bool) {
|
|
logConfig.lock.Lock()
|
|
defer logConfig.lock.Unlock()
|
|
|
|
l = logConfig.registered[domain]
|
|
if l != nil {
|
|
l.SetLevel(level)
|
|
return l, true
|
|
}
|
|
|
|
if w == nil {
|
|
w = os.Stdout
|
|
}
|
|
|
|
if e == nil {
|
|
e = w
|
|
}
|
|
|
|
l = &Logger{
|
|
domain: domain,
|
|
level: level,
|
|
out: w,
|
|
err: e,
|
|
lock: new(sync.Mutex),
|
|
}
|
|
|
|
l.Enable()
|
|
logConfig.registered[domain] = l
|
|
return l, false
|
|
}
|
|
|
|
// NewFromFile returns a new logger that opens the files for writing. If
|
|
// multiplex is true, output will be multiplexed to standard output
|
|
// and standard error as well.
|
|
func NewFromFile(domain string, level Level, outFile, errFile string, multiplex bool, flags int) (*Logger, error) {
|
|
l := &Logger{
|
|
domain: domain,
|
|
level: level,
|
|
lock: new(sync.Mutex),
|
|
}
|
|
|
|
outf, err := os.OpenFile(outFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY|flags, 0644)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
errf, err := os.OpenFile(errFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY|flags, 0644)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if multiplex {
|
|
l.out = mwc.MultiWriteCloser(outf, os.Stdout)
|
|
l.err = mwc.MultiWriteCloser(errf, os.Stderr)
|
|
} else {
|
|
l.out = outf
|
|
l.err = errf
|
|
}
|
|
|
|
Enable(domain)
|
|
return l, nil
|
|
}
|
|
|
|
// Enable allows output from the logger.
|
|
func (l *Logger) Enable() {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.enabled = true
|
|
}
|
|
|
|
// Enabled returns true if the logger is enabled.
|
|
func (l *Logger) Enabled() bool {
|
|
return l.enabled
|
|
}
|
|
|
|
// Suppress ignores output from the logger.
|
|
func (l *Logger) Suppress() {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.enabled = false
|
|
}
|
|
|
|
// Domain returns the domain of the logger.
|
|
func (l *Logger) Domain() string {
|
|
return l.domain
|
|
}
|
|
|
|
// SetLevel changes the level of the logger.
|
|
func (l *Logger) SetLevel(level Level) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.level = level
|
|
}
|