Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions internal/deliverymq/messagehandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,9 @@ func (h *messageHandler) handleError(msg *mqs.Message, err error) error {
}
// Attempt errors from a destination's publish call (webhook 5xx, timeout,
// refused, etc.) are expected operational outcomes. Ack semantics are
// already decided above; the failure is captured in the audit log, the
// ClickHouse log entry, and the scheduled retry. Suppress propagation so
// the consumer doesn't log them as unexpected handler errors.
// already decided above; the failure is captured in the delivery.attempted
// line, the ClickHouse log entry, and the scheduled retry. Suppress
// propagation so the consumer doesn't log them as unexpected handler errors.
if atmErr, ok := err.(*AttemptError); ok {
var pubErr *destregistry.ErrDestinationPublishAttempt
if errors.As(atmErr.err, &pubErr) {
Expand All @@ -186,8 +186,8 @@ func (h *messageHandler) handleError(msg *mqs.Message, err error) error {
return err
}

// retryOutcome captures the retry-scheduling decisions made during a delivery
// attempt so they can be folded into the single delivery.attempted audit event.
// retryOutcome defers the retry-scheduling decisions taken during an attempt
// so logDeliveryResult can fold them into the one delivery.attempted line.
type retryOutcome struct {
scheduled bool
backoff time.Duration
Expand Down Expand Up @@ -278,10 +278,6 @@ func (h *messageHandler) logDeliveryResult(ctx context.Context, task *models.Del
attempt.AttemptNumber = task.Attempt
attempt.Manual = task.Manual

// Wide event: one audit per delivery attempt carrying the full outcome
// (attempt result, timing, retry decision). Replaces the separate
// "retry scheduled" and "scheduled retry canceled" audits so consumers
// don't have to join across lines to reconstruct what happened.
fields := []zap.Field{
zap.String("attempt_id", attempt.ID),
zap.String("event_id", task.Event.ID),
Expand Down
5 changes: 5 additions & 0 deletions internal/logging/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ func (l *Logger) Ctx(ctx context.Context) LoggerWithCtx {
}
}

// Audit records control-plane decisions: lifecycle changes a tenant or
// operator needs a durable record of.
//
// Per-unit-of-work outcomes on the hot path stay on Info however significant
// they are — this sink isn't sized for per-event volume.
func (l *Logger) Audit(msg string, fields ...zap.Field) {
l.auditLogger.Info(msg, fields...)
}
Expand Down
5 changes: 2 additions & 3 deletions internal/publishmq/eventhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,8 @@ func (h *eventHandler) Handle(ctx context.Context, event *models.Event) (*Handle
logger := h.logger.Ctx(ctx)
receivedAt := time.Now()

// Wide event state: populated by the rest of Handle and emitted as a single
// audit at the end. Replaces the separate "processing event" and per-
// destination "delivery task enqueued" audits.
// Wide event state: accumulated through Handle, emitted as a single
// event.received line by the defer below.
var enqueuedMu sync.Mutex
var enqueued []string
var matched []string
Expand Down
Loading