476 lines
12 KiB
Go
476 lines
12 KiB
Go
// Package logging is an alternative to log package in standard library.
|
|
package logging
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
// Color represents log level colors
|
|
Color int
|
|
|
|
// Level represent severity of logs
|
|
Level int
|
|
)
|
|
|
|
// Colors for different log levels.
|
|
const (
|
|
BLACK Color = (iota + 30)
|
|
RED
|
|
GREEN
|
|
YELLOW
|
|
BLUE
|
|
MAGENTA
|
|
CYAN
|
|
WHITE
|
|
)
|
|
|
|
// Logging levels.
|
|
const (
|
|
CRITICAL Level = iota
|
|
ERROR
|
|
WARNING
|
|
NOTICE
|
|
INFO
|
|
DEBUG
|
|
)
|
|
|
|
// LevelNames provides mapping for log levels
|
|
var LevelNames = map[Level]string{
|
|
CRITICAL: "CRITICAL",
|
|
ERROR: "ERROR",
|
|
WARNING: "WARNING",
|
|
NOTICE: "NOTICE",
|
|
INFO: "INFO",
|
|
DEBUG: "DEBUG",
|
|
}
|
|
|
|
// LevelColors provides mapping for log colors
|
|
var LevelColors = map[Level]Color{
|
|
CRITICAL: MAGENTA,
|
|
ERROR: RED,
|
|
WARNING: YELLOW,
|
|
NOTICE: GREEN,
|
|
INFO: WHITE,
|
|
DEBUG: CYAN,
|
|
}
|
|
|
|
var (
|
|
// DefaultLogger holds default logger
|
|
DefaultLogger Logger = NewLogger(procName())
|
|
|
|
// DefaultLevel holds default value for loggers
|
|
DefaultLevel Level = INFO
|
|
|
|
// DefaultHandler holds default handler for loggers
|
|
DefaultHandler Handler = StderrHandler
|
|
|
|
// DefaultFormatter holds default formatter for loggers
|
|
DefaultFormatter Formatter = &defaultFormatter{}
|
|
|
|
// StdoutHandler holds a handler with outputting to stdout
|
|
StdoutHandler = NewWriterHandler(os.Stdout)
|
|
|
|
// StderrHandler holds a handler with outputting to stderr
|
|
StderrHandler = NewWriterHandler(os.Stderr)
|
|
)
|
|
|
|
// Logger is the interface for outputing log messages in different levels.
|
|
// A new Logger can be created with NewLogger() function.
|
|
// You can changed the output handler with SetHandler() function.
|
|
type Logger interface {
|
|
// SetLevel changes the level of the logger. Default is logging.Info.
|
|
SetLevel(Level)
|
|
|
|
// SetHandler replaces the current handler for output. Default is logging.StderrHandler.
|
|
SetHandler(Handler)
|
|
|
|
// SetCallDepth sets the parameter passed to runtime.Caller().
|
|
// It is used to get the file name from call stack.
|
|
// For example you need to set it to 1 if you are using a wrapper around
|
|
// the Logger. Default value is zero.
|
|
SetCallDepth(int)
|
|
|
|
// New creates a new inerhited context logger with given prefixes.
|
|
New(prefixes ...interface{}) Logger
|
|
|
|
// Fatal is equivalent to l.Critical followed by a call to os.Exit(1).
|
|
Fatal(format string, args ...interface{})
|
|
|
|
// Panic is equivalent to l.Critical followed by a call to panic().
|
|
Panic(format string, args ...interface{})
|
|
|
|
// Critical logs a message using CRITICAL as log level.
|
|
Critical(format string, args ...interface{})
|
|
|
|
// Error logs a message using ERROR as log level.
|
|
Error(format string, args ...interface{})
|
|
|
|
// Warning logs a message using WARNING as log level.
|
|
Warning(format string, args ...interface{})
|
|
|
|
// Notice logs a message using NOTICE as log level.
|
|
Notice(format string, args ...interface{})
|
|
|
|
// Info logs a message using INFO as log level.
|
|
Info(format string, args ...interface{})
|
|
|
|
// Debug logs a message using DEBUG as log level.
|
|
Debug(format string, args ...interface{})
|
|
}
|
|
|
|
// Handler handles the output.
|
|
type Handler interface {
|
|
SetFormatter(Formatter)
|
|
SetLevel(Level)
|
|
|
|
// Handle single log record.
|
|
Handle(*Record)
|
|
|
|
// Close the handler.
|
|
Close()
|
|
}
|
|
|
|
// Record contains all of the information about a single log message.
|
|
type Record struct {
|
|
Format string // Format string
|
|
Args []interface{} // Arguments to format string
|
|
LoggerName string // Name of the logger module
|
|
Level Level // Level of the record
|
|
Time time.Time // Time of the record (local time)
|
|
Filename string // File name of the log call (absolute path)
|
|
Line int // Line number in file
|
|
ProcessID int // PID
|
|
ProcessName string // Name of the process
|
|
}
|
|
|
|
// Formatter formats a record.
|
|
type Formatter interface {
|
|
// Format the record and return a message.
|
|
Format(*Record) (message string)
|
|
}
|
|
|
|
///////////////////////
|
|
// //
|
|
// Default Formatter //
|
|
// //
|
|
///////////////////////
|
|
|
|
type defaultFormatter struct{}
|
|
|
|
// Format outputs a message like "2014-02-28 18:15:57 [example] INFO something happened"
|
|
func (f *defaultFormatter) Format(rec *Record) string {
|
|
return fmt.Sprintf("%s [%s] %-8s %s", fmt.Sprint(rec.Time)[:19], rec.LoggerName, LevelNames[rec.Level], fmt.Sprintf(rec.Format, rec.Args...))
|
|
}
|
|
|
|
///////////////////////////
|
|
// //
|
|
// Logger implementation //
|
|
// //
|
|
///////////////////////////
|
|
|
|
// logger is the default Logger implementation.
|
|
type logger struct {
|
|
Name string
|
|
Level Level
|
|
Handler Handler
|
|
calldepth int
|
|
}
|
|
|
|
// NewLogger returns a new Logger implementation. Do not forget to close it at exit.
|
|
func NewLogger(name string) Logger {
|
|
return &logger{
|
|
Name: name,
|
|
Level: DefaultLevel,
|
|
Handler: DefaultHandler,
|
|
}
|
|
}
|
|
|
|
// New creates a new inerhited logger with the given prefixes
|
|
func (l *logger) New(prefixes ...interface{}) Logger {
|
|
return newContext(*l, "", prefixes...)
|
|
}
|
|
|
|
func (l *logger) SetLevel(level Level) {
|
|
l.Level = level
|
|
}
|
|
|
|
func (l *logger) SetHandler(b Handler) {
|
|
l.Handler = b
|
|
}
|
|
|
|
func (l *logger) SetCallDepth(n int) {
|
|
l.calldepth = n
|
|
}
|
|
|
|
// Fatal is equivalent to Critical() followed by a call to os.Exit(1).
|
|
func (l *logger) Fatal(format string, args ...interface{}) {
|
|
l.Critical(format, args...)
|
|
l.Handler.Close()
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Panic is equivalent to Critical() followed by a call to panic().
|
|
func (l *logger) Panic(format string, args ...interface{}) {
|
|
l.Critical(format, args...)
|
|
panic(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// Critical sends a critical level log message to the handler. Arguments are handled in the manner of fmt.Printf.
|
|
func (l *logger) Critical(format string, args ...interface{}) {
|
|
if l.Level >= CRITICAL {
|
|
l.log(CRITICAL, format, args...)
|
|
}
|
|
}
|
|
|
|
// Error sends a error level log message to the handler. Arguments are handled in the manner of fmt.Printf.
|
|
func (l *logger) Error(format string, args ...interface{}) {
|
|
if l.Level >= ERROR {
|
|
l.log(ERROR, format, args...)
|
|
}
|
|
}
|
|
|
|
// Warning sends a warning level log message to the handler. Arguments are handled in the manner of fmt.Printf.
|
|
func (l *logger) Warning(format string, args ...interface{}) {
|
|
if l.Level >= WARNING {
|
|
l.log(WARNING, format, args...)
|
|
}
|
|
}
|
|
|
|
// Notice sends a notice level log message to the handler. Arguments are handled in the manner of fmt.Printf.
|
|
func (l *logger) Notice(format string, args ...interface{}) {
|
|
if l.Level >= NOTICE {
|
|
l.log(NOTICE, format, args...)
|
|
}
|
|
}
|
|
|
|
// Info sends a info level log message to the handler. Arguments are handled in the manner of fmt.Printf.
|
|
func (l *logger) Info(format string, args ...interface{}) {
|
|
if l.Level >= INFO {
|
|
l.log(INFO, format, args...)
|
|
}
|
|
}
|
|
|
|
// Debug sends a debug level log message to the handler. Arguments are handled in the manner of fmt.Printf.
|
|
func (l *logger) Debug(format string, args ...interface{}) {
|
|
if l.Level >= DEBUG {
|
|
l.log(DEBUG, format, args...)
|
|
}
|
|
}
|
|
|
|
func (l *logger) log(level Level, format string, args ...interface{}) {
|
|
// Add missing newline at the end.
|
|
if !strings.HasSuffix(format, "\n") {
|
|
format += "\n"
|
|
}
|
|
|
|
_, file, line, ok := runtime.Caller(l.calldepth + 2)
|
|
if !ok {
|
|
file = "???"
|
|
line = 0
|
|
}
|
|
|
|
rec := &Record{
|
|
Format: format,
|
|
Args: args,
|
|
LoggerName: l.Name,
|
|
Level: level,
|
|
Time: time.Now(),
|
|
Filename: file,
|
|
Line: line,
|
|
ProcessName: procName(),
|
|
ProcessID: os.Getpid(),
|
|
}
|
|
|
|
l.Handler.Handle(rec)
|
|
}
|
|
|
|
// procName returns the name of the current process.
|
|
func procName() string { return filepath.Base(os.Args[0]) }
|
|
|
|
///////////////////
|
|
// //
|
|
// DefaultLogger //
|
|
// //
|
|
///////////////////
|
|
|
|
// Fatal is equivalent to Critical() followed by a call to os.Exit(1).
|
|
func Fatal(format string, args ...interface{}) {
|
|
DefaultLogger.Fatal(format, args...)
|
|
}
|
|
|
|
// Panic is equivalent to Critical() followed by a call to panic().
|
|
func Panic(format string, args ...interface{}) {
|
|
DefaultLogger.Panic(format, args...)
|
|
}
|
|
|
|
// Critical prints a critical level log message to the stderr. Arguments are handled in the manner of fmt.Printf.
|
|
func Critical(format string, args ...interface{}) {
|
|
DefaultLogger.Critical(format, args...)
|
|
}
|
|
|
|
// Error prints a error level log message to the stderr. Arguments are handled in the manner of fmt.Printf.
|
|
func Error(format string, args ...interface{}) {
|
|
DefaultLogger.Error(format, args...)
|
|
}
|
|
|
|
// Warning prints a warning level log message to the stderr. Arguments are handled in the manner of fmt.Printf.
|
|
func Warning(format string, args ...interface{}) {
|
|
DefaultLogger.Warning(format, args...)
|
|
}
|
|
|
|
// Notice prints a notice level log message to the stderr. Arguments are handled in the manner of fmt.Printf.
|
|
func Notice(format string, args ...interface{}) {
|
|
DefaultLogger.Notice(format, args...)
|
|
}
|
|
|
|
// Info prints a info level log message to the stderr. Arguments are handled in the manner of fmt.Printf.
|
|
func Info(format string, args ...interface{}) {
|
|
DefaultLogger.Info(format, args...)
|
|
}
|
|
|
|
// Debug prints a debug level log message to the stderr. Arguments are handled in the manner of fmt.Printf.
|
|
func Debug(format string, args ...interface{}) {
|
|
DefaultLogger.Debug(format, args...)
|
|
}
|
|
|
|
/////////////////
|
|
// //
|
|
// BaseHandler //
|
|
// //
|
|
/////////////////
|
|
|
|
// BaseHandler provides basic functionality for handler
|
|
type BaseHandler struct {
|
|
Level Level
|
|
Formatter Formatter
|
|
}
|
|
|
|
// NewBaseHandler creates a newBaseHandler with default values
|
|
func NewBaseHandler() *BaseHandler {
|
|
return &BaseHandler{
|
|
Level: DefaultLevel,
|
|
Formatter: DefaultFormatter,
|
|
}
|
|
}
|
|
|
|
// SetLevel sets logging level for handler
|
|
func (h *BaseHandler) SetLevel(l Level) {
|
|
h.Level = l
|
|
}
|
|
|
|
// SetFormatter sets logging formatter for handler
|
|
func (h *BaseHandler) SetFormatter(f Formatter) {
|
|
h.Formatter = f
|
|
}
|
|
|
|
// FilterAndFormat filters any record according to loggging level
|
|
func (h *BaseHandler) FilterAndFormat(rec *Record) string {
|
|
if h.Level >= rec.Level {
|
|
return h.Formatter.Format(rec)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
///////////////////
|
|
// //
|
|
// WriterHandler //
|
|
// //
|
|
///////////////////
|
|
|
|
// WriterHandler is a handler implementation that writes the logging output to a io.Writer.
|
|
type WriterHandler struct {
|
|
*BaseHandler
|
|
w io.Writer
|
|
Colorize bool
|
|
}
|
|
|
|
// NewWriterHandler creates a new writer handler with given io.Writer
|
|
func NewWriterHandler(w io.Writer) *WriterHandler {
|
|
return &WriterHandler{
|
|
BaseHandler: NewBaseHandler(),
|
|
w: w,
|
|
}
|
|
}
|
|
|
|
// Handle writes any given Record to the Writer.
|
|
func (b *WriterHandler) Handle(rec *Record) {
|
|
message := b.BaseHandler.FilterAndFormat(rec)
|
|
if message == "" {
|
|
return
|
|
}
|
|
if b.Colorize {
|
|
b.w.Write([]byte(fmt.Sprintf("\033[%dm", LevelColors[rec.Level])))
|
|
}
|
|
fmt.Fprint(b.w, message)
|
|
if b.Colorize {
|
|
b.w.Write([]byte("\033[0m")) // reset color
|
|
}
|
|
}
|
|
|
|
// Close closes WriterHandler
|
|
func (b *WriterHandler) Close() {}
|
|
|
|
//////////////////
|
|
// //
|
|
// MultiHandler //
|
|
// //
|
|
//////////////////
|
|
|
|
// MultiHandler sends the log output to multiple handlers concurrently.
|
|
type MultiHandler struct {
|
|
handlers []Handler
|
|
}
|
|
|
|
// NewMultiHandler creates a new handler with given handlers
|
|
func NewMultiHandler(handlers ...Handler) *MultiHandler {
|
|
return &MultiHandler{handlers: handlers}
|
|
}
|
|
|
|
// SetFormatter sets formatter for all handlers
|
|
func (b *MultiHandler) SetFormatter(f Formatter) {
|
|
for _, h := range b.handlers {
|
|
h.SetFormatter(f)
|
|
}
|
|
}
|
|
|
|
// SetLevel sets level for all handlers
|
|
func (b *MultiHandler) SetLevel(l Level) {
|
|
for _, h := range b.handlers {
|
|
h.SetLevel(l)
|
|
}
|
|
}
|
|
|
|
// Handle handles given record with all handlers concurrently
|
|
func (b *MultiHandler) Handle(rec *Record) {
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(len(b.handlers))
|
|
for _, handler := range b.handlers {
|
|
go func(handler Handler) {
|
|
handler.Handle(rec)
|
|
wg.Done()
|
|
}(handler)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
// Close closes all handlers concurrently
|
|
func (b *MultiHandler) Close() {
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(len(b.handlers))
|
|
for _, handler := range b.handlers {
|
|
go func(handler Handler) {
|
|
handler.Close()
|
|
wg.Done()
|
|
}(handler)
|
|
}
|
|
wg.Wait()
|
|
}
|