Adding logging and some common functions.
This commit is contained in:
parent
49444c3318
commit
0750c235b6
|
@ -0,0 +1,11 @@
|
|||
package lib
|
||||
|
||||
// Various constants used throughout the tools.
|
||||
|
||||
const (
|
||||
// ExitSuccess is the successful exit status.
|
||||
ExitSuccess = 0
|
||||
|
||||
// ExitFailure is the failing exit status.
|
||||
ExitFailure = 1
|
||||
)
|
|
@ -0,0 +1,47 @@
|
|||
// Package lib contains functions useful for most programs.
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var progname = filepath.Base(os.Args[0])
|
||||
|
||||
// Warnx displays a formatted error message to standard error, à la
|
||||
// warnx(3).
|
||||
func Warnx(format string, a ...interface{}) (int, error) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += "\n"
|
||||
return fmt.Fprintf(os.Stderr, format, a...)
|
||||
}
|
||||
|
||||
// Warn displays a formatted error message to standard output,
|
||||
// appending the error string, à la warn(3).
|
||||
func Warn(err error, format string, a ...interface{}) (int, error) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += ": %v\n"
|
||||
a = append(a, err)
|
||||
return fmt.Fprintf(os.Stderr, format, a...)
|
||||
}
|
||||
|
||||
// Errx displays a formatted error message to standard error and exits
|
||||
// with the status code from `exit`, à la errx(3).
|
||||
func Errx(exit int, format string, a ...interface{}) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += "\n"
|
||||
fmt.Fprintf(os.Stderr, format, a...)
|
||||
os.Exit(exit)
|
||||
}
|
||||
|
||||
// Err displays a formatting error message to standard error,
|
||||
// appending the error string, and exits with the status code from
|
||||
// `exit`, à la err(3).
|
||||
func Err(exit int, err error, format string, a ...interface{}) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += ": %v\n"
|
||||
a = append(a, err)
|
||||
fmt.Fprintf(os.Stderr, format, a...)
|
||||
os.Exit(exit)
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package main
|
||||
|
||||
import "github.com/kisom/goutils/logging"
|
||||
|
||||
var log = logging.Init()
|
||||
var olog = logging.New("subsystem #42", logging.LevelNotice)
|
||||
|
||||
func main() {
|
||||
log.Notice("Hello, world.")
|
||||
log.Warning("this program is about to end")
|
||||
|
||||
olog.Print("now online")
|
||||
logging.Suppress("olog")
|
||||
olog.Print("extraneous information")
|
||||
|
||||
logging.Enable("olog")
|
||||
olog.Print("relevant now")
|
||||
|
||||
logging.SuppressAll()
|
||||
log.Alert("screaming into the void")
|
||||
olog.Critical("can anyone hear me?")
|
||||
|
||||
log.Enable()
|
||||
log.Notice("i'm baaack")
|
||||
log.Suppress()
|
||||
log.Warning("but not for long")
|
||||
|
||||
logging.EnableAll()
|
||||
log.Notice("fare thee well")
|
||||
olog.Print("all good journeys must come to an end")
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Level represents a logging level.
|
||||
type Level uint8
|
||||
|
||||
// The following constants represent logging levels in increasing levels of seriousness.
|
||||
const (
|
||||
// LevelDebug are debug output useful during program testing
|
||||
// and debugging.
|
||||
LevelDebug = iota
|
||||
|
||||
// LevelInfo is used for informational messages.
|
||||
LevelInfo
|
||||
|
||||
// LevelNotice is for messages that are normal but
|
||||
// significant.
|
||||
LevelNotice
|
||||
|
||||
// LevelWarning is for messages that are warning conditions:
|
||||
// they're not indicative of a failure, but of a situation
|
||||
// that may lead to a failure later.
|
||||
LevelWarning
|
||||
|
||||
// LevelError is for messages indicating an error of some
|
||||
// kind.
|
||||
LevelError
|
||||
|
||||
// LevelCritical are messages for critical conditions.
|
||||
LevelCritical
|
||||
|
||||
// LevelAlert are for messages indicating that action
|
||||
// must be taken immediately.
|
||||
LevelAlert
|
||||
|
||||
// LevelFatal messages are akin to syslog's LOG_EMERG: the
|
||||
// system is unusable and cannot continue execution.
|
||||
LevelFatal
|
||||
)
|
||||
|
||||
func writeToOut(level Level) bool {
|
||||
if level < LevelWarning {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var levelPrefix = [...]string{
|
||||
LevelDebug: "[DEBUG] ",
|
||||
LevelInfo: "[INFO] ",
|
||||
LevelNotice: "[NOTICE] ",
|
||||
LevelWarning: "[WARNING] ",
|
||||
LevelError: "[ERROR] ",
|
||||
LevelCritical: "[CRITICAL] ",
|
||||
LevelAlert: "[ALERT] ",
|
||||
LevelFatal: "[FATAL] ",
|
||||
}
|
||||
|
||||
var DateFormat = "2006-01-02T15:03:04-0700"
|
||||
|
||||
func (l *Logger) outputf(level Level, format string, v []interface{}) {
|
||||
if !logConfig.registered[l.domain] {
|
||||
return
|
||||
}
|
||||
|
||||
if level >= l.level {
|
||||
format = fmt.Sprintf("%s %s: %s%s\n",
|
||||
time.Now().Format(DateFormat),
|
||||
l.domain, levelPrefix[level], format)
|
||||
if writeToOut(level) {
|
||||
fmt.Fprintf(l.out, format, v...)
|
||||
} else {
|
||||
fmt.Fprintf(l.err, format, v...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) output(level Level, v []interface{}) {
|
||||
if !logConfig.registered[l.domain] {
|
||||
return
|
||||
}
|
||||
|
||||
if level >= l.level {
|
||||
format := fmt.Sprintf("%s %s: %s",
|
||||
time.Now().Format(DateFormat),
|
||||
l.domain, levelPrefix[level])
|
||||
if writeToOut(level) {
|
||||
fmt.Fprintf(l.out, format)
|
||||
fmt.Fprintln(l.out, v...)
|
||||
} else {
|
||||
fmt.Fprintf(l.err, format)
|
||||
fmt.Fprintln(l.err, v...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fatalf logs a formatted message at the "fatal" level and then exits. The
|
||||
// arguments are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
||||
l.outputf(LevelFatal, format, v)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatal logs its arguments at the "fatal" level and then exits.
|
||||
func (l *Logger) Fatal(v ...interface{}) {
|
||||
l.output(LevelFatal, v)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Alertf logs a formatted message at the "alert" level. The
|
||||
// arguments are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Alertf(format string, v ...interface{}) {
|
||||
l.outputf(LevelAlert, format, v)
|
||||
}
|
||||
|
||||
// Alert logs its arguments at the "alert" level.
|
||||
func (l *Logger) Alert(v ...interface{}) {
|
||||
l.output(LevelAlert, v)
|
||||
}
|
||||
|
||||
// Criticalf logs a formatted message at the "critical" level. The
|
||||
// arguments are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Criticalf(format string, v ...interface{}) {
|
||||
l.outputf(LevelCritical, format, v)
|
||||
}
|
||||
|
||||
// Critical logs its arguments at the "critical" level.
|
||||
func (l *Logger) Critical(v ...interface{}) {
|
||||
l.output(LevelCritical, v)
|
||||
}
|
||||
|
||||
// Errorf logs a formatted message at the "error" level. The arguments
|
||||
// are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Errorf(format string, v ...interface{}) {
|
||||
l.outputf(LevelError, format, v)
|
||||
}
|
||||
|
||||
// Error logs its arguments at the "error" level.
|
||||
func (l *Logger) Error(v ...interface{}) {
|
||||
l.output(LevelError, v)
|
||||
}
|
||||
|
||||
// Warningf logs a formatted message at the "warning" level. The
|
||||
// arguments are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Warningf(format string, v ...interface{}) {
|
||||
l.outputf(LevelWarning, format, v)
|
||||
}
|
||||
|
||||
// Warning logs its arguments at the "warning" level.
|
||||
func (l *Logger) Warning(v ...interface{}) {
|
||||
l.output(LevelWarning, v)
|
||||
}
|
||||
|
||||
// Noticef logs a formatted message at the "notice" level. The arguments
|
||||
// are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Noticef(format string, v ...interface{}) {
|
||||
l.outputf(LevelNotice, format, v)
|
||||
}
|
||||
|
||||
// Notice logs its arguments at the "notice" level.
|
||||
func (l *Logger) Notice(v ...interface{}) {
|
||||
l.output(LevelNotice, v)
|
||||
}
|
||||
|
||||
// Infof logs a formatted message at the "info" level. The arguments
|
||||
// are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Infof(format string, v ...interface{}) {
|
||||
l.outputf(LevelInfo, format, v)
|
||||
}
|
||||
|
||||
// Info logs its arguments at the "info" level.
|
||||
func (l *Logger) Info(v ...interface{}) {
|
||||
l.output(LevelInfo, v)
|
||||
}
|
||||
|
||||
// Debugf logs a formatted message at the "debug" level. The arguments
|
||||
// are handled in the same manner as fmt.Printf.
|
||||
func (l *Logger) Debugf(format string, v ...interface{}) {
|
||||
l.outputf(LevelDebug, format, v)
|
||||
}
|
||||
|
||||
// Debug logs its arguments at the "debug" level.
|
||||
func (l *Logger) Debug(v ...interface{}) {
|
||||
l.output(LevelDebug, v)
|
||||
}
|
||||
|
||||
// Printf prints a formatted message at the default level.
|
||||
func (l *Logger) Printf(format string, v ...interface{}) {
|
||||
l.outputf(DefaultLevel, format, v)
|
||||
}
|
||||
|
||||
// Print prints its arguments at the default level.
|
||||
func (l *Logger) Print(v ...interface{}) {
|
||||
l.output(DefaultLevel, v)
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
// Package logging is an adaptation of the CFSSL logging library. It
|
||||
// operates on domains, which are components for which logging can be
|
||||
// selectively enabled or disabled. It also differentiates between
|
||||
// normal messages (which are sent to standard output) and errors,
|
||||
// which are sent to standard error.
|
||||
package logging
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/kisom/goutils/mwc"
|
||||
)
|
||||
|
||||
var logConfig = struct {
|
||||
registered map[string]bool
|
||||
lock *sync.Mutex
|
||||
}{
|
||||
registered: map[string]bool{},
|
||||
lock: new(sync.Mutex),
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return New(filepath.Base(os.Args[0]), DefaultLevel)
|
||||
}
|
||||
|
||||
// A Logger writes logs on behalf of a particular domain at a certain
|
||||
// level.
|
||||
type Logger struct {
|
||||
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()
|
||||
logConfig.registered[domain] = false
|
||||
}
|
||||
|
||||
// SuppressAll suppresses all logging output.
|
||||
func SuppressAll() {
|
||||
logConfig.lock.Lock()
|
||||
defer logConfig.lock.Unlock()
|
||||
for domain := range logConfig.registered {
|
||||
logConfig.registered[domain] = false
|
||||
}
|
||||
}
|
||||
|
||||
// Enable enables logs from a specific domain.
|
||||
func Enable(domain string) {
|
||||
logConfig.lock.Lock()
|
||||
defer logConfig.lock.Unlock()
|
||||
logConfig.registered[domain] = true
|
||||
}
|
||||
|
||||
// EnableAll enables all domains.
|
||||
func EnableAll() {
|
||||
logConfig.lock.Lock()
|
||||
defer logConfig.lock.Unlock()
|
||||
for domain := range logConfig.registered {
|
||||
logConfig.registered[domain] = true
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a new logger that writes to standard output for Notice
|
||||
// and below and standard error for levels above Notice.
|
||||
func New(domain string, level Level) *Logger {
|
||||
l := &Logger{
|
||||
domain: domain,
|
||||
level: level,
|
||||
out: os.Stdout,
|
||||
err: os.Stderr,
|
||||
}
|
||||
|
||||
Enable(domain)
|
||||
return l
|
||||
}
|
||||
|
||||
// NewWriters 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.
|
||||
func NewWriters(domain string, level Level, w, e io.WriteCloser) *Logger {
|
||||
if e == nil {
|
||||
e = w
|
||||
}
|
||||
|
||||
l := &Logger{
|
||||
domain: domain,
|
||||
level: level,
|
||||
out: w,
|
||||
err: e,
|
||||
}
|
||||
|
||||
Enable(domain)
|
||||
return l
|
||||
}
|
||||
|
||||
// NewFile 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 NewFile(domain string, level Level, outFile, errFile string, multiplex bool) (*Logger, error) {
|
||||
l := &Logger{
|
||||
domain: domain,
|
||||
level: level,
|
||||
}
|
||||
|
||||
var err error
|
||||
l.out, err = os.OpenFile(outFile, os.O_APPEND|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.err, err = os.OpenFile(errFile, os.O_APPEND|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if multiplex {
|
||||
l.out = mwc.MultiWriteCloser(l.out, os.Stdout)
|
||||
l.err = mwc.MultiWriteCloser(l.err, os.Stderr)
|
||||
}
|
||||
|
||||
Enable(domain)
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Enable allows output from the logger.
|
||||
func (l *Logger) Enable() {
|
||||
Enable(l.domain)
|
||||
}
|
||||
|
||||
// Suppress ignores output from the logger.
|
||||
func (l *Logger) Suppress() {
|
||||
Suppress(l.domain)
|
||||
}
|
||||
|
||||
// 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.level = level
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Package mwc implements MultiWriteClosers.
|
||||
package mwc
|
||||
|
||||
import "io"
|
||||
|
||||
type mwc struct {
|
||||
wcs []io.WriteCloser
|
||||
}
|
||||
|
||||
// Write implements the Writer interface.
|
||||
func (t *mwc) Write(p []byte) (n int, err error) {
|
||||
for _, w := range t.wcs {
|
||||
n, err = w.Write(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n != len(p) {
|
||||
err = io.ErrShortWrite
|
||||
return
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Close implements the Closer interface.
|
||||
func (t *mwc) Close() error {
|
||||
for _, wc := range t.wcs {
|
||||
err := wc.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultiWriteClose creates a WriteCloser that duplicates its writes to
|
||||
// all the provided writers, similar to the Unix tee(1) command.
|
||||
func MultiWriteCloser(wc ...io.WriteCloser) io.WriteCloser {
|
||||
wcs := make([]io.WriteCloser, len(wc))
|
||||
copy(wcs, wc)
|
||||
return &mwc{wcs}
|
||||
}
|
Loading…
Reference in New Issue