log.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package log
  2. import (
  3. "context"
  4. "log/slog"
  5. "os"
  6. "runtime"
  7. )
  8. type ctxKey string
  9. const loggerKey ctxKey = "logger"
  10. var defaultLogger *slog.Logger
  11. func Init(env string) *slog.Logger {
  12. var handler slog.Handler
  13. if env == "production" {
  14. handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
  15. Level: slog.LevelInfo,
  16. AddSource: true,
  17. ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
  18. if a.Key == slog.TimeKey {
  19. return slog.Attr{Key: "timestamp", Value: a.Value}
  20. }
  21. if a.Key == slog.LevelKey {
  22. return slog.Attr{Key: "level", Value: a.Value}
  23. }
  24. if a.Key == slog.MessageKey {
  25. return slog.Attr{Key: "message", Value: a.Value}
  26. }
  27. return a
  28. },
  29. })
  30. } else {
  31. handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
  32. Level: slog.LevelDebug,
  33. AddSource: true,
  34. })
  35. }
  36. defaultLogger = slog.New(handler)
  37. slog.SetDefault(defaultLogger)
  38. return defaultLogger
  39. }
  40. func Default() *slog.Logger {
  41. if defaultLogger == nil {
  42. return slog.Default()
  43. }
  44. return defaultLogger
  45. }
  46. func FromContext(ctx context.Context) *slog.Logger {
  47. if logger, ok := ctx.Value(loggerKey).(*slog.Logger); ok {
  48. return logger
  49. }
  50. return Default()
  51. }
  52. func WithContext(ctx context.Context, logger *slog.Logger) context.Context {
  53. return context.WithValue(ctx, loggerKey, logger)
  54. }
  55. func WithRequestID(ctx context.Context, requestID string) *slog.Logger {
  56. return FromContext(ctx).With("request_id", requestID)
  57. }
  58. func WithUserID(ctx context.Context, userID string) *slog.Logger {
  59. return FromContext(ctx).With("user_id", userID)
  60. }
  61. func WithError(err error) slog.Attr {
  62. return slog.String("error", err.Error())
  63. }
  64. func WithFields(fields map[string]any) []slog.Attr {
  65. attrs := make([]slog.Attr, 0, len(fields))
  66. for k, v := range fields {
  67. attrs = append(attrs, slog.Any(k, v))
  68. }
  69. return attrs
  70. }
  71. func LogError(ctx context.Context, msg string, err error, args ...any) {
  72. logger := FromContext(ctx)
  73. allArgs := append([]any{"error", err}, args...)
  74. logger.ErrorContext(ctx, msg, allArgs...)
  75. }
  76. func LogWarn(ctx context.Context, msg string, args ...any) {
  77. FromContext(ctx).WarnContext(ctx, msg, args...)
  78. }
  79. func LogInfo(ctx context.Context, msg string, args ...any) {
  80. FromContext(ctx).InfoContext(ctx, msg, args...)
  81. }
  82. func LogDebug(ctx context.Context, msg string, args ...any) {
  83. FromContext(ctx).DebugContext(ctx, msg, args...)
  84. }
  85. func callerInfo(skip int) slog.Attr {
  86. _, file, line, ok := runtime.Caller(skip + 1)
  87. if !ok {
  88. return slog.String("caller", "unknown")
  89. }
  90. return slog.String("caller", file+":"+string(rune(line)))
  91. }