Compare commits

...

6 Commits

3 changed files with 127 additions and 5 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

View File

@ -8,6 +8,10 @@ A simple log primative Open Privacy uses
log.SetLevel(log.LevelDebug)
log.ExcludeFromPattern("connection/connection")
log.ExcludeFromPattern("outbound/3dhauthchannel")
log.AddPrivacyFilter(func(s string) bool {
return len(s) == 56
})
log.SetPrivacyFilterReplace(true)
log.Debugf("Creating the thing with param %v\n", param)
log.Infoln("Doing thing X")

127
log.go
View File

@ -6,6 +6,7 @@ import (
"os"
"runtime"
"strings"
"sync"
)
// Level indicates the level a log should be classified at
@ -19,24 +20,61 @@ const (
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, everythingFromPatterns: make([]string, 0), nothingExceptPatterns: make([]string, 0)}
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
@ -46,13 +84,16 @@ func NewFile(level Level, filename string) (*Logger, error) {
return nil, err
}
return &Logger{logger: golog.New(logfile, "", golog.Ldate|golog.Ltime), level: level, everythingFromPatterns: make([]string, 0), nothingExceptPatterns: make([]string, 0)}, nil
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
}
@ -95,24 +136,51 @@ func (l *Logger) aboveLevel(level Level) bool {
// 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 {
@ -131,18 +199,52 @@ func (l *Logger) header(level Level) string {
}
file = short
}
return file + " " + levelString[level] + " "
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, v...))
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...))
}
@ -153,6 +255,11 @@ 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)
@ -168,6 +275,16 @@ 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...)
@ -216,4 +333,4 @@ func Warnln(v ...interface{}) {
// Errorln outputs the variables at the Error level
func Errorln(v ...interface{}) {
std.Println(LevelError, v...)
}
}