2

本文主要研究一下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


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...