336 rivejä
9.0 KiB
Go
336 rivejä
9.0 KiB
Go
package log
|
|
|
|
import (
|
|
"fmt"
|
|
golog "log"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// Level indicates the level a log should be classified at
|
|
type Level int
|
|
|
|
// The possible levels a log can have
|
|
const (
|
|
LevelDebug Level = iota + 1
|
|
LevelInfo
|
|
LevelWarn
|
|
LevelError
|
|
)
|
|
|
|
/**** Color code from https://gist.github.com/ik5/d8ecde700972d4378d87 ****/
|
|
var (
|
|
Debug = Teal
|
|
Info = Green
|
|
Warn = Yellow
|
|
Error = Red
|
|
)
|
|
|
|
var (
|
|
Black = Color("\033[1;30m%s\033[0m")
|
|
Red = Color("\033[1;31m%s\033[0m")
|
|
Green = Color("\033[1;32m%s\033[0m")
|
|
Yellow = Color("\033[1;33m%s\033[0m")
|
|
Purple = Color("\033[1;34m%s\033[0m")
|
|
Magenta = Color("\033[1;35m%s\033[0m")
|
|
Teal = Color("\033[1;36m%s\033[0m")
|
|
White = Color("\033[1;37m%s\033[0m")
|
|
)
|
|
|
|
func Color(colorString string) func(...interface{}) string {
|
|
sprint := func(args ...interface{}) string {
|
|
return fmt.Sprintf(colorString,
|
|
fmt.Sprint(args...))
|
|
}
|
|
return sprint
|
|
}
|
|
|
|
/**** End color gist ****/
|
|
|
|
var levelString = map[Level]string{LevelDebug: "[DBUG]", LevelInfo: "[INFO]", LevelWarn: "[WARN]", LevelError: "[ERR ]"}
|
|
var levelColor = map[Level]func(...interface{})string {LevelDebug: Debug, LevelInfo: Info, LevelWarn: Warn, LevelError: Error}
|
|
|
|
|
|
// Logger is a go Logger wrapper that adds log levels and pattern filtering
|
|
// It allows high level 'log level' filtering broadly
|
|
// It allows allows two addition levels of filtering:
|
|
// everythingFrom which allows inclusion of packages/patterns along with general logging
|
|
// nothingExcept which allows focusing on just listed areas
|
|
// privacyFilters by default apply all functions to printf args and add warnings to them for matches
|
|
// privacyFilterReplace obscures any matches from the privacy filter functions
|
|
type Logger struct {
|
|
lock sync.Mutex // ensures atomic writes; protects the following fields
|
|
logger *golog.Logger
|
|
level Level
|
|
useColor bool
|
|
nothingExceptPatterns []string
|
|
everythingFromPatterns []string
|
|
excludeFromPatterns []string
|
|
privacyFilers []func(string)bool
|
|
privacyFilterReplace bool
|
|
}
|
|
|
|
// New returns a new Logger with a filter set to the supplied level
|
|
func New(level Level) *Logger {
|
|
return &Logger{logger: golog.New(os.Stderr, "", golog.Ldate|golog.Ltime), level: level, useColor: true, everythingFromPatterns: make([]string, 0), nothingExceptPatterns: make([]string, 0), privacyFilers: make([]func(string)bool, 0), privacyFilterReplace: false}
|
|
}
|
|
|
|
// NewFile returns a new Logger that logs to the supplied file with a filter set to the supplied level
|
|
func NewFile(level Level, filename string) (*Logger, error) {
|
|
logfile, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Logger{logger: golog.New(logfile, "", golog.Ldate|golog.Ltime), level: level, useColor: false, everythingFromPatterns: make([]string, 0), nothingExceptPatterns: make([]string, 0), privacyFilers: make([]func(string)bool, 0), privacyFilterReplace: false}, nil
|
|
}
|
|
|
|
var stdLock sync.Mutex
|
|
var std = New(LevelWarn)
|
|
|
|
// SetStd sets the default logger all other functions use
|
|
func SetStd(logger *Logger) {
|
|
stdLock.Lock()
|
|
defer stdLock.Unlock()
|
|
std = logger
|
|
}
|
|
|
|
// filter
|
|
func (l *Logger) filter(level Level) bool {
|
|
|
|
_, file, _, ok := runtime.Caller(3)
|
|
if !ok {
|
|
file = "???"
|
|
}
|
|
|
|
for _, pattern := range l.excludeFromPatterns {
|
|
if strings.Contains(file, pattern) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for _, pattern := range l.everythingFromPatterns {
|
|
if strings.Contains(file, pattern) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
for _, pattern := range l.nothingExceptPatterns {
|
|
if strings.Contains(file, pattern) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if len(l.nothingExceptPatterns) > 0 {
|
|
return false
|
|
}
|
|
|
|
return l.aboveLevel(level)
|
|
}
|
|
|
|
func (l *Logger) aboveLevel(level Level) bool {
|
|
return level >= l.level
|
|
}
|
|
|
|
// SetLevel adjusts the output filter by logging level
|
|
func (l *Logger) SetLevel(level Level) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.level = level
|
|
}
|
|
|
|
// SetUseColor toggles weather color output is used
|
|
func (l *Logger) SetUseColor(useColor bool) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.useColor = useColor
|
|
}
|
|
|
|
// AddNothingExceptFilter enables strong filtering showing logs only for things on the approved list, adding this pattern to the list
|
|
func (l *Logger) AddNothingExceptFilter(pattern string) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.nothingExceptPatterns = append(l.nothingExceptPatterns, pattern)
|
|
}
|
|
|
|
// AddEverythingFromPattern adds a pattern to skip log level filtering, guaranteeing all logs matching the pattern are seen
|
|
func (l *Logger) AddEverythingFromPattern(pattern string) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.everythingFromPatterns = append(l.everythingFromPatterns, pattern)
|
|
}
|
|
|
|
// ExcludeFromPattern adds a pattern to exclude logs from
|
|
func (l *Logger) ExcludeFromPattern(pattern string) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.excludeFromPatterns = append(l.excludeFromPatterns, pattern)
|
|
}
|
|
|
|
func (l *Logger) AddPrivacyFilter(fn func(string)bool) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.privacyFilers = append(l.privacyFilers, fn)
|
|
}
|
|
|
|
func (l *Logger) SetPrivacyFilterReplace(b bool) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
l.privacyFilterReplace = b
|
|
}
|
|
|
|
func (l *Logger) header(level Level) string {
|
|
_, file, _, ok := runtime.Caller(3)
|
|
if !ok {
|
|
file = "???"
|
|
} else {
|
|
short := file
|
|
count := 0
|
|
for i := len(file) - 1; i > 0; i-- {
|
|
if file[i] == '/' {
|
|
short = file[i+1:]
|
|
count++
|
|
if count == 2 {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
file = short
|
|
}
|
|
if l.useColor {
|
|
return file + " " + levelColor[level](levelString[level]) + " "
|
|
} else {
|
|
return file + " " + levelString[level] + " "
|
|
}
|
|
}
|
|
|
|
func (l *Logger) privacyFilter(v ...interface{}) []interface{} {
|
|
if len(l.privacyFilers) > 0 {
|
|
for i, val := range v {
|
|
for _, fn := range l.privacyFilers {
|
|
switch cval := val.(type) {
|
|
case string:
|
|
if fn(cval) {
|
|
if l.privacyFilterReplace {
|
|
cval = strings.Repeat("*", len(cval))
|
|
}
|
|
|
|
cval = "PRIVACY-FILTER-FLAGGED[" + cval + "]"
|
|
|
|
if l.useColor {
|
|
cval = Magenta(cval)
|
|
}
|
|
|
|
v[i] = cval
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return v
|
|
}
|
|
|
|
// Printf outputs the format and variables, assuming it passes the filter levels
|
|
func (l *Logger) Printf(level Level, format string, v ...interface{}) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
if l.filter(level) {
|
|
l.logger.Output(3, l.header(level)+fmt.Sprintf(format, l.privacyFilter(v...)...))
|
|
}
|
|
}
|
|
|
|
// Println outputs the variables assuming the filter levels are passed
|
|
func (l *Logger) Println(level Level, v ...interface{}) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
if l.filter(level) {
|
|
l.logger.Output(3, l.header(level)+fmt.Sprintln(v...))
|
|
}
|
|
}
|
|
|
|
// SetLevel changes the filtering level of the system logger
|
|
func SetLevel(level Level) {
|
|
std.SetLevel(level)
|
|
}
|
|
|
|
// SetUseColor toggles weather color output is used
|
|
func SetUseColor(useColor bool) {
|
|
std.useColor = useColor
|
|
}
|
|
|
|
// AddNothingExceptFilter enables strong filtering showing logs only for things on the approved list, adding this pattern to the list
|
|
func AddNothingExceptFilter(pattern string) {
|
|
std.AddNothingExceptFilter(pattern)
|
|
}
|
|
|
|
// AddEverythingFromPattern adds a pattern to skip log level filtering, guaranteeing all logs matching the pattern are seen
|
|
func AddEverythingFromPattern(pattern string) {
|
|
std.AddEverythingFromPattern(pattern)
|
|
}
|
|
|
|
// ExcludeFromPattern adds a pattern to exclude logs from
|
|
func ExcludeFromPattern(pattern string) {
|
|
std.ExcludeFromPattern(pattern)
|
|
}
|
|
|
|
// AddPrivacyFilter adds a function applied to printf argument strings that can flag them as sensitive data
|
|
func AddPrivacyFilter(fn func(string)bool) {
|
|
std.AddPrivacyFilter(fn)
|
|
}
|
|
|
|
// SetPrivacyFilterReplace replaces the string argument to printf that a Privacy Filter identified with ****s
|
|
func SetPrivacyFilterReplace(b bool) {
|
|
std.SetPrivacyFilterReplace(b)
|
|
}
|
|
|
|
// Printf outputs the format with variables assuming it passes the filter level
|
|
func Printf(level Level, format string, v ...interface{}) {
|
|
std.Printf(level, format, v...)
|
|
}
|
|
|
|
// Println outputs the varibles assuming they pass the filter level
|
|
func Println(level Level, v ...interface{}) {
|
|
std.Println(level, v...)
|
|
}
|
|
|
|
// Debugf outputs the format and variables at the Debug level
|
|
func Debugf(format string, v ...interface{}) {
|
|
std.Printf(LevelDebug, format, v...)
|
|
}
|
|
|
|
// Infof outputs the format and variables at the Info level
|
|
func Infof(format string, v ...interface{}) {
|
|
std.Printf(LevelInfo, format, v...)
|
|
}
|
|
|
|
// Warnf outputs the format and variables at the Warning level
|
|
func Warnf(format string, v ...interface{}) {
|
|
std.Printf(LevelWarn, format, v...)
|
|
}
|
|
|
|
// Errorf outputs the format and variables at the Error level
|
|
func Errorf(format string, v ...interface{}) {
|
|
std.Printf(LevelError, format, v...)
|
|
}
|
|
|
|
// Debugln outputs the variables at the Debug level
|
|
func Debugln(v ...interface{}) {
|
|
std.Println(LevelDebug, v...)
|
|
}
|
|
|
|
// Infoln outputs the variables at the Info level
|
|
func Infoln(v ...interface{}) {
|
|
std.Println(LevelInfo, v...)
|
|
}
|
|
|
|
// Warnln outputs the variables at the Warn level
|
|
func Warnln(v ...interface{}) {
|
|
std.Println(LevelWarn, v...)
|
|
}
|
|
|
|
// Errorln outputs the variables at the Error level
|
|
func Errorln(v ...interface{}) {
|
|
std.Println(LevelError, v...)
|
|
} |