codeword/vendor/penahub.gitlab.yandexcloud.net/external/trashlog.git/wrappers/zaptg/zaptg.go
2024-08-27 22:09:10 +03:00

227 lines
4.6 KiB
Go

package zaptg
import (
"context"
"encoding/json"
"fmt"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
tb "gopkg.in/tucnak/telebot.v2"
pb "penahub.gitlab.yandexcloud.net/external/trashlog.git/proto/generated"
"strings"
)
type levelEnabler struct {
minLevel zapcore.Level
}
type TGCore struct {
levelEnabler
coreFields map[string]interface{}
ctx context.Context
bot *tb.Bot
svcData *pb.SvcData
ctxFields map[string]*pb.Value
keyFields map[string]*pb.Value
chatID int64
}
func NewCore(
ctx context.Context,
minLevel zapcore.Level,
token, version, commit string,
buildTime, chatID int64,
) (*TGCore, error) {
bot, err := tb.NewBot(tb.Settings{
Token: token,
})
if err != nil {
return nil, err
}
return &TGCore{
levelEnabler: levelEnabler{
minLevel: minLevel,
},
coreFields: make(map[string]interface{}),
ctx: ctx,
svcData: &pb.SvcData{
BuildTime: uint64(buildTime),
Version: version,
Commit: commit,
},
bot: bot,
chatID: chatID,
ctxFields: make(map[string]*pb.Value),
keyFields: make(map[string]*pb.Value),
}, nil
}
func (c *TGCore) WrapLogger(logger *zap.Logger) *zap.Logger {
return logger.WithOptions(
zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewTee(core, c)
}),
zap.AddCallerSkip(1),
)
}
func (le *levelEnabler) Enabled(l zapcore.Level) bool {
return l >= le.minLevel
}
func copyFieldsMap(src map[string]*pb.Value) map[string]*pb.Value {
dst := make(map[string]*pb.Value, len(src))
for k, v := range src {
dst[k] = v
}
return dst
}
func copyCoreMap(src map[string]any) map[string]any {
dst := make(map[string]any, len(src))
for k, v := range src {
dst[k] = v
}
return dst
}
func (c *TGCore) With(fields []zapcore.Field) zapcore.Core {
fieldMap := fieldsToMap(fields)
destCoreFields := copyCoreMap(c.coreFields)
destKeyFields := copyFieldsMap(c.keyFields)
destCtxFields := copyFieldsMap(c.ctxFields)
for k, v := range fieldMap {
destCoreFields[k] = v
switch {
case strings.HasPrefix(k, "Ctx"):
destCtxFields[strings.TrimPrefix(k, "Ctx")] = convertValue(v)
case strings.HasPrefix(k, "Key"):
destKeyFields[strings.TrimPrefix(k, "Key")] = convertValue(v)
default:
destKeyFields[k] = convertValue(v)
}
}
return &TGCore{
coreFields: destCoreFields,
ctxFields: destCtxFields,
keyFields: destKeyFields,
chatID: c.chatID,
svcData: c.svcData,
ctx: c.ctx,
bot: c.bot,
}
}
func convertValue(v interface{}) *pb.Value {
switch t := v.(type) {
case string:
return &pb.Value{Value: &pb.Value_Str{Str: t}}
case int64:
return &pb.Value{Value: &pb.Value_Num{Num: t}}
case float64:
return &pb.Value{Value: &pb.Value_Double{Double: float32(t)}}
case bool:
return &pb.Value{Value: &pb.Value_Flag{Flag: t}}
}
return nil
}
func (c *TGCore) Check(
entry zapcore.Entry,
checkedEntry *zapcore.CheckedEntry,
) *zapcore.CheckedEntry {
if c.levelEnabler.Enabled(entry.Level) {
return checkedEntry.AddCore(entry, c)
}
return checkedEntry
}
func (c *TGCore) Write(
entry zapcore.Entry,
fields []zapcore.Field,
) error {
fieldMap := fieldsToMap(fields)
splittedMessage := strings.Split(entry.Message, "!")
var msg string
if len(splittedMessage) == 2 {
msg = splittedMessage[1]
} else {
msg = entry.Message
}
keyFields, ctxFields := make(map[string]*pb.Value), make(map[string]*pb.Value)
for k, v := range c.keyFields {
keyFields[k] = v
}
for k, v := range c.ctxFields {
ctxFields[k] = v
}
for k, v := range fieldMap {
switch {
case strings.HasPrefix(k, "Ctx"):
ctxFields[strings.TrimPrefix(k, "Ctx")] = convertValue(v)
case strings.HasPrefix(k, "Key"):
keyFields[strings.TrimPrefix(k, "Key")] = convertValue(v)
default:
ctxFields[k] = convertValue(v)
}
}
dataKey, _ := json.Marshal(keyFields)
dataCtx, _ := json.Marshal(ctxFields)
if _, err := c.bot.Send(&tb.Chat{ID: c.chatID}, fmt.Sprintf(`
Событие: %s
Уровень: %s
Логгернейм: %s
Время события: %s
Версия: %s, Коммит: %s
Файл: %s
Ключевые: %s
Контекстные: %s
`,
msg,
entry.Level,
entry.LoggerName,
entry.Time.String(),
c.svcData.Version,
c.svcData.Commit,
entry.Caller.File,
string(dataKey),
string(dataCtx),
)); err != nil {
fmt.Println(err)
}
return nil
}
func (c *TGCore) Sync() error {
return nil
}
func fieldsToMap(fields []zapcore.Field) map[string]interface{} {
enc := zapcore.NewMapObjectEncoder()
for _, f := range fields {
f.AddTo(enc)
}
m := make(map[string]interface{})
for k, v := range enc.Fields {
m[k] = v
}
return m
}