diff --git a/logs/zap/entry_encoder_test.go b/logs/zap/entry_encoder_test.go
index b65399764cb9f7d0d48d6f00a6f231b08e1f170b..052b13f7f85fc2f2a2848427767d3445077ac3b1 100644
--- a/logs/zap/entry_encoder_test.go
+++ b/logs/zap/entry_encoder_test.go
@@ -1 +1,75 @@
 package zap
+
+import (
+	"fmt"
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/items"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+func BenchmarkEntryEncoderSimple(b *testing.B) {
+	fields := []zapcore.Field{
+		logzap.Event(items.EventCreateItem),
+		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
+		logzap.Caller("/system"),
+		logzap.Tags("tag1", "tag2", "tag3"),
+	}
+
+	enc := NewEntryEncoder()
+	for i := 0; i < b.N; i++ {
+		_, _ = enc.EncodeEntry(zapcore.Entry{Message: fmt.Sprintf("Msg: %d", i)}, fields)
+	}
+}
+
+func BenchmarkEntryEncoderUnknownFields(b *testing.B) {
+	fields := []zapcore.Field{
+		logzap.Event(items.EventCreateItem),
+		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
+		logzap.Caller("/system"),
+		logzap.Tags("tag1", "tag2", "tag3"),
+	}
+
+	for i := 0; i < 1000; i++ {
+		fields = append(fields, zap.String(fmt.Sprintf("Key: %d", i), fmt.Sprintf("Value: %d", i)))
+	}
+
+	enc := NewEntryEncoder()
+	for i := 0; i < b.N; i++ {
+		_, _ = enc.EncodeEntry(zapcore.Entry{Message: fmt.Sprintf("Msg: %d", i)}, fields)
+	}
+}
+
+func BenchmarkEntryEncoderV2Simple(b *testing.B) {
+	fields := []zapcore.Field{
+		logzap.Event(items.EventCreateItem),
+		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
+		logzap.Caller("/system"),
+		logzap.Tags("tag1", "tag2", "tag3"),
+	}
+
+	enc := NewEntryEncoderV2()
+	for i := 0; i < b.N; i++ {
+		_, _ = enc.EncodeEntry(zapcore.Entry{Message: fmt.Sprintf("Msg: %d", i)}, fields)
+	}
+}
+
+func BenchmarkEntryEncoderV2UnknownFields(b *testing.B) {
+	fields := []zapcore.Field{
+		logzap.Event(items.EventCreateItem),
+		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
+		logzap.Caller("/system"),
+		logzap.Tags("tag1", "tag2", "tag3"),
+	}
+
+	for i := 0; i < 1000; i++ {
+		fields = append(fields, zap.String(fmt.Sprintf("Key: %d", i), fmt.Sprintf("Value: %d", i)))
+	}
+
+	enc := NewEntryEncoderV2()
+	for i := 0; i < b.N; i++ {
+		_, _ = enc.EncodeEntry(zapcore.Entry{Message: fmt.Sprintf("Msg: %d", i)}, fields)
+	}
+}
diff --git a/logs/zap/entry_encoder_v2.go b/logs/zap/entry_encoder_v2.go
new file mode 100644
index 0000000000000000000000000000000000000000..a73be0930a2a082e4bcc27f9218850e6d3e9395c
--- /dev/null
+++ b/logs/zap/entry_encoder_v2.go
@@ -0,0 +1,69 @@
+package zap
+
+import (
+	"fmt"
+	"slices"
+
+	oid "git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/logs"
+	"git.perx.ru/perxis/perxis-go/pkg/id"
+	"git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+type EncoderV2 interface {
+	With([]zapcore.Field) EncoderV2
+	EncodeEntry(zapcore.Entry, []zapcore.Field) (*logs.Entry, error)
+}
+
+type entryEncoderV2 struct {
+	fields []zapcore.Field
+}
+
+func NewEntryEncoderV2() EncoderV2 {
+	return &entryEncoderV2{}
+}
+
+func (enc *entryEncoderV2) With(fields []zapcore.Field) EncoderV2 {
+	return enc.with(fields)
+}
+
+func (enc *entryEncoderV2) with(fields []zapcore.Field) *entryEncoderV2 {
+	return &entryEncoderV2{fields: slices.Concat(enc.fields, fields)}
+}
+
+func (enc *entryEncoderV2) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*logs.Entry, error) {
+	ent := &logs.Entry{
+		ID:        id.GenerateNewID(),
+		Timestamp: entry.Time,
+		LogLevel:  logs.Level(entry.Level),
+		Message:   entry.Message,
+	}
+
+	clone := enc.with(fields)
+
+	for i := range clone.fields {
+		switch clone.fields[i].Key {
+		case "category":
+			ent.Category = clone.fields[i].String
+		case "component":
+			ent.Component = clone.fields[i].String
+		case "event":
+			ent.Event = clone.fields[i].String
+		case "object":
+			ent.ObjectID, _ = clone.fields[i].Interface.(*oid.ObjectId)
+		case "caller":
+			ent.CallerID, _ = clone.fields[i].Interface.(*oid.ObjectId)
+		case "attr":
+			ent.Attr = clone.fields[i].Interface
+		case "error":
+			if err, _ := clone.fields[i].Interface.(error); err != nil {
+				ent.Message = fmt.Sprintf("%s. Error: %s", ent.Message, err.Error())
+			}
+		case "tags":
+			ent.Tags, _ = clone.fields[i].Interface.(zap.StringArray)
+		}
+	}
+
+	return ent, nil
+}
diff --git a/zap/channels.go b/zap/channels.go
index 5753163514b1a91605e54e41adfdac711a2518b4..8726cc54ce4f8ab55ad27b469dff3809c68804fa 100644
--- a/zap/channels.go
+++ b/zap/channels.go
@@ -17,7 +17,7 @@ func ContainsChannels(channels ...string) FilterFunc {
 	return func(entry zapcore.Entry, fields []zapcore.Field) bool {
 		for _, f := range fields {
 			if f.Key == channelKey && f.Type == zapcore.SkipType {
-				for _, v := range f.Interface.(stringArray) {
+				for _, v := range f.Interface.(StringArray) {
 					if data.Contains(v, channels) {
 						return true
 					}
diff --git a/zap/field.go b/zap/field.go
index 308c21d6e83a4a80c35ab9866cb757e65c4b7a24..bccee93496f88404f2ca90332fcf3f1b89909104 100644
--- a/zap/field.go
+++ b/zap/field.go
@@ -10,9 +10,9 @@ import (
 	"go.uber.org/zap/zapcore"
 )
 
-type stringArray []string
+type StringArray []string
 
-func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+func (ss StringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
 	for i := range ss {
 		arr.AppendString(ss[i])
 	}
@@ -24,7 +24,7 @@ func Channels(channels ...string) zap.Field {
 	return zap.Field{
 		Key:       channelKey,
 		Type:      zapcore.SkipType, // используем тип zapcore.SkipType, чтобы при кодировании поле игнорировалось
-		Interface: stringArray(channels),
+		Interface: StringArray(channels),
 	}
 }
 
@@ -64,5 +64,5 @@ func Attr(attr any) zap.Field {
 }
 
 func Tags(tags ...string) zap.Field {
-	return zap.Strings("tags", tags)
+	return zap.Array("tags", StringArray(tags))
 }
diff --git a/zap/field_test.go b/zap/field_test.go
index 84efa584f85a66468e160334510e39ddee0246b0..8e81ce28cfc34a8861543c0338d5a0ec016e3066 100644
--- a/zap/field_test.go
+++ b/zap/field_test.go
@@ -19,8 +19,8 @@ func TestChannels(t *testing.T) {
 		field zap.Field
 		want  zap.Field
 	}{
-		{name: "ok", field: Channels("master"), want: zap.Field{Key: channelKey, Type: zapcore.SkipType, Interface: stringArray{"master"}}},
-		{name: "invalid", field: Channels(), want: zap.Field{Key: channelKey, Type: zapcore.SkipType, Interface: stringArray(nil)}},
+		{name: "ok", field: Channels("master"), want: zap.Field{Key: channelKey, Type: zapcore.SkipType, Interface: StringArray{"master"}}},
+		{name: "invalid", field: Channels(), want: zap.Field{Key: channelKey, Type: zapcore.SkipType, Interface: StringArray(nil)}},
 	}
 
 	for _, tc := range tests {