package logging import ( "context" "errors" "log/slog" ) // MultiHandler sends log records to multiple handlers. type MultiHandler struct { handlers []slog.Handler } func NewMultiHandler(handlers ...slog.Handler) *MultiHandler { return &MultiHandler{handlers: handlers} } // Enabled reports whether any handler handles records at the given level. func (h *MultiHandler) Enabled(ctx context.Context, level slog.Level) bool { for _, handler := range h.handlers { if handler.Enabled(ctx, level) { return true } } return false } // Handle sends the record to all enabled handlers. // It continues processing all handlers even if one fails, and returns // a combined error of all failures using errors.Join. func (h *MultiHandler) Handle(ctx context.Context, r slog.Record) error { var errs []error for _, handler := range h.handlers { if handler.Enabled(ctx, r.Level) { if err := handler.Handle(ctx, r.Clone()); err != nil { errs = append(errs, err) } } } return errors.Join(errs...) } // WithAttrs returns a new MultiHandler with the given attributes added to all handlers. func (h *MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler { handlers := make([]slog.Handler, len(h.handlers)) for i, handler := range h.handlers { handlers[i] = handler.WithAttrs(attrs) } return &MultiHandler{handlers: handlers} } // WithGroup returns a new MultiHandler with the given group name applied to all handlers. func (h *MultiHandler) WithGroup(name string) slog.Handler { handlers := make([]slog.Handler, len(h.handlers)) for i, handler := range h.handlers { handlers[i] = handler.WithGroup(name) } return &MultiHandler{handlers: handlers} } var _ slog.Handler = (*MultiHandler)(nil)