Skip to content
Snippets Groups Projects
logger.go 4.39 KiB
Newer Older
  • Learn to ignore specific revisions
  • Manuel Kieweg's avatar
    Manuel Kieweg committed
    package log
    
    import (
    	"fmt"
    	"io"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"os"
    
    	"runtime"
    	"strconv"
    	"strings"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"sync"
    	"time"
    )
    
    var logger *Logger
    var once sync.Once
    
    // Logger is a wrapper for log.Logger and provides
    // methods to enable and disable logging.
    type Logger struct {
    
    	DefaultWriter  io.Writer
    	LoglevelWriter map[Level]io.Writer
    
    	toSyslog       map[Level]bool
    
    	Loglevel       Level
    	lock           sync.Mutex
    	builder        strings.Builder
    }
    
    
    func (l *Logger) buildMessage(level Level, syslog bool, args ...interface{}) {
    	if !syslog {
    		l.builder.WriteString(time.Now().Format(time.RFC3339))
    	}
    
    	l.builder.WriteRune('\t')
    	l.builder.WriteString(prefix(level))
    	l.builder.WriteRune('\t')
    	function, line := callers()
    	functionSplitted := strings.SplitAfter(function, "/")
    	function = functionSplitted[len(functionSplitted)-1]
    	l.builder.WriteString(function)
    	l.builder.WriteRune(':')
    	l.builder.WriteString(strconv.Itoa(line))
    	l.builder.WriteRune('\t')
    	l.builder.WriteString(fmt.Sprint(args...))
    	l.builder.WriteRune('\n')
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    }
    
    func get() *Logger {
    	once.Do(func() {
    		logger = &Logger{
    
    			DefaultWriter:  os.Stderr,
    			LoglevelWriter: make(map[Level]io.Writer),
    
    			toSyslog:       make(map[Level]bool),
    
    			Loglevel:       INFO,
    			lock:           sync.Mutex{},
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		}
    	})
    	return logger
    }
    
    
    // Loglevel sets the verbosity of the logger
    // Defaults to INFO
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func Loglevel(level Level) {
    	l := get()
    	l.lock.Lock()
    	defer l.lock.Unlock()
    	l.Loglevel = level
    }
    
    
    // Output defines the output of the logger
    // Defaults to os.Stderr
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func Output(out io.Writer) {
    	l := get()
    	l.lock.Lock()
    	defer l.lock.Unlock()
    
    	l.DefaultWriter = out
    }
    
    
    // LoglevelOutput defines a special output
    // for a certain log level
    
    func LoglevelOutput(level Level, out io.Writer) {
    	l := get()
    	l.lock.Lock()
    	defer l.lock.Unlock()
    	l.LoglevelWriter[level] = out
    
    	if reflect.TypeOf(out) == reflect.TypeOf(&syslog.Writer{}) {
    
    		l.toSyslog[level] = true
    	}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    }
    
    
    // Debug passes the DEBUG flag and a
    // message to the logger
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func Debug(args ...interface{}) {
    
    Malte Bauch's avatar
    Malte Bauch committed
    	log(DEBUG, args...)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    }
    
    
    // Info passes the INFO flag and a
    // message to the logger
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func Info(args ...interface{}) {
    
    Malte Bauch's avatar
    Malte Bauch committed
    	log(INFO, args...)
    
    // Warn passes the WARNING flag and a
    // message to the logger
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func Warn(args ...interface{}) {
    
    Malte Bauch's avatar
    Malte Bauch committed
    	log(WARNING, args...)
    
    // Error passes the ERROR flag and a
    // message to the logger
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func Error(args ...interface{}) {
    
    Malte Bauch's avatar
    Malte Bauch committed
    	log(ERROR, args...)
    
    // Fatal passes the FATAL flag and a
    // message to the logger and calls
    // os.Exit(1)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func Fatal(args ...interface{}) {
    
    Malte Bauch's avatar
    Malte Bauch committed
    	log(FATAL, args...)
    
    	os.Exit(1)
    
    // Debugf passes the DEBUG flag,
    // a formatted string and a
    // message to the logger
    func Debugf(format string, args ...interface{}) {
    	logf(DEBUG, format, args...)
    }
    
    // Infof passes the INFO flag,
    // a formatted string and and a
    // message to the logger
    func Infof(format string, args ...interface{}) {
    	logf(INFO, format, args...)
    }
    
    // Warnf passes the WARNING flag,
    // a formatted string and and a
    // message to the logger
    func Warnf(format string, args ...interface{}) {
    	logf(WARNING, format, args...)
    }
    
    // Errorf passes the ERROR flag,
    // a formatted string and and a
    // message to the logger
    func Errorf(format string, args ...interface{}) {
    	logf(ERROR, format, args...)
    }
    
    // Fatalf passes the FATAL flag,
    // a formatted string and and a
    // message to the logger and calls
    // os.Exit(1)
    func Fatalf(format string, args ...interface{}) {
    	logf(FATAL, format, args...)
    	os.Exit(1)
    }
    
    func logf(level Level, format string, args ...interface{}) {
    	log(level, fmt.Sprintf(format, args...))
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    }
    
    func log(level Level, args ...interface{}) {
    
    	defer func() {
    		if r := recover(); r != nil {
    			fmt.Println("Recovered in f", r)
    		}
    	}()
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	l := get()
    	l.lock.Lock()
    	defer l.lock.Unlock()
    
    	defer l.builder.Reset()
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	if level <= l.Loglevel {
    
    		l.buildMessage(level, l.toSyslog[level], args...)
    
    		msg := []byte(l.builder.String())
    		writer, ok := l.LoglevelWriter[level]
    		var err error
    		if !ok {
    			_, err = l.DefaultWriter.Write(msg)
    		} else {
    			_, err = writer.Write(msg)
    		}
    
    		if err != nil {
    			panic(err)
    		}
    
    func callers() (string, int) {
    
    	pc := make([]uintptr, 15)
    
    	n := runtime.Callers(5, pc)
    
    	frames := runtime.CallersFrames(pc[:n])
    	frame, _ := frames.Next()
    
    	return frame.Function, frame.Line
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func prefix(level Level) string {
    	switch level {
    	case FATAL:
    		return "FATAL"
    	case ERROR:
    		return "ERROR"
    	case WARNING:
    		return "WARNING"
    	case INFO:
    		return "INFO"
    	case DEBUG:
    		return "DEBUG"
    	}
    	return ""
    }