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 {