skeleton added

This commit is contained in:
2026-02-08 19:56:16 +03:00
committed by KentoNion
commit f0b04b229a
19 changed files with 2021 additions and 0 deletions

136
pkg/logger/logger.go Executable file
View File

@@ -0,0 +1,136 @@
package logger
import (
"context"
"fmt"
"io"
"log"
"log/slog"
"os"
"runtime"
"strconv"
"tgVideoCall/pkg/config"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
const (
envLocal = "local"
envDev = "dev"
envProd = "prod"
)
func MustInitLogger(cfg config.Config) slog.Logger {
var logFile *os.File
var err error
if cfg.Log.FilePath != "" { //Если строка в конфиге пустая, это будет означать что нам не нужно сохранение логов в файл
logFile, err = os.OpenFile(cfg.Log.FilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal("error opening file:", err)
}
}
var log *slog.Logger
switch cfg.Env {
case envLocal:
if cfg.Log.FilePath == "" {
log = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
return *log
}
log = slog.New(slog.NewTextHandler(io.MultiWriter(os.Stdout, logFile), &slog.HandlerOptions{Level: slog.LevelDebug}))
case envProd:
if cfg.Log.FilePath == "" {
log = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
return *log
}
log = slog.New(slog.NewJSONHandler(io.MultiWriter(os.Stdout, logFile), &slog.HandlerOptions{Level: slog.LevelInfo}))
}
if cfg.Log.FilePath != "" {
log.Info(fmt.Sprintf("Logs are saving to: %s", cfg.Log.FilePath))
}
return *log
}
// ZapSlogHandler реализует slog.Handler для zap.Logger
type ZapSlogHandler struct {
zapLogger *zap.Logger
}
func NewZapSlogHandler(zapLogger *zap.Logger) *ZapSlogHandler {
return &ZapSlogHandler{zapLogger: zapLogger}
}
func (h *ZapSlogHandler) Enabled(ctx context.Context, level slog.Level) bool {
// Преобразуем slog.Level в zapcore.Level
var zapLevel zapcore.Level
switch {
case level < slog.LevelDebug:
zapLevel = zapcore.DebugLevel
case level < slog.LevelInfo:
zapLevel = zapcore.InfoLevel
case level < slog.LevelWarn:
zapLevel = zapcore.WarnLevel
case level < slog.LevelError:
zapLevel = zapcore.ErrorLevel
default:
zapLevel = zapcore.DPanicLevel
}
return h.zapLogger.Core().Enabled(zapLevel)
}
func (h *ZapSlogHandler) Handle(ctx context.Context, r slog.Record) error {
// Преобразуем slog.Record в поля zap
fields := make([]zap.Field, 0, r.NumAttrs()+1)
fields = append(fields, zap.String("message", r.Message))
// Добавляем атрибуты
r.Attrs(func(attr slog.Attr) bool {
fields = append(fields, zap.Any(attr.Key, attr.Value.Any()))
return true
})
// Добавляем источник (source), если есть
if r.PC != 0 {
fs := runtime.CallersFrames([]uintptr{r.PC})
f, _ := fs.Next()
fields = append(fields, zap.String("source", f.File+":"+strconv.FormatUint(uint64(r.PC), 10)))
}
// Логируем с соответствующим уровнем
switch r.Level {
case slog.LevelDebug:
h.zapLogger.Debug(r.Message, fields...)
case slog.LevelInfo:
h.zapLogger.Info(r.Message, fields...)
case slog.LevelWarn:
h.zapLogger.Warn(r.Message, fields...)
case slog.LevelError:
h.zapLogger.Error(r.Message, fields...)
default:
h.zapLogger.DPanic(r.Message, fields...)
}
return nil
}
func (h *ZapSlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
fields := make([]zap.Field, len(attrs))
for i, attr := range attrs {
fields[i] = zap.Any(attr.Key, attr.Value.Any())
}
return &ZapSlogHandler{zapLogger: h.zapLogger.With(fields...)}
}
func (h *ZapSlogHandler) WithGroup(name string) slog.Handler {
// Для простоты игнорируем группы, но в production нужно реализовать
return h
}
// WrapZapToSlog оборачивает zap.Logger в slog.Logger
func WrapZapToSlog(zapLogger *zap.Logger) *slog.Logger {
return slog.New(NewZapSlogHandler(zapLogger))
}