485 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			485 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.
 | 
						|
 | 
						|
// Package log4go provides level-based and highly configurable logging.
 | 
						|
//
 | 
						|
// Enhanced Logging
 | 
						|
//
 | 
						|
// This is inspired by the logging functionality in Java.  Essentially, you create a Logger
 | 
						|
// object and create output filters for it.  You can send whatever you want to the Logger,
 | 
						|
// and it will filter that based on your settings and send it to the outputs.  This way, you
 | 
						|
// can put as much debug code in your program as you want, and when you're done you can filter
 | 
						|
// out the mundane messages so only the important ones show up.
 | 
						|
//
 | 
						|
// Utility functions are provided to make life easier. Here is some example code to get started:
 | 
						|
//
 | 
						|
// log := log4go.NewLogger()
 | 
						|
// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
 | 
						|
// log.AddFilter("log",    log4go.FINE,  log4go.NewFileLogWriter("example.log", true))
 | 
						|
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
 | 
						|
//
 | 
						|
// The first two lines can be combined with the utility NewDefaultLogger:
 | 
						|
//
 | 
						|
// log := log4go.NewDefaultLogger(log4go.DEBUG)
 | 
						|
// log.AddFilter("log",    log4go.FINE,  log4go.NewFileLogWriter("example.log", true))
 | 
						|
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
 | 
						|
//
 | 
						|
// Usage notes:
 | 
						|
// - The ConsoleLogWriter does not display the source of the message to standard
 | 
						|
//   output, but the FileLogWriter does.
 | 
						|
// - The utility functions (Info, Debug, Warn, etc) derive their source from the
 | 
						|
//   calling function, and this incurs extra overhead.
 | 
						|
//
 | 
						|
// Changes from 2.0:
 | 
						|
// - The external interface has remained mostly stable, but a lot of the
 | 
						|
//   internals have been changed, so if you depended on any of this or created
 | 
						|
//   your own LogWriter, then you will probably have to update your code.  In
 | 
						|
//   particular, Logger is now a map and ConsoleLogWriter is now a channel
 | 
						|
//   behind-the-scenes, and the LogWrite method no longer has return values.
 | 
						|
//
 | 
						|
// Future work: (please let me know if you think I should work on any of these particularly)
 | 
						|
// - Log file rotation
 | 
						|
// - Logging configuration files ala log4j
 | 
						|
// - Have the ability to remove filters?
 | 
						|
// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
 | 
						|
//   for another method of logging
 | 
						|
// - Add an XML filter type
 | 
						|
package log4go
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"runtime"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
// Version information
 | 
						|
const (
 | 
						|
	L4G_VERSION = "log4go-v3.0.1"
 | 
						|
	L4G_MAJOR   = 3
 | 
						|
	L4G_MINOR   = 0
 | 
						|
	L4G_BUILD   = 1
 | 
						|
)
 | 
						|
 | 
						|
/****** Constants ******/
 | 
						|
 | 
						|
// These are the integer logging levels used by the logger
 | 
						|
type Level int
 | 
						|
 | 
						|
const (
 | 
						|
	FINEST Level = iota
 | 
						|
	FINE
 | 
						|
	DEBUG
 | 
						|
	TRACE
 | 
						|
	INFO
 | 
						|
	WARNING
 | 
						|
	ERROR
 | 
						|
	CRITICAL
 | 
						|
)
 | 
						|
 | 
						|
// Logging level strings
 | 
						|
var (
 | 
						|
	levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
 | 
						|
)
 | 
						|
 | 
						|
func (l Level) String() string {
 | 
						|
	if l < 0 || int(l) > len(levelStrings) {
 | 
						|
		return "UNKNOWN"
 | 
						|
	}
 | 
						|
	return levelStrings[int(l)]
 | 
						|
}
 | 
						|
 | 
						|
/****** Variables ******/
 | 
						|
var (
 | 
						|
	// LogBufferLength specifies how many log messages a particular log4go
 | 
						|
	// logger can buffer at a time before writing them.
 | 
						|
	LogBufferLength = 32
 | 
						|
)
 | 
						|
 | 
						|
/****** LogRecord ******/
 | 
						|
 | 
						|
// A LogRecord contains all of the pertinent information for each message
 | 
						|
type LogRecord struct {
 | 
						|
	Level   Level     // The log level
 | 
						|
	Created time.Time // The time at which the log message was created (nanoseconds)
 | 
						|
	Source  string    // The message source
 | 
						|
	Message string    // The log message
 | 
						|
}
 | 
						|
 | 
						|
/****** LogWriter ******/
 | 
						|
 | 
						|
// This is an interface for anything that should be able to write logs
 | 
						|
type LogWriter interface {
 | 
						|
	// This will be called to log a LogRecord message.
 | 
						|
	LogWrite(rec *LogRecord)
 | 
						|
 | 
						|
	// This should clean up anything lingering about the LogWriter, as it is called before
 | 
						|
	// the LogWriter is removed.  LogWrite should not be called after Close.
 | 
						|
	Close()
 | 
						|
}
 | 
						|
 | 
						|
/****** Logger ******/
 | 
						|
 | 
						|
// A Filter represents the log level below which no log records are written to
 | 
						|
// the associated LogWriter.
 | 
						|
type Filter struct {
 | 
						|
	Level Level
 | 
						|
	LogWriter
 | 
						|
}
 | 
						|
 | 
						|
// A Logger represents a collection of Filters through which log messages are
 | 
						|
// written.
 | 
						|
type Logger map[string]*Filter
 | 
						|
 | 
						|
// Create a new logger.
 | 
						|
//
 | 
						|
// DEPRECATED: Use make(Logger) instead.
 | 
						|
func NewLogger() Logger {
 | 
						|
	os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
 | 
						|
	return make(Logger)
 | 
						|
}
 | 
						|
 | 
						|
// Create a new logger with a "stdout" filter configured to send log messages at
 | 
						|
// or above lvl to standard output.
 | 
						|
//
 | 
						|
// DEPRECATED: use NewDefaultLogger instead.
 | 
						|
func NewConsoleLogger(lvl Level) Logger {
 | 
						|
	os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
 | 
						|
	return Logger{
 | 
						|
		"stdout": &Filter{lvl, NewConsoleLogWriter()},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Create a new logger with a "stdout" filter configured to send log messages at
 | 
						|
// or above lvl to standard output.
 | 
						|
func NewDefaultLogger(lvl Level) Logger {
 | 
						|
	return Logger{
 | 
						|
		"stdout": &Filter{lvl, NewConsoleLogWriter()},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Closes all log writers in preparation for exiting the program or a
 | 
						|
// reconfiguration of logging.  Calling this is not really imperative, unless
 | 
						|
// you want to guarantee that all log messages are written.  Close removes
 | 
						|
// all filters (and thus all LogWriters) from the logger.
 | 
						|
func (log Logger) Close() {
 | 
						|
	// Close all open loggers
 | 
						|
	for name, filt := range log {
 | 
						|
		filt.Close()
 | 
						|
		delete(log, name)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Add a new LogWriter to the Logger which will only log messages at lvl or
 | 
						|
// higher.  This function should not be called from multiple goroutines.
 | 
						|
// Returns the logger for chaining.
 | 
						|
func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
 | 
						|
	log[name] = &Filter{lvl, writer}
 | 
						|
	return log
 | 
						|
}
 | 
						|
 | 
						|
/******* Logging *******/
 | 
						|
// Send a formatted log message internally
 | 
						|
func (log Logger) intLogf(lvl Level, format string, args ...interface{}) {
 | 
						|
	skip := true
 | 
						|
 | 
						|
	// Determine if any logging will be done
 | 
						|
	for _, filt := range log {
 | 
						|
		if lvl >= filt.Level {
 | 
						|
			skip = false
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if skip {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Determine caller func
 | 
						|
	pc, _, lineno, ok := runtime.Caller(2)
 | 
						|
	src := ""
 | 
						|
	if ok {
 | 
						|
		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
 | 
						|
	}
 | 
						|
 | 
						|
	msg := format
 | 
						|
	if len(args) > 0 {
 | 
						|
		msg = fmt.Sprintf(format, args...)
 | 
						|
	}
 | 
						|
 | 
						|
	// Make the log record
 | 
						|
	rec := &LogRecord{
 | 
						|
		Level:   lvl,
 | 
						|
		Created: time.Now(),
 | 
						|
		Source:  src,
 | 
						|
		Message: msg,
 | 
						|
	}
 | 
						|
 | 
						|
	// Dispatch the logs
 | 
						|
	for _, filt := range log {
 | 
						|
		if lvl < filt.Level {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		filt.LogWrite(rec)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Send a closure log message internally
 | 
						|
func (log Logger) intLogc(lvl Level, closure func() string) {
 | 
						|
	skip := true
 | 
						|
 | 
						|
	// Determine if any logging will be done
 | 
						|
	for _, filt := range log {
 | 
						|
		if lvl >= filt.Level {
 | 
						|
			skip = false
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if skip {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Determine caller func
 | 
						|
	pc, _, lineno, ok := runtime.Caller(2)
 | 
						|
	src := ""
 | 
						|
	if ok {
 | 
						|
		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
 | 
						|
	}
 | 
						|
 | 
						|
	// Make the log record
 | 
						|
	rec := &LogRecord{
 | 
						|
		Level:   lvl,
 | 
						|
		Created: time.Now(),
 | 
						|
		Source:  src,
 | 
						|
		Message: closure(),
 | 
						|
	}
 | 
						|
 | 
						|
	// Dispatch the logs
 | 
						|
	for _, filt := range log {
 | 
						|
		if lvl < filt.Level {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		filt.LogWrite(rec)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Send a log message with manual level, source, and message.
 | 
						|
func (log Logger) Log(lvl Level, source, message string) {
 | 
						|
	skip := true
 | 
						|
 | 
						|
	// Determine if any logging will be done
 | 
						|
	for _, filt := range log {
 | 
						|
		if lvl >= filt.Level {
 | 
						|
			skip = false
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if skip {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Make the log record
 | 
						|
	rec := &LogRecord{
 | 
						|
		Level:   lvl,
 | 
						|
		Created: time.Now(),
 | 
						|
		Source:  source,
 | 
						|
		Message: message,
 | 
						|
	}
 | 
						|
 | 
						|
	// Dispatch the logs
 | 
						|
	for _, filt := range log {
 | 
						|
		if lvl < filt.Level {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		filt.LogWrite(rec)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Logf logs a formatted log message at the given log level, using the caller as
 | 
						|
// its source.
 | 
						|
func (log Logger) Logf(lvl Level, format string, args ...interface{}) {
 | 
						|
	log.intLogf(lvl, format, args...)
 | 
						|
}
 | 
						|
 | 
						|
// Logc logs a string returned by the closure at the given log level, using the caller as
 | 
						|
// its source.  If no log message would be written, the closure is never called.
 | 
						|
func (log Logger) Logc(lvl Level, closure func() string) {
 | 
						|
	log.intLogc(lvl, closure)
 | 
						|
}
 | 
						|
 | 
						|
// Finest logs a message at the finest log level.
 | 
						|
// See Debug for an explanation of the arguments.
 | 
						|
func (log Logger) Finest(arg0 interface{}, args ...interface{}) {
 | 
						|
	const (
 | 
						|
		lvl = FINEST
 | 
						|
	)
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		log.intLogf(lvl, first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		log.intLogc(lvl, first)
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Fine logs a message at the fine log level.
 | 
						|
// See Debug for an explanation of the arguments.
 | 
						|
func (log Logger) Fine(arg0 interface{}, args ...interface{}) {
 | 
						|
	const (
 | 
						|
		lvl = FINE
 | 
						|
	)
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		log.intLogf(lvl, first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		log.intLogc(lvl, first)
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Debug is a utility method for debug log messages.
 | 
						|
// The behavior of Debug depends on the first argument:
 | 
						|
// - arg0 is a string
 | 
						|
//   When given a string as the first argument, this behaves like Logf but with
 | 
						|
//   the DEBUG log level: the first argument is interpreted as a format for the
 | 
						|
//   latter arguments.
 | 
						|
// - arg0 is a func()string
 | 
						|
//   When given a closure of type func()string, this logs the string returned by
 | 
						|
//   the closure iff it will be logged.  The closure runs at most one time.
 | 
						|
// - arg0 is interface{}
 | 
						|
//   When given anything else, the log message will be each of the arguments
 | 
						|
//   formatted with %v and separated by spaces (ala Sprint).
 | 
						|
func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
 | 
						|
	const (
 | 
						|
		lvl = DEBUG
 | 
						|
	)
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		log.intLogf(lvl, first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		log.intLogc(lvl, first)
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Trace logs a message at the trace log level.
 | 
						|
// See Debug for an explanation of the arguments.
 | 
						|
func (log Logger) Trace(arg0 interface{}, args ...interface{}) {
 | 
						|
	const (
 | 
						|
		lvl = TRACE
 | 
						|
	)
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		log.intLogf(lvl, first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		log.intLogc(lvl, first)
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Info logs a message at the info log level.
 | 
						|
// See Debug for an explanation of the arguments.
 | 
						|
func (log Logger) Info(arg0 interface{}, args ...interface{}) {
 | 
						|
	const (
 | 
						|
		lvl = INFO
 | 
						|
	)
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		log.intLogf(lvl, first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		log.intLogc(lvl, first)
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Warn logs a message at the warning log level and returns the formatted error.
 | 
						|
// At the warning level and higher, there is no performance benefit if the
 | 
						|
// message is not actually logged, because all formats are processed and all
 | 
						|
// closures are executed to format the error message.
 | 
						|
// See Debug for further explanation of the arguments.
 | 
						|
func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
 | 
						|
	const (
 | 
						|
		lvl = WARNING
 | 
						|
	)
 | 
						|
	var msg string
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		msg = fmt.Sprintf(first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		msg = first()
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
	log.intLogf(lvl, msg)
 | 
						|
	return errors.New(msg)
 | 
						|
}
 | 
						|
 | 
						|
// Error logs a message at the error log level and returns the formatted error,
 | 
						|
// See Warn for an explanation of the performance and Debug for an explanation
 | 
						|
// of the parameters.
 | 
						|
func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
 | 
						|
	const (
 | 
						|
		lvl = ERROR
 | 
						|
	)
 | 
						|
	var msg string
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		msg = fmt.Sprintf(first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		msg = first()
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
	log.intLogf(lvl, msg)
 | 
						|
	return errors.New(msg)
 | 
						|
}
 | 
						|
 | 
						|
// Critical logs a message at the critical log level and returns the formatted error,
 | 
						|
// See Warn for an explanation of the performance and Debug for an explanation
 | 
						|
// of the parameters.
 | 
						|
func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
 | 
						|
	const (
 | 
						|
		lvl = CRITICAL
 | 
						|
	)
 | 
						|
	var msg string
 | 
						|
	switch first := arg0.(type) {
 | 
						|
	case string:
 | 
						|
		// Use the string as a format string
 | 
						|
		msg = fmt.Sprintf(first, args...)
 | 
						|
	case func() string:
 | 
						|
		// Log the closure (no other arguments used)
 | 
						|
		msg = first()
 | 
						|
	default:
 | 
						|
		// Build a format string so that it will be similar to Sprint
 | 
						|
		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
 | 
						|
	}
 | 
						|
	log.intLogf(lvl, msg)
 | 
						|
	return errors.New(msg)
 | 
						|
}
 |