序
本文次要钻研一下 klog 的 LogFilter
LogFilter
k8s.io/klog/v2@v2.4.0/klog.go
// LogFilter is a collection of functions that can filter all logging calls,
// e.g. for sanitization of arguments and prevent accidental leaking of secrets.
type LogFilter interface {Filter(args []interface{}) []interface{}
FilterF(format string, args []interface{}) (string, []interface{})
FilterS(msg string, keysAndValues []interface{}) (string, []interface{})
}
func SetLogFilter(filter LogFilter) {logging.mu.Lock()
defer logging.mu.Unlock()
logging.filter = filter
}
LogFilter 接口定义了 Filter、FilterF、FilterS 办法用于过滤 log
filter.Filter
k8s.io/klog/v2@v2.4.0/klog.go
func (l *loggingT) println(s severity, logr logr.Logger, filter LogFilter, args ...interface{}) {buf, file, line := l.header(s, 0)
// if logr is set, we clear the generated header as we rely on the backing
// logr implementation to print headers
if logr != nil {l.putBuffer(buf)
buf = l.getBuffer()}
if filter != nil {args = filter.Filter(args)
}
fmt.Fprintln(buf, args...)
l.output(s, logr, buf, file, line, false)
}
func (l *loggingT) printDepth(s severity, logr logr.Logger, filter LogFilter, depth int, args ...interface{}) {buf, file, line := l.header(s, depth)
// if logr is set, we clear the generated header as we rely on the backing
// logr implementation to print headers
if logr != nil {l.putBuffer(buf)
buf = l.getBuffer()}
if filter != nil {args = filter.Filter(args)
}
fmt.Fprint(buf, args...)
if buf.Bytes()[buf.Len()-1] != '\n' {buf.WriteByte('\n')
}
l.output(s, logr, buf, file, line, false)
}
func (l *loggingT) printWithFileLine(s severity, logr logr.Logger, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) {buf := l.formatHeader(s, file, line)
// if logr is set, we clear the generated header as we rely on the backing
// logr implementation to print headers
if logr != nil {l.putBuffer(buf)
buf = l.getBuffer()}
if filter != nil {args = filter.Filter(args)
}
fmt.Fprint(buf, args...)
if buf.Bytes()[buf.Len()-1] != '\n' {buf.WriteByte('\n')
}
l.output(s, logr, buf, file, line, alsoToStderr)
}
println、printDepth、printWithFileLine 会通过 filter.Filter(args) 来过滤 args
filter.FilterF
k8s.io/klog/v2@v2.4.0/klog.go
func (l *loggingT) printf(s severity, logr logr.Logger, filter LogFilter, format string, args ...interface{}) {buf, file, line := l.header(s, 0)
// if logr is set, we clear the generated header as we rely on the backing
// logr implementation to print headers
if logr != nil {l.putBuffer(buf)
buf = l.getBuffer()}
if filter != nil {format, args = filter.FilterF(format, args)
}
fmt.Fprintf(buf, format, args...)
if buf.Bytes()[buf.Len()-1] != '\n' {buf.WriteByte('\n')
}
l.output(s, logr, buf, file, line, false)
}
printf 办法应用 filter.FilterF(format, args) 返回的 format 及 args 进行格式化
filter.FilterS
k8s.io/klog/v2@v2.4.0/klog.go
func (l *loggingT) infoS(loggr logr.Logger, filter LogFilter, msg string, keysAndValues ...interface{}) {
if filter != nil {msg, keysAndValues = filter.FilterS(msg, keysAndValues)
}
if loggr != nil {loggr.Info(msg, keysAndValues...)
return
}
l.printS(nil, msg, keysAndValues...)
}
func (l *loggingT) errorS(err error, loggr logr.Logger, filter LogFilter, msg string, keysAndValues ...interface{}) {
if filter != nil {msg, keysAndValues = filter.FilterS(msg, keysAndValues)
}
if loggr != nil {loggr.Error(err, msg, keysAndValues...)
return
}
l.printS(err, msg, keysAndValues...)
}
infoS 及 errorS 办法会应用 filter.FilterS(msg, keysAndValues) 返回的 msg 及 keysAndValues 进行打印
实例
type sampleLogFilter struct{}
func (f *sampleLogFilter) Filter(args []interface{}) []interface{} {
for i, arg := range args {v, ok := arg.(string)
if ok && strings.Contains(v, "filter me") {args[i] = "[FILTERED]"
}
}
return args
}
func (f *sampleLogFilter) FilterF(format string, args []interface{}) (string, []interface{}) {return strings.Replace(format, "filter me", "[FILTERED]", 1), f.Filter(args)
}
func (f *sampleLogFilter) FilterS(msg string, keysAndValues []interface{}) (string, []interface{}) {return strings.Replace(msg, "filter me", "[FILTERED]", 1), f.Filter(keysAndValues)
}
func TestLogFilter(t *testing.T) {setFlags()
defer logging.swap(logging.newBuffers())
SetLogFilter(&sampleLogFilter{})
defer SetLogFilter(nil)
funcs := []struct {
name string
logFunc func(args ...interface{})
severity severity
}{{
name: "Info",
logFunc: Info,
severity: infoLog,
}, {
name: "InfoDepth",
logFunc: func(args ...interface{}) {InfoDepth(1, args...)
},
severity: infoLog,
}, {
name: "Infoln",
logFunc: Infoln,
severity: infoLog,
}, {
name: "Infof",
logFunc: func(args ...interface{}) {Infof(args[0].(string), args[1:]...)
},
severity: infoLog,
}, {
name: "InfoS",
logFunc: func(args ...interface{}) {InfoS(args[0].(string), args[1:]...)
},
severity: infoLog,
}, {
name: "Warning",
logFunc: Warning,
severity: warningLog,
}, {
name: "WarningDepth",
logFunc: func(args ...interface{}) {WarningDepth(1, args...)
},
severity: warningLog,
}, {
name: "Warningln",
logFunc: Warningln,
severity: warningLog,
}, {
name: "Warningf",
logFunc: func(args ...interface{}) {Warningf(args[0].(string), args[1:]...)
},
severity: warningLog,
}, {
name: "Error",
logFunc: Error,
severity: errorLog,
}, {
name: "ErrorDepth",
logFunc: func(args ...interface{}) {ErrorDepth(1, args...)
},
severity: errorLog,
}, {
name: "Errorln",
logFunc: Errorln,
severity: errorLog,
}, {
name: "Errorf",
logFunc: func(args ...interface{}) {Errorf(args[0].(string), args[1:]...)
},
severity: errorLog,
}, {
name: "ErrorS",
logFunc: func(args ...interface{}) {ErrorS(errors.New("testerror"), args[0].(string), args[1:]...)
},
severity: errorLog,
}, {name: "V().Info",
logFunc: func(args ...interface{}) {V(0).Info(args...)
},
severity: infoLog,
}, {name: "V().Infoln",
logFunc: func(args ...interface{}) {V(0).Infoln(args...)
},
severity: infoLog,
}, {name: "V().Infof",
logFunc: func(args ...interface{}) {V(0).Infof(args[0].(string), args[1:]...)
},
severity: infoLog,
}, {name: "V().InfoS",
logFunc: func(args ...interface{}) {V(0).InfoS(args[0].(string), args[1:]...)
},
severity: infoLog,
}, {name: "V().Error",
logFunc: func(args ...interface{}) {V(0).Error(errors.New("test error"), args[0].(string), args[1:]...)
},
severity: errorLog,
}, {name: "V().ErrorS",
logFunc: func(args ...interface{}) {V(0).ErrorS(errors.New("test error"), args[0].(string), args[1:]...)
},
severity: errorLog,
}}
testcases := []struct {
name string
args []interface{}
expectFiltered bool
}{{args: []interface{}{"%s:%s", "foo", "bar"},
expectFiltered: false,
}, {args: []interface{}{"%s:%s", "foo", "filter me"},
expectFiltered: true,
}, {args: []interface{}{"filter me %s:%s", "foo", "bar"},
expectFiltered: true,
}}
for _, f := range funcs {
for _, tc := range testcases {logging.newBuffers()
f.logFunc(tc.args...)
got := contains(f.severity, "[FILTERED]", t)
if got != tc.expectFiltered {t.Errorf("%s filter application failed, got %v, want %v", f.name, got, tc.expectFiltered)
}
}
}
}
小结
klog 的 LogFilter 接口定义了 Filter、FilterF、FilterS 办法用于过滤 log;println、printDepth、printWithFileLine 会通过 filter.Filter(args) 来过滤 args;printf 办法应用 filter.FilterF(format, args) 返回的 format 及 args 进行格式化;infoS 及 errorS 办法会应用 filter.FilterS(msg, keysAndValues) 返回的 msg 及 keysAndValues 进行打印。
doc
- klog