4
0

logging.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package server
  2. import (
  3. "context"
  4. "log/slog"
  5. "os"
  6. "strings"
  7. "github.com/mkaminski/goaim/oscar"
  8. )
  9. const (
  10. LevelTrace = slog.Level(-8)
  11. )
  12. var levelNames = map[slog.Leveler]string{
  13. LevelTrace: "TRACE",
  14. }
  15. func NewLogger(cfg Config) *slog.Logger {
  16. var level slog.Level
  17. switch strings.ToLower(cfg.LogLevel) {
  18. case "trace":
  19. level = LevelTrace
  20. case "debug":
  21. level = slog.LevelDebug
  22. case "warn":
  23. level = slog.LevelWarn
  24. case "error":
  25. level = slog.LevelError
  26. case "info":
  27. fallthrough
  28. default:
  29. level = slog.LevelInfo
  30. }
  31. opts := &slog.HandlerOptions{
  32. Level: level,
  33. ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
  34. if a.Key == slog.LevelKey {
  35. level := a.Value.Any().(slog.Level)
  36. levelLabel, exists := levelNames[level]
  37. if !exists {
  38. levelLabel = level.String()
  39. }
  40. a.Value = slog.StringValue(levelLabel)
  41. }
  42. return a
  43. },
  44. }
  45. return slog.New(Handler{slog.NewTextHandler(os.Stdout, opts)})
  46. }
  47. type Handler struct {
  48. slog.Handler
  49. }
  50. func (h Handler) Handle(ctx context.Context, r slog.Record) error {
  51. if sn := ctx.Value("screenName"); sn != nil {
  52. r.AddAttrs(slog.Attr{Key: "screenName", Value: slog.StringValue(sn.(string))})
  53. }
  54. if ip := ctx.Value("ip"); ip != nil {
  55. r.AddAttrs(slog.Attr{Key: "ip", Value: slog.StringValue(ip.(string))})
  56. }
  57. return h.Handler.Handle(ctx, r)
  58. }
  59. func (h Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
  60. return Handler{h.Handler.WithAttrs(attrs)}
  61. }
  62. func (h Handler) WithGroup(name string) slog.Handler {
  63. return h.Handler.WithGroup(name)
  64. }
  65. type RouteLogger struct {
  66. Logger *slog.Logger
  67. }
  68. func (rt RouteLogger) logRequestAndResponse(ctx context.Context, inFrame oscar.SnacFrame, inSNAC any, outFrame oscar.SnacFrame, outSNAC any) {
  69. msg := "client request -> server response"
  70. switch {
  71. case rt.Logger.Enabled(ctx, LevelTrace):
  72. rt.Logger.LogAttrs(ctx, LevelTrace, msg, SNACLogGroupWithPayload("request", inFrame, inSNAC),
  73. SNACLogGroupWithPayload("response", outFrame, outSNAC))
  74. case rt.Logger.Enabled(ctx, slog.LevelDebug):
  75. rt.Logger.LogAttrs(ctx, slog.LevelDebug, msg, SNACLogGroup("request", inFrame),
  76. SNACLogGroup("response", outFrame))
  77. }
  78. }
  79. func (rt RouteLogger) logRequest(ctx context.Context, inFrame oscar.SnacFrame, inSNAC any) {
  80. const msg = "client request"
  81. switch {
  82. case rt.Logger.Enabled(ctx, LevelTrace):
  83. rt.Logger.LogAttrs(ctx, LevelTrace, msg, SNACLogGroupWithPayload("request", inFrame, inSNAC))
  84. case rt.Logger.Enabled(ctx, slog.LevelDebug):
  85. rt.Logger.LogAttrs(ctx, slog.LevelDebug, msg, slog.Group("request", SNACLogGroup("request", inFrame)))
  86. }
  87. }
  88. func SNACLogGroup(key string, outFrame oscar.SnacFrame) slog.Attr {
  89. return slog.Group(key,
  90. slog.String("food_group", oscar.FoodGroupStr(outFrame.FoodGroup)),
  91. slog.String("sub_group", oscar.SubGroupStr(outFrame.FoodGroup, outFrame.SubGroup)),
  92. )
  93. }
  94. func SNACLogGroupWithPayload(key string, outFrame oscar.SnacFrame, outSNAC any) slog.Attr {
  95. return slog.Group(key,
  96. slog.String("food_group", oscar.FoodGroupStr(outFrame.FoodGroup)),
  97. slog.String("sub_group", oscar.SubGroupStr(outFrame.FoodGroup, outFrame.SubGroup)),
  98. slog.Any("snac_frame", outFrame),
  99. slog.Any("snac_payload", outSNAC),
  100. )
  101. }