137 lines
3.8 KiB
Go
Executable File
137 lines
3.8 KiB
Go
Executable File
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))
|
||
}
|