关于golang:聊聊klog的LogFilter

4次阅读

共计 6357 个字符,预计需要花费 16 分钟才能阅读完成。

本文次要钻研一下 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
正文完
 0