| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- // Package log предоставляет структурированное логирование на основе slog.
- // Поддерживает JSON-формат для production и текстовый для development.
- // Позволяет получать/сохранять логгер в контексте (FromContext/WithContext),
- // добавлять request_id, user_id, error и произвольные поля к записям.
- package log
- import (
- "context"
- "log/slog"
- "os"
- "runtime"
- "strconv"
- )
- type ctxKey string
- const loggerKey ctxKey = "logger"
- var defaultLogger *slog.Logger
- func Init(env string) *slog.Logger {
- var handler slog.Handler
- if env == "production" {
- handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
- Level: slog.LevelInfo,
- AddSource: true,
- ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
- if a.Key == slog.TimeKey {
- return slog.Attr{Key: "timestamp", Value: a.Value}
- }
- if a.Key == slog.LevelKey {
- return slog.Attr{Key: "level", Value: a.Value}
- }
- if a.Key == slog.MessageKey {
- return slog.Attr{Key: "message", Value: a.Value}
- }
- return a
- },
- })
- } else {
- handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
- Level: slog.LevelDebug,
- AddSource: true,
- })
- }
- defaultLogger = slog.New(handler)
- slog.SetDefault(defaultLogger)
- return defaultLogger
- }
- func Default() *slog.Logger {
- if defaultLogger == nil {
- return slog.Default()
- }
- return defaultLogger
- }
- func FromContext(ctx context.Context) *slog.Logger {
- if logger, ok := ctx.Value(loggerKey).(*slog.Logger); ok {
- return logger
- }
- return Default()
- }
- func WithContext(ctx context.Context, logger *slog.Logger) context.Context {
- return context.WithValue(ctx, loggerKey, logger)
- }
- func WithRequestID(ctx context.Context, requestID string) *slog.Logger {
- return FromContext(ctx).With("request_id", requestID)
- }
- func WithUserID(ctx context.Context, userID string) *slog.Logger {
- return FromContext(ctx).With("user_id", userID)
- }
- func WithError(err error) slog.Attr {
- return slog.String("error", err.Error())
- }
- func WithFields(fields map[string]any) []slog.Attr {
- attrs := make([]slog.Attr, 0, len(fields))
- for k, v := range fields {
- attrs = append(attrs, slog.Any(k, v))
- }
- return attrs
- }
- func LogError(ctx context.Context, msg string, err error, args ...any) {
- logger := FromContext(ctx)
- allArgs := append([]any{"error", err}, args...)
- logger.ErrorContext(ctx, msg, allArgs...)
- }
- func LogWarn(ctx context.Context, msg string, args ...any) {
- FromContext(ctx).WarnContext(ctx, msg, args...)
- }
- func LogInfo(ctx context.Context, msg string, args ...any) {
- FromContext(ctx).InfoContext(ctx, msg, args...)
- }
- func LogDebug(ctx context.Context, msg string, args ...any) {
- FromContext(ctx).DebugContext(ctx, msg, args...)
- }
- func callerInfo(skip int) slog.Attr {
- _, file, line, ok := runtime.Caller(skip + 1)
- if !ok {
- return slog.String("caller", "unknown")
- }
- return slog.String("caller", file+":"+strconv.Itoa(line))
- }
|