Files
tgVideoCall/pkg/logger/logger.go
2026-04-05 18:09:28 +03:00

137 lines
3.8 KiB
Go
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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))
}