diff --git a/log/filter.go b/log/filter.go
new file mode 100644
index 000000000..de1ebf55f
--- /dev/null
+++ b/log/filter.go
@@ -0,0 +1,81 @@
+package log
+
+// FilterOption is filter option.
+type FilterOption func(*Filter)
+
+// FilterLevel with filter level.
+func FilterLevel(level Level) FilterOption {
+	return func(opts *Filter) {
+		opts.level = level
+	}
+}
+
+// FilterKey with filter key.
+func FilterKey(key ...string) FilterOption {
+	return func(o *Filter) {
+		for _, v := range key {
+			o.key[v] = struct{}{}
+		}
+	}
+}
+
+// FilterValue with filter value.
+func FilterValue(value ...string) FilterOption {
+	return func(o *Filter) {
+		for _, v := range value {
+			o.value[v] = struct{}{}
+		}
+	}
+}
+
+// FilterFunc with filter func.
+func FilterFunc(f func(level Level, keyvals ...interface{}) bool) FilterOption {
+	return func(o *Filter) {
+		o.filter = f
+	}
+}
+
+// Filter is a logger filter.
+type Filter struct {
+	logger Logger
+	level  Level
+	key    map[interface{}]struct{}
+	value  map[interface{}]struct{}
+	filter func(level Level, keyvals ...interface{}) bool
+}
+
+// NewFilter new a logger filter.
+func NewFilter(logger Logger, opts ...FilterOption) *Filter {
+	options := Filter{
+		logger: logger,
+		key:    make(map[interface{}]struct{}),
+		value:  make(map[interface{}]struct{}),
+	}
+	for _, o := range opts {
+		o(&options)
+	}
+	return &options
+}
+
+// Log Print log by level and keyvals.
+func (f *Filter) Log(level Level, keyvals ...interface{}) error {
+	if f.level > level {
+		return nil
+	}
+	if f.filter != nil && f.filter(level, keyvals...) {
+		return nil
+	}
+	for i := 0; i < len(keyvals); i += 2 {
+		if _, ok := f.key[keyvals[i]]; ok {
+			keyvals[i+1] = "***"
+		}
+		vi := i + 1
+		if vi >= len(keyvals) {
+			continue
+		}
+		if _, ok := f.value[keyvals[vi]]; ok {
+			keyvals[i+1] = "***"
+		}
+	}
+	return f.logger.Log(level, keyvals...)
+}
diff --git a/log/filter_test.go b/log/filter_test.go
new file mode 100644
index 000000000..386ee81cb
--- /dev/null
+++ b/log/filter_test.go
@@ -0,0 +1,90 @@
+package log
+
+import (
+	"io/ioutil"
+	"testing"
+)
+
+func TestFilterAll(t *testing.T) {
+	logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
+	log := NewHelper(NewFilter(logger,
+		FilterLevel(LevelDebug),
+		FilterKey("username"),
+		FilterValue("hello"),
+		FilterFunc(testFilterFunc),
+	))
+	log.Log(LevelDebug, "msg", "test debug")
+	log.Info("hello")
+	log.Infow("password", "123456")
+	log.Infow("username", "kratos")
+	log.Warn("warn log")
+}
+func TestFilterLevel(t *testing.T) {
+	logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
+	log := NewHelper(NewFilter(NewFilter(logger, FilterLevel(LevelWarn))))
+	log.Log(LevelDebug, "msg1", "te1st debug")
+	log.Debug("test debug")
+	log.Debugf("test %s", "debug")
+	log.Debugw("log", "test debug")
+	log.Warn("warn log")
+}
+
+func TestFilterCaller(t *testing.T) {
+	logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
+	log := NewFilter(logger)
+	log.Log(LevelDebug, "msg1", "te1st debug")
+	logHelper := NewHelper(NewFilter(logger))
+	logHelper.Log(LevelDebug, "msg1", "te1st debug")
+}
+
+func TestFilterKey(t *testing.T) {
+	logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
+	log := NewHelper(NewFilter(logger, FilterKey("password")))
+	log.Debugw("password", "123456")
+}
+
+func TestFilterValue(t *testing.T) {
+	logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
+	log := NewHelper(NewFilter(logger, FilterValue("debug")))
+	log.Debugf("test %s", "debug")
+}
+
+func TestFilterFunc(t *testing.T) {
+	logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
+	log := NewHelper(NewFilter(logger, FilterFunc(testFilterFunc)))
+	log.Debug("debug level")
+	log.Infow("password", "123456")
+}
+
+func BenchmarkFilterKey(b *testing.B) {
+	log := NewHelper(NewFilter(NewStdLogger(ioutil.Discard), FilterKey("password")))
+	for i := 0; i < b.N; i++ {
+		log.Infow("password", "123456")
+	}
+}
+
+func BenchmarkFilterValue(b *testing.B) {
+	log := NewHelper(NewFilter(NewStdLogger(ioutil.Discard), FilterValue("password")))
+	for i := 0; i < b.N; i++ {
+		log.Infow("password")
+	}
+}
+
+func BenchmarkFilterFunc(b *testing.B) {
+	log := NewHelper(NewFilter(NewStdLogger(ioutil.Discard), FilterFunc(testFilterFunc)))
+	for i := 0; i < b.N; i++ {
+		log.Info("password", "123456")
+	}
+}
+
+func testFilterFunc(level Level, keyvals ...interface{}) bool {
+	if level == LevelWarn {
+		return true
+	}
+	for i := 0; i < len(keyvals); i++ {
+		if keyvals[i] == "password" {
+			keyvals[i+1] = "***"
+		}
+	}
+	return false
+}
diff --git a/log/helper.go b/log/helper.go
index 2a2aceb43..7e6a25d12 100644
--- a/log/helper.go
+++ b/log/helper.go
@@ -25,7 +25,7 @@ func (h *Helper) WithContext(ctx context.Context) *Helper {
 	}
 }
 
-// Log .
+// Log Print log by level and keyvals.
 func (h *Helper) Log(level Level, keyvals ...interface{}) {
 	h.logger.Log(level, keyvals...)
 }
diff --git a/log/value.go b/log/value.go
index ea6338ebb..a472d77af 100644
--- a/log/value.go
+++ b/log/value.go
@@ -33,8 +33,13 @@ func Value(ctx context.Context, v interface{}) interface{} {
 func Caller(depth int) Valuer {
 	return func(context.Context) interface{} {
 		_, file, line, _ := runtime.Caller(depth)
+		if strings.LastIndex(file, "/log/filter.go") > 0 {
+			depth++
+			_, file, line, _ = runtime.Caller(depth)
+		}
 		if strings.LastIndex(file, "/log/helper.go") > 0 {
-			_, file, line, _ = runtime.Caller(depth + 1)
+			depth++
+			_, file, line, _ = runtime.Caller(depth)
 		}
 		idx := strings.LastIndexByte(file, '/')
 		return file[idx+1:] + ":" + strconv.Itoa(line)