diff --git a/README.md b/README.md index 191ebc6..45f2187 100644 --- a/README.md +++ b/README.md @@ -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") diff --git a/log.go b/log.go index a105f72..e00acee 100644 --- a/log.go +++ b/log.go @@ -57,6 +57,8 @@ var levelColor = map[Level]func(...interface{})string {LevelDebug: Debug, LevelI // 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 { logger *golog.Logger level Level @@ -64,11 +66,13 @@ type Logger struct { 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)} + 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 @@ -78,7 +82,7 @@ func NewFile(level Level, filename string) (*Logger, error) { 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)}, 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 std = New(LevelWarn) @@ -150,6 +154,14 @@ func (l *Logger) ExcludeFromPattern(pattern string) { l.excludeFromPatterns = append(l.excludeFromPatterns, pattern) } +func (l *Logger) AddPrivacyFilter(fn func(string)bool) { + l.privacyFilers = append(l.privacyFilers, fn) +} + +func (l *Logger) SetPrivacyFilterReplace(b bool) { + l.privacyFilterReplace = b +} + func (l *Logger) header(level Level) string { _, file, _, ok := runtime.Caller(3) if !ok { @@ -175,10 +187,36 @@ func (l *Logger) header(level Level) string { } } +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{}) { 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...)...)) } } @@ -214,6 +252,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...) @@ -262,4 +310,4 @@ func Warnln(v ...interface{}) { // Errorln outputs the variables at the Error level func Errorln(v ...interface{}) { std.Println(LevelError, v...) -} +} \ No newline at end of file