From eea44197efffe19b953ab1eca8f3c5f675f4beff Mon Sep 17 00:00:00 2001 From: Semyon Krestyaninov <ensiouel@gmail.com> Date: Mon, 19 Feb 2024 15:04:32 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20zap=20core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- log/zap/channel_core.go | 43 +++++++++++++++++++++++++++++++++++ log/zap/channel_core_test.go | 44 ++++++++++++++++++++++++++++++++++++ log/zap/field.go | 8 +++++++ log/zap/filter_core.go | 28 +++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 log/zap/channel_core.go create mode 100644 log/zap/channel_core_test.go diff --git a/log/zap/channel_core.go b/log/zap/channel_core.go new file mode 100644 index 00000000..4eda6878 --- /dev/null +++ b/log/zap/channel_core.go @@ -0,0 +1,43 @@ +package zap + +import ( + "strings" + + "go.uber.org/zap/zapcore" +) + +const channelKey = "channel" + +func HasChannel(channel string) FilterFunc { + return func(entry zapcore.Entry, fields []zapcore.Field) bool { + for _, f := range fields { + if f.Key == channelKey && f.Type == zapcore.StringType { + list := strings.Split(f.String, ",") + for _, v := range list { + if v == channel { + return true + } + } + } + } + return false + } +} + +// NewDefaultChannelCore аналогичен NewChannelCore, но также устанавливает переданный канал в качестве канала по умолчанию. +// Это означает, что если поле Channel в записи не указано, запись все равно будет передана в zapcore.Core. +func NewDefaultChannelCore(core zapcore.Core, channel string) zapcore.Core { + return RegisterFilters( + core, + Or( + HasChannel(channel), + Not(HasKey(channelKey)), + ), + ) +} + +// NewChannelCore добавляет к переданному Core фильтрацию записей по каналам. +// Это означает, что если запись содержит поле Channel и значение соответствует переданному каналу, то запись будет передана в zapcore.Core. +func NewChannelCore(core zapcore.Core, channel string) zapcore.Core { + return RegisterFilters(core, HasChannel(channel)) +} diff --git a/log/zap/channel_core_test.go b/log/zap/channel_core_test.go new file mode 100644 index 00000000..af2317c3 --- /dev/null +++ b/log/zap/channel_core_test.go @@ -0,0 +1,44 @@ +package zap + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" +) + +func TestNewChannelCore_WriteSingleChannel(t *testing.T) { + core, logs := observer.New(zapcore.InfoLevel) + core = NewChannelCore(core, "test") + + core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{Channel("test")}) + core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{Channel("empty")}) // запись не попадет в лог + + require.Equal(t, 1, logs.Len()) +} + +func TestNewChannelCore_WriteMultiplyChannels(t *testing.T) { + core, logs := observer.New(zapcore.InfoLevel) + + core = zapcore.NewTee( + NewChannelCore(core, "test1"), + NewChannelCore(core, "test2"), + ) + + core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{Channel("test1", "test2")}) // запись попадет сразу в 2 core + + require.Equal(t, 2, logs.Len()) +} + +func TestNewDefaultChannelCore(t *testing.T) { + core, logs := observer.New(zapcore.InfoLevel) + + core = NewDefaultChannelCore(core, "test1") + + core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{Channel("test1")}) + core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{Channel("test2")}) // эта запись не попадет в лог + core.Write(zapcore.Entry{Message: "msg"}, []zapcore.Field{}) + + require.Equal(t, 2, logs.Len()) +} diff --git a/log/zap/field.go b/log/zap/field.go index acc6932d..b439f51b 100644 --- a/log/zap/field.go +++ b/log/zap/field.go @@ -2,6 +2,7 @@ package zap import ( "context" + "strings" "git.perx.ru/perxis/perxis-go/id" _ "git.perx.ru/perxis/perxis-go/id/system" // регистрируем обработчики для системных объектов @@ -9,6 +10,13 @@ import ( "go.uber.org/zap" ) +func Channel(channels ...string) zap.Field { + if len(channels) == 0 { + return zap.Skip() + } + return zap.String(channelKey, strings.Join(channels, ",")) +} + func Category(category string) zap.Field { if category == "" { return zap.Skip() diff --git a/log/zap/filter_core.go b/log/zap/filter_core.go index 1c6099e6..f60ba37d 100644 --- a/log/zap/filter_core.go +++ b/log/zap/filter_core.go @@ -24,6 +24,34 @@ func NotHasField(field zapcore.Field) FilterFunc { } } +func HasKey(key string) FilterFunc { + return func(entry zapcore.Entry, fields []zapcore.Field) bool { + for _, f := range fields { + if f.Key == key { + return true + } + } + return false + } +} + +func Or(filters ...FilterFunc) FilterFunc { + return func(entry zapcore.Entry, fields []zapcore.Field) bool { + for _, f := range filters { + if f(entry, fields) { + return true + } + } + return false + } +} + +func Not(filter FilterFunc) FilterFunc { + return func(entry zapcore.Entry, fields []zapcore.Field) bool { + return !filter(entry, fields) + } +} + type filterCore struct { zapcore.Core -- GitLab