route/vendor/github.com/koding/logging/logging.go

476 lines
12 KiB
Go
Raw Normal View History

2017-01-22 17:36:44 +00:00
// 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()
}