201 lines
4.8 KiB
Go
201 lines
4.8 KiB
Go
|
package log
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"os"
|
||
|
"runtime"
|
||
|
"strconv"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/mattn/go-colorable"
|
||
|
"github.com/mattn/go-isatty"
|
||
|
)
|
||
|
|
||
|
// scream so user fixes it
|
||
|
const warnImbalancedKey = "FIX_IMBALANCED_PAIRS"
|
||
|
const warnImbalancedPairs = warnImbalancedKey + " => "
|
||
|
const singleArgKey = "_"
|
||
|
|
||
|
func badKeyAtIndex(i int) string {
|
||
|
return "BAD_KEY_AT_INDEX_" + strconv.Itoa(i)
|
||
|
}
|
||
|
|
||
|
// DefaultLogLog is the default log for this package.
|
||
|
var DefaultLog Logger
|
||
|
|
||
|
// Suppress supresses logging and is useful to supress output in
|
||
|
// in unit tests.
|
||
|
//
|
||
|
// Example
|
||
|
// log.Suppress(true)
|
||
|
// defer log.suppress(false)
|
||
|
func Suppress(quiet bool) {
|
||
|
silent = quiet
|
||
|
}
|
||
|
|
||
|
var silent bool
|
||
|
|
||
|
// internalLog is the logger used by logxi itself
|
||
|
var InternalLog Logger
|
||
|
|
||
|
type loggerMap struct {
|
||
|
sync.Mutex
|
||
|
loggers map[string]Logger
|
||
|
}
|
||
|
|
||
|
var loggers = &loggerMap{
|
||
|
loggers: map[string]Logger{},
|
||
|
}
|
||
|
|
||
|
func (lm *loggerMap) set(name string, logger Logger) {
|
||
|
lm.loggers[name] = logger
|
||
|
}
|
||
|
|
||
|
// The assignment character between key-value pairs
|
||
|
var AssignmentChar = ": "
|
||
|
|
||
|
// Separator is the separator to use between key value pairs
|
||
|
//var Separator = "{~}"
|
||
|
var Separator = " "
|
||
|
|
||
|
const ltsvAssignmentChar = ":"
|
||
|
const ltsvSeparator = "\t"
|
||
|
|
||
|
// logxiEnabledMap maps log name patterns to levels
|
||
|
var logxiNameLevelMap map[string]int
|
||
|
|
||
|
// logxiFormat is the formatter kind to create
|
||
|
var logxiFormat string
|
||
|
|
||
|
var colorableStdout io.Writer
|
||
|
var defaultContextLines = 2
|
||
|
var defaultFormat string
|
||
|
var defaultLevel int
|
||
|
var defaultLogxiEnv string
|
||
|
var defaultLogxiFormatEnv string
|
||
|
var defaultMaxCol = 80
|
||
|
var defaultPretty = false
|
||
|
var defaultLogxiColorsEnv string
|
||
|
var defaultTimeFormat string
|
||
|
var disableCallstack bool
|
||
|
var disableCheckKeys bool
|
||
|
var disableColors bool
|
||
|
var home string
|
||
|
var isPretty bool
|
||
|
var isTerminal bool
|
||
|
var isWindows = runtime.GOOS == "windows"
|
||
|
var pkgMutex sync.Mutex
|
||
|
var pool = NewBufferPool()
|
||
|
var timeFormat string
|
||
|
var wd string
|
||
|
var pid = os.Getpid()
|
||
|
var pidStr = strconv.Itoa(os.Getpid())
|
||
|
|
||
|
// KeyMapping is the key map used to print built-in log entry fields.
|
||
|
type KeyMapping struct {
|
||
|
Level string
|
||
|
Message string
|
||
|
Name string
|
||
|
PID string
|
||
|
Time string
|
||
|
CallStack string
|
||
|
}
|
||
|
|
||
|
// KeyMap is the key map to use when printing log statements.
|
||
|
var KeyMap = &KeyMapping{
|
||
|
Level: "_l",
|
||
|
Message: "_m",
|
||
|
Name: "_n",
|
||
|
PID: "_p",
|
||
|
Time: "_t",
|
||
|
CallStack: "_c",
|
||
|
}
|
||
|
|
||
|
var logxiKeys []string
|
||
|
|
||
|
func setDefaults(isTerminal bool) {
|
||
|
var err error
|
||
|
contextLines = defaultContextLines
|
||
|
wd, err = os.Getwd()
|
||
|
if err != nil {
|
||
|
InternalLog.Error("Could not get working directory")
|
||
|
}
|
||
|
|
||
|
logxiKeys = []string{KeyMap.Level, KeyMap.Message, KeyMap.Name, KeyMap.Time, KeyMap.CallStack, KeyMap.PID}
|
||
|
|
||
|
if isTerminal {
|
||
|
defaultLogxiEnv = "*=WRN"
|
||
|
defaultLogxiFormatEnv = "happy,fit,maxcol=80,t=15:04:05.000000,context=-1"
|
||
|
defaultFormat = FormatHappy
|
||
|
defaultLevel = LevelWarn
|
||
|
defaultTimeFormat = "15:04:05.000000"
|
||
|
} else {
|
||
|
defaultLogxiEnv = "*=ERR"
|
||
|
defaultLogxiFormatEnv = "JSON,t=2006-01-02T15:04:05-0700"
|
||
|
defaultFormat = FormatJSON
|
||
|
defaultLevel = LevelError
|
||
|
defaultTimeFormat = "2006-01-02T15:04:05-0700"
|
||
|
disableColors = true
|
||
|
}
|
||
|
|
||
|
if isWindows {
|
||
|
home = os.Getenv("HOMEPATH")
|
||
|
if os.Getenv("ConEmuANSI") == "ON" {
|
||
|
defaultLogxiColorsEnv = "key=cyan+h,value,misc=blue+h,source=yellow,TRC,DBG,WRN=yellow+h,INF=green+h,ERR=red+h"
|
||
|
} else {
|
||
|
colorableStdout = NewConcurrentWriter(colorable.NewColorableStdout())
|
||
|
defaultLogxiColorsEnv = "ERR=red,misc=cyan,key=cyan"
|
||
|
}
|
||
|
// DefaultScheme is a color scheme optimized for dark background
|
||
|
// but works well with light backgrounds
|
||
|
} else {
|
||
|
home = os.Getenv("HOME")
|
||
|
term := os.Getenv("TERM")
|
||
|
if term == "xterm-256color" {
|
||
|
defaultLogxiColorsEnv = "key=cyan+h,value,misc=blue,source=88,TRC,DBG,WRN=yellow,INF=green+h,ERR=red+h,message=magenta+h"
|
||
|
} else {
|
||
|
defaultLogxiColorsEnv = "key=cyan+h,value,misc=blue,source=magenta,TRC,DBG,WRN=yellow,INF=green,ERR=red+h"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func isReservedKey(k interface{}) (bool, error) {
|
||
|
key, ok := k.(string)
|
||
|
if !ok {
|
||
|
return false, fmt.Errorf("Key is not a string")
|
||
|
}
|
||
|
|
||
|
// check if reserved
|
||
|
for _, key2 := range logxiKeys {
|
||
|
if key == key2 {
|
||
|
return true, nil
|
||
|
}
|
||
|
}
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
colorableStdout = NewConcurrentWriter(os.Stdout)
|
||
|
|
||
|
isTerminal = isatty.IsTerminal(os.Stdout.Fd())
|
||
|
|
||
|
// the internal logger to report errors
|
||
|
if isTerminal {
|
||
|
InternalLog = NewLogger3(NewConcurrentWriter(os.Stdout), "__logxi", NewTextFormatter("__logxi"))
|
||
|
} else {
|
||
|
InternalLog = NewLogger3(NewConcurrentWriter(os.Stdout), "__logxi", NewJSONFormatter("__logxi"))
|
||
|
}
|
||
|
InternalLog.SetLevel(LevelError)
|
||
|
|
||
|
setDefaults(isTerminal)
|
||
|
|
||
|
RegisterFormatFactory(FormatHappy, formatFactory)
|
||
|
RegisterFormatFactory(FormatText, formatFactory)
|
||
|
RegisterFormatFactory(FormatJSON, formatFactory)
|
||
|
ProcessEnv(readFromEnviron())
|
||
|
|
||
|
// package logger for users
|
||
|
DefaultLog = New("~")
|
||
|
}
|