apilogger.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package util
  2. import (
  3. "context"
  4. "log/slog"
  5. "sync"
  6. opencode "github.com/sst/opencode-sdk-go"
  7. )
  8. type APILogHandler struct {
  9. client *opencode.Client
  10. service string
  11. level slog.Level
  12. attrs []slog.Attr
  13. groups []string
  14. mu sync.Mutex
  15. }
  16. func NewAPILogHandler(client *opencode.Client, service string, level slog.Level) *APILogHandler {
  17. return &APILogHandler{
  18. client: client,
  19. service: service,
  20. level: level,
  21. attrs: make([]slog.Attr, 0),
  22. groups: make([]string, 0),
  23. }
  24. }
  25. func (h *APILogHandler) Enabled(_ context.Context, level slog.Level) bool {
  26. return level >= h.level
  27. }
  28. func (h *APILogHandler) Handle(ctx context.Context, r slog.Record) error {
  29. var apiLevel opencode.AppLogParamsLevel
  30. switch r.Level {
  31. case slog.LevelDebug:
  32. apiLevel = opencode.AppLogParamsLevelDebug
  33. case slog.LevelInfo:
  34. apiLevel = opencode.AppLogParamsLevelInfo
  35. case slog.LevelWarn:
  36. apiLevel = opencode.AppLogParamsLevelWarn
  37. case slog.LevelError:
  38. apiLevel = opencode.AppLogParamsLevelError
  39. default:
  40. apiLevel = opencode.AppLogParamsLevelInfo
  41. }
  42. extra := make(map[string]any)
  43. h.mu.Lock()
  44. for _, attr := range h.attrs {
  45. extra[attr.Key] = attr.Value.Any()
  46. }
  47. h.mu.Unlock()
  48. r.Attrs(func(attr slog.Attr) bool {
  49. extra[attr.Key] = attr.Value.Any()
  50. return true
  51. })
  52. params := opencode.AppLogParams{
  53. Service: opencode.F(h.service),
  54. Level: opencode.F(apiLevel),
  55. Message: opencode.F(r.Message),
  56. }
  57. if len(extra) > 0 {
  58. params.Extra = opencode.F(extra)
  59. }
  60. go func() {
  61. _, err := h.client.App.Log(context.Background(), params)
  62. if err != nil {
  63. // Fallback: we can't log the error using slog as it would create a loop
  64. // TODO: fallback file?
  65. }
  66. }()
  67. return nil
  68. }
  69. // WithAttrs returns a new Handler whose attributes consist of
  70. // both the receiver's attributes and the arguments.
  71. func (h *APILogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
  72. h.mu.Lock()
  73. defer h.mu.Unlock()
  74. newHandler := &APILogHandler{
  75. client: h.client,
  76. service: h.service,
  77. level: h.level,
  78. attrs: make([]slog.Attr, len(h.attrs)+len(attrs)),
  79. groups: make([]string, len(h.groups)),
  80. }
  81. copy(newHandler.attrs, h.attrs)
  82. copy(newHandler.attrs[len(h.attrs):], attrs)
  83. copy(newHandler.groups, h.groups)
  84. return newHandler
  85. }
  86. // WithGroup returns a new Handler with the given group appended to
  87. // the receiver's existing groups.
  88. func (h *APILogHandler) WithGroup(name string) slog.Handler {
  89. h.mu.Lock()
  90. defer h.mu.Unlock()
  91. newHandler := &APILogHandler{
  92. client: h.client,
  93. service: h.service,
  94. level: h.level,
  95. attrs: make([]slog.Attr, len(h.attrs)),
  96. groups: make([]string, len(h.groups)+1),
  97. }
  98. copy(newHandler.attrs, h.attrs)
  99. copy(newHandler.groups, h.groups)
  100. newHandler.groups[len(h.groups)] = name
  101. return newHandler
  102. }