forked from lug/matterbridge
		
	
		
			
				
	
	
		
			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)
 | |
| }
 | 
