SquatchMail.Capture (SquatchMail v0.1.0)

Copy Markdown View Source

Observes every Swoosh.Mailer.deliver/2 and deliver_many/2 call in the host application via :telemetry, with zero changes to the host's mailer or adapter.

Swoosh.Mailer wraps each send in :telemetry.span/3, emitting [:swoosh, :deliver | :deliver_many, :start | :stop | :exception]. This module attaches to :stop and :exception only — never :start, since there's nothing to persist until a send has actually finished — and records the outcome through SquatchMail.Capture.Recorder.

Design constraints

Telemetry handlers run synchronously in the caller's process — the same process that just called Mailer.deliver/2. This module therefore:

  • never raises: every step is wrapped and logged on failure rather than propagated, so a SquatchMail bug can never break the host's mail sending;
  • never blocks the caller: the handler only builds attrs and hands them to SquatchMail.Capture.Recorder.record/1 (a GenServer.cast/2), returning immediately regardless of how long the actual database write takes, or whether the queue is full and the attrs get dropped.

Adapter result normalization

The result telemetry metadata is adapter-specific. This module recognizes:

Any other adapter's result is still recorded — just without a message_id, which means SES event ingestion won't be able to correlate delivery/bounce events back to it later. Unrecognized shapes are never treated as an error.

Configuration

See SquatchMail.Config for :enabled and the nested :capture options (:store_html, :store_text, :sample_rate, :max_queue).

Summary

Functions

Attaches the capture handler.

Detaches the capture handler.

Functions

attach()

@spec attach() :: :ok

Attaches the capture handler.

No-ops (logging nothing, doing nothing) when Swoosh isn't loaded — it's an optional dependency, and a host observing nothing through it is a valid configuration, not an error. Safe to call more than once: re-attaching detaches the previous handler first rather than erroring or double-firing.

detach()

@spec detach() :: :ok

Detaches the capture handler.