diff --git a/log/zap/filter_core.go b/log/zap/filter_core.go
new file mode 100644
index 0000000000000000000000000000000000000000..1c6099e6baebacfed5d6bc21e844673d42d42534
--- /dev/null
+++ b/log/zap/filter_core.go
@@ -0,0 +1,84 @@
+package zap
+
+import (
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+type FilterFunc func(entry zapcore.Entry, fields []zapcore.Field) bool
+
+func HasField(field zapcore.Field) FilterFunc {
+	return func(_ zapcore.Entry, fields []zapcore.Field) bool {
+		for _, f := range fields {
+			if f.Equals(field) {
+				return true
+			}
+		}
+		return false
+	}
+}
+
+func NotHasField(field zapcore.Field) FilterFunc {
+	return func(entry zapcore.Entry, fields []zapcore.Field) bool {
+		return !HasField(field)(entry, fields)
+	}
+}
+
+type filterCore struct {
+	zapcore.Core
+
+	filters []FilterFunc
+
+	// fields хранит контекст записей ядра, передаваемых при вызове With.
+	// В методе Write передаются только поля конкретной записи, но мы также хотим учитывать поля контекста ядра.
+	fields []zap.Field
+}
+
+// RegisterFilters - добавить фильтры, которые будут применяться при записи лога (вызове `core.Write`)
+// Метод `core.Write` будет вызван только в случае, когда результат всех фильтров `true`
+//
+// Обратить внимание, фильтр не применяется к полям, которые были добавлены в `core` через вызов `core.With`
+// до вызова RegisterFilters. Пример:
+//
+//	l, _ := zap.NewDevelopment()
+//	core := l.Core().With([]zapcore.Field{zap.Int("a", 5)})
+//	core = RegisterFilters(core, HasField(zap.Int("a", 5)))
+//
+//	logger := zap.New(core)
+//	logger.Info("Test log") // НЕ будет записан
+//	logger.Info("Test log", zap.Int("a", 5)) // будет записан
+func RegisterFilters(core zapcore.Core, filters ...FilterFunc) zapcore.Core {
+	return &filterCore{
+		Core:    core,
+		filters: filters,
+	}
+}
+
+func (core *filterCore) With(fields []zapcore.Field) zapcore.Core {
+	return &filterCore{
+		Core:    core.Core.With(fields),
+		filters: core.filters,
+		fields:  append(core.fields, fields...),
+	}
+}
+
+func (core *filterCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry {
+	if core.Core.Enabled(entry.Level) {
+		return checkedEntry.AddCore(entry, core)
+	}
+	return checkedEntry
+}
+
+func (core *filterCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
+	if len(core.fields) > 0 {
+		fields = append(core.fields, fields...)
+	}
+
+	for _, filter := range core.filters {
+		if !filter(entry, fields) {
+			return nil
+		}
+	}
+
+	return core.Core.Write(entry, fields)
+}
diff --git a/log/zap/filter_core_test.go b/log/zap/filter_core_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2cf2cf01561ce7ee84a99ed3a420bc6a71026275
--- /dev/null
+++ b/log/zap/filter_core_test.go
@@ -0,0 +1,48 @@
+package zap
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"go.uber.org/zap/zaptest/observer"
+)
+
+func TestFilterCore_Write(t *testing.T) {
+	core, logs := observer.New(zapcore.InfoLevel)
+	core = RegisterFilters(core, HasField(zap.Bool("check", true)))
+
+	err := core.With([]zapcore.Field{zap.Bool("check", true)}).Write(zapcore.Entry{Message: "msg"}, nil)
+	require.NoError(t, err)
+
+	err = core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{zap.Bool("check", true)})
+	require.NoError(t, err)
+
+	err = core.Write(zapcore.Entry{Message: "msg"}, nil)
+	require.NoError(t, err)
+
+	require.Equal(t, 2, logs.Len())
+}
+
+func TestNotHasField(t *testing.T) {
+	core, logs := observer.New(zapcore.InfoLevel)
+	core = RegisterFilters(core, NotHasField(zap.Int("b", 2)))
+
+	err := core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{
+		zap.Int("a", 1),
+		zap.Int("b", 2),
+	})
+	require.NoError(t, err)
+
+	err = core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{
+		zap.Int("a", 1),
+		zap.Int("b", 3),
+	})
+	require.NoError(t, err)
+
+	err = core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{})
+	require.NoError(t, err)
+
+	require.Equal(t, 2, logs.Len())
+}