| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- package util
- import (
- "context"
- "fmt"
- "log/slog"
- "reflect"
- "sync"
- opencode "github.com/sst/opencode-sdk-go"
- )
- func sanitizeValue(val any) any {
- if val == nil {
- return nil
- }
- if err, ok := val.(error); ok {
- return err.Error()
- }
- v := reflect.ValueOf(val)
- if v.Kind() == reflect.Interface && !v.IsNil() {
- return fmt.Sprintf("%T", val)
- }
- return val
- }
- type APILogHandler struct {
- client *opencode.Client
- service string
- level slog.Level
- attrs []slog.Attr
- groups []string
- mu sync.Mutex
- queue chan opencode.AppLogParams
- }
- func NewAPILogHandler(ctx context.Context, client *opencode.Client, service string, level slog.Level) *APILogHandler {
- result := &APILogHandler{
- client: client,
- service: service,
- level: level,
- attrs: make([]slog.Attr, 0),
- groups: make([]string, 0),
- queue: make(chan opencode.AppLogParams, 100_000),
- }
- go func() {
- for {
- select {
- case <-ctx.Done():
- return
- case params := <-result.queue:
- _, err := client.App.Log(context.Background(), params)
- if err != nil {
- slog.Error("Failed to log to API", "error", err)
- }
- }
- }
- }()
- return result
- }
- func (h *APILogHandler) Enabled(_ context.Context, level slog.Level) bool {
- return level >= h.level
- }
- func (h *APILogHandler) Handle(ctx context.Context, r slog.Record) error {
- var apiLevel opencode.AppLogParamsLevel
- switch r.Level {
- case slog.LevelDebug:
- apiLevel = opencode.AppLogParamsLevelDebug
- case slog.LevelInfo:
- apiLevel = opencode.AppLogParamsLevelInfo
- case slog.LevelWarn:
- apiLevel = opencode.AppLogParamsLevelWarn
- case slog.LevelError:
- apiLevel = opencode.AppLogParamsLevelError
- default:
- apiLevel = opencode.AppLogParamsLevelInfo
- }
- extra := make(map[string]any)
- h.mu.Lock()
- for _, attr := range h.attrs {
- val := attr.Value.Any()
- extra[attr.Key] = sanitizeValue(val)
- }
- h.mu.Unlock()
- r.Attrs(func(attr slog.Attr) bool {
- val := attr.Value.Any()
- extra[attr.Key] = sanitizeValue(val)
- return true
- })
- params := opencode.AppLogParams{
- Service: opencode.F(h.service),
- Level: opencode.F(apiLevel),
- Message: opencode.F(r.Message),
- }
- if len(extra) > 0 {
- params.Extra = opencode.F(extra)
- }
- h.queue <- params
- return nil
- }
- // WithAttrs returns a new Handler whose attributes consist of
- // both the receiver's attributes and the arguments.
- func (h *APILogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
- h.mu.Lock()
- defer h.mu.Unlock()
- newHandler := &APILogHandler{
- client: h.client,
- service: h.service,
- level: h.level,
- attrs: make([]slog.Attr, len(h.attrs)+len(attrs)),
- groups: make([]string, len(h.groups)),
- }
- copy(newHandler.attrs, h.attrs)
- copy(newHandler.attrs[len(h.attrs):], attrs)
- copy(newHandler.groups, h.groups)
- return newHandler
- }
- // WithGroup returns a new Handler with the given group appended to
- // the receiver's existing groups.
- func (h *APILogHandler) WithGroup(name string) slog.Handler {
- h.mu.Lock()
- defer h.mu.Unlock()
- newHandler := &APILogHandler{
- client: h.client,
- service: h.service,
- level: h.level,
- attrs: make([]slog.Attr, len(h.attrs)),
- groups: make([]string, len(h.groups)+1),
- }
- copy(newHandler.attrs, h.attrs)
- copy(newHandler.groups, h.groups)
- newHandler.groups[len(h.groups)] = name
- return newHandler
- }
|