func (gl *goLogger) Error(info, msg string) {
	log.Println("[ERROR]", info, msg)
}
Error is specified on the Logger interface.
package logger
import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"
)
type LogLevel int
const (
	LevelDebug LogLevel = iota
	LevelInfo
	LevelWarning
	LevelError
	LevelCritical
	LevelFatal
)
type FatalExiterFunc func()
func OsFatalExiter() {
	os.Exit(-1)
}
func PanicFatalExiter() {
	panic("program aborted after fatal situation, see log")
}
type FilterFunc func(level LogLevel, info, msg string) bool
var (
	logMux         sync.RWMutex
	logLevel       LogLevel        = LevelInfo
	logBackend     Logger          = NewStandardLogger(os.Stdout)
	logFatalExiter FatalExiterFunc = OsFatalExiter
	logFilter      FilterFunc
)
func Level() LogLevel {
	logMux.Lock()
	defer logMux.Unlock()
	return logLevel
}
func SetLevel(level LogLevel) LogLevel {
	logMux.Lock()
	defer logMux.Unlock()
	current := logLevel
	switch {
	case level <= LevelDebug:
		logLevel = LevelDebug
	case level >= LevelFatal:
		logLevel = LevelFatal
	default:
		logLevel = level
	}
	return current
}
func SetLevelString(levelstr string) LogLevel {
	logMux.Lock()
	defer logMux.Unlock()
	current := logLevel
	switch strings.ToLower(levelstr) {
	case "debug":
		logLevel = LevelDebug
	case "info":
		logLevel = LevelInfo
	case "warning":
		logLevel = LevelWarning
	case "error":
		logLevel = LevelError
	case "critical":
		logLevel = LevelCritical
	case "fatal":
		logLevel = LevelFatal
	}
	return current
}
func SetLogger(l Logger) Logger {
	logMux.Lock()
	defer logMux.Unlock()
	old := logBackend
	logBackend = l
	return old
}
func SetFatalExiter(fef FatalExiterFunc) FatalExiterFunc {
	logMux.Lock()
	defer logMux.Unlock()
	current := logFatalExiter
	logFatalExiter = fef
	return current
}
func SetFilter(ff FilterFunc) FilterFunc {
	logMux.Lock()
	defer logMux.Unlock()
	current := logFilter
	logFilter = ff
	return current
}
func UnsetFilter() FilterFunc {
	logMux.Lock()
	defer logMux.Unlock()
	current := logFilter
	logFilter = nil
	return current
}
func Debugf(format string, args ...interface{}) {
	logMux.RLock()
	defer logMux.RUnlock()
	if logLevel <= LevelDebug {
		info := retrieveCallInfo().verboseFormat()
		msg := fmt.Sprintf(format, args...)
		if shallLog(LevelDebug, info, msg) {
			logBackend.Debug(info, msg)
		}
	}
}
func Infof(format string, args ...interface{}) {
	logMux.RLock()
	defer logMux.RUnlock()
	if logLevel <= LevelInfo {
		info := retrieveCallInfo().shortFormat()
		msg := fmt.Sprintf(format, args...)
		if shallLog(LevelInfo, info, msg) {
			logBackend.Info(info, msg)
		}
	}
}
func Warningf(format string, args ...interface{}) {
	logMux.RLock()
	defer logMux.RUnlock()
	if logLevel <= LevelWarning {
		info := retrieveCallInfo().shortFormat()
		msg := fmt.Sprintf(format, args...)
		if shallLog(LevelWarning, info, msg) {
			logBackend.Warning(info, msg)
		}
	}
}
func Errorf(format string, args ...interface{}) {
	logMux.RLock()
	defer logMux.RUnlock()
	if logLevel <= LevelError {
		info := retrieveCallInfo().shortFormat()
		msg := fmt.Sprintf(format, args...)
		if shallLog(LevelError, info, msg) {
			logBackend.Error(info, msg)
		}
	}
}
func Criticalf(format string, args ...interface{}) {
	logMux.RLock()
	defer logMux.RUnlock()
	if logLevel <= LevelCritical {
		info := retrieveCallInfo().verboseFormat()
		msg := fmt.Sprintf(format, args...)
		if shallLog(LevelCritical, info, msg) {
			logBackend.Critical(info, msg)
		}
	}
}
func Fatalf(format string, args ...interface{}) {
	logMux.RLock()
	defer logMux.RUnlock()
	info := retrieveCallInfo().verboseFormat()
	msg := fmt.Sprintf(format, args...)
	logBackend.Fatal(info, msg)
	logFatalExiter()
}
type Logger interface {
	Debug(info, msg string)
	Info(info, msg string)
	Warning(info, msg string)
	Error(info, msg string)
	Critical(info, msg string)
	Fatal(info, msg string)
}
const defaultTimeFormat = "2006-01-02 15:04:05 Z07:00"
type standardLogger struct {
	mutex      sync.Mutex
	out        io.Writer
	timeFormat string
}
func NewTimeformatLogger(out io.Writer, timeFormat string) Logger {
	return &standardLogger{
		out:        out,
		timeFormat: timeFormat,
	}
}
func NewStandardLogger(out io.Writer) Logger {
	return NewTimeformatLogger(out, defaultTimeFormat)
}
func (sl *standardLogger) Debug(info, msg string) {
	sl.writeLog("DEBUG", info, msg)
}
func (sl *standardLogger) Info(info, msg string) {
	sl.writeLog("INFO", info, msg)
}
func (sl *standardLogger) Warning(info, msg string) {
	sl.writeLog("WARNING", info, msg)
}
func (sl *standardLogger) Error(info, msg string) {
	sl.writeLog("ERROR", info, msg)
}
func (sl *standardLogger) Critical(info, msg string) {
	sl.writeLog("CRITICAL", info, msg)
}
func (sl *standardLogger) Fatal(info, msg string) {
	sl.writeLog("FATAL", info, msg)
}
func (sl *standardLogger) writeLog(level, info, msg string) {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()
	io.WriteString(sl.out, time.Now().Format(sl.timeFormat))
	io.WriteString(sl.out, " [")
	io.WriteString(sl.out, level)
	io.WriteString(sl.out, "] ")
	io.WriteString(sl.out, info)
	io.WriteString(sl.out, " ")
	io.WriteString(sl.out, msg)
	io.WriteString(sl.out, "\n")
}
type goLogger struct{}
func NewGoLogger() Logger {
	return &goLogger{}
}
func (gl *goLogger) Debug(info, msg string) {
	log.Println("[DEBUG]", info, msg)
}
func (gl *goLogger) Info(info, msg string) {
	log.Println("[INFO]", info, msg)
}
func (gl *goLogger) Warning(info, msg string) {
	log.Println("[WARNING]", info, msg)
}