Mail Delivery Logging
This module automatically logs all outgoing emails sent by your Drupal site, providing a detailed audit trail of recipients, content, and delivery status. It offers features like searchable logs, attachment archiving, bulk resending, a test mode to redirect emails, and PII redaction for privacy. This helps in debugging delivery issues, maintaining compliance, and ensuring critical communications are successfully sent.
Mail Delivery Logging intercepts, stores, and surfaces a full audit trail of every outgoing email sent by your Drupal 10 or 11 site — without changing a single existing hook_mail() call site. It decorates the active mail manager service, so every send is captured automatically: who it was sent to, what it contained, whether it was delivered, and why it failed if it wasn't. On top of that core logging capability sits a searchable admin log with summary statistics and failure charts, attachment archiving, queue-based bulk resend, a non-production "Send Test" reroute mode, PII redaction, inbound/outbound delivery-status webhooks, Drush tooling, and one-click .eml export. Whether you're debugging a broken SMTP relay, proving what was communicated to a customer, or alerting your on-call team the moment a password-reset email fails, Mail Delivery Logging turns Drupal's "fire and forget" mail system into a fully observable, recoverable, and auditable pipeline.
Features
Mail Interception & Logging
- Transparent mail interception — decorates
plugin.manager.mail(orMailsystemManagerwhen the Mailsystem module is installed) so everyhook_mail()-based send is captured with zero call-site changes anywhere in your codebase. - Full audit entity — a dedicated
email_logcontent entity stores sender/recipient/CC/BCC/Reply-To, subject, HTML and plain-text body, raw headers, delivery status, SMTP response, error category, mail backend/provider, retry count, and provider webhook event history. - Fallback safety-net logging —
hook_mail_alter()catches any email that somehow bypasses the primary logging path, so nothing sent through Drupal's mail system goes unrecorded. - Configurable retention — choose a preset (30/60/90/180 days, 1/2 years, forever) or a custom day count; old logs are purged automatically during cron or on demand with Drush.
- Configurable body logging level — None, Subject Only, or Full Content, so sites with strict data-minimisation requirements can log delivery metadata without ever persisting message bodies.
Delivery Intelligence & Webhooks
- Failure categorization — every failed send's SMTP response or exception message is parsed into
recipient_invalid,authentication,connection_timeout,quota_exceeded,spam_rejected,message_too_large,relay_denied, orother. - Mail backend / provider detection — records which transport handled each send (SMTP, SendGrid, Amazon SES, Mailgun, PHP
mail(), …) and tracks a per-log retry count across resends. - Inbound provider webhooks — accepts delivery-status callbacks from SendGrid, Mailgun, Amazon SES (via SNS), or a generic/custom relay, updating a log entry's status to Delivered, Bounced, Rejected, or Complained and appending to a JSON event history. Requests are authenticated with a rotating secret token, not a Drupal session.
- Outbound failure alerts — a Symfony event (
EmailDeliveryFailedEvent) fires on every failed send regardless of configuration; a built-in subscriber can also post ready-to-use Slack/Mattermost-compatible webhook notifications. - Dependency-free charting — a 30-day sent-vs-failed trend chart and a failure-category breakdown render as inline SVG/CSS with no external JS library and no CDN, so they work under any Content-Security-Policy.
Resend, Attachments & Testing
- Queue-based bulk resend — select any number of rows in the log list and resend or delete them in bulk. Resends are pushed onto a Queue API worker rather than sent synchronously, so bulk actions never risk a request timeout. No contributed module required — though Views Bulk Operations will pick up the same Action plugin if it's already on your site.
- Attachment archiving — a per-site policy (archive to
private://, keep filename/size metadata only, or ignore entirely) with a maximum-size guard against disk bloat. - Send Test / reroute mode — redirect all outgoing mail to a single debug address on staging or dev, with per-module exemptions and a subject-line prefix that shows the real intended recipient, so QA can verify content without ever emailing a real customer.
- .eml download & clipboard actions — download any logged message as a standards-compliant
.emlfile for opening in a desktop mail client, or copy its subject/HTML body straight to the clipboard for a bug report.
Privacy & Compliance
- Regex redaction — one PCRE pattern per line, applied to the subject, HTML body, plain-text body, and headers before anything is written to the database. The email actually delivered is never touched — only what is stored.
- Always-redact mail keys — mark specific
module_keycombinations (e.g. password-reset emails) so their entire body is replaced with a redaction notice, while subject and headers are preserved for triage. - Granular permissions — viewing that an email was sent is a separate permission from viewing its sensitive body, headers, and attachments, so support staff can triage delivery issues without exposure to message content.
Reporting & Views Integration
- Summary statistics atop the log list — Total / Sent / Failed / Resent / Success Rate stat cards sit directly above the searchable, sortable log you're already filtering.
- Searchable multi-select filters — Status, Drupal Module, and Mail Key are searchable multi-select dropdowns with removable chips, auto-populated from your actual log data; Date is a quick relative-time preset (Today, Last 7/30 Days, Last 1/2 Years, …).
- Saved filter presets — name and reuse a filter combination you check often, stored per-browser.
- Custom Views plugins — dedicated field, filter, and area plugins expose the
email_logtable to Views, so you can build your own reports on top of the same data. - CSV export — download any filtered result set from the web UI or via Drush, with the same date-range computation used by the on-screen filters so exports always match what you see.
Developer Experience
- Extension hooks —
hook_mail_delivery_logging_type_map_alter(),hook_mail_delivery_logging_resend_params_alter(), andhook_mail_delivery_logging_webhook_event_alter()let other modules integrate without a hard dependency. - Dispatched event — subscribe to
EmailDeliveryFailedEventto build custom alerting (PagerDuty, internal monitoring, etc.) beyond the built-in Slack/Mattermost webhook. - Injectable services —
EmailLoggerService,ResendEmailService,ErrorCategorizerService,AttachmentStorageService,EmailRedactionService,DeliveryStatsService, andEmailLogExportServiceare all registered in the service container for reuse. - Drush 12+ commands —
email-log:purge,email-log:resend, andemail-log:export, discovered automatically via PHP-attribute commands. - PHP 8.1+ typed properties and PSR-4 autoloading under the
Drupal\mail_delivery_loggingnamespace throughout.
Accessibility
- Searchable filter dropdowns are built as accessible comboboxes —
aria-haspopup,aria-expanded, and full keyboard support for opening and navigating the panel. - Removable filter chips carry a translated
aria-label(e.g. "Remove Failed") rather than a bare "×" glyph. - Every log-list column header is a real link, so sorting works with JavaScript disabled.
Post-Installation
After enabling the module, follow these steps to get email auditing running:
-
Run database updates and clear caches.
drush updb && drush cr— this creates theemail_logentity's base table and installs default configuration. -
Set permissions.
Go to People → Permissions and grant:view email audit logs— the log list, summary statistics, and per-email metadata.view email log content— the sensitive body, headers, and attachments of a logged email (requires the permission above; never granted automatically, even to roles that already hold it).export email audit logs— CSV downloads from the web UI.administer email audit— resend, delete, and change settings.
-
Confirm a private file path is configured.
Outgoing attachments are archived toprivate://email-logs/. Set$settings['file_private_path']insettings.php, or configure one at Configuration → Media → File system, before enabling the "Log" attachment policy. -
Review the settings form.
Go to Configuration → System → Email Audit Logs Settings (/admin/config/system/mail-delivery-logging) to adjust retention, body-logging level, attachment policy, reroute mode, redaction patterns, and outbound/inbound webhooks. -
Visit the log list.
Every email sent through\Drupal::service('plugin.manager.mail')->mail()is now logged automatically. Browse it at Reports → Email Audit Logs (/admin/reports/email-audit/logs), which opens with summary statistics and delivery charts above the searchable, filterable list. -
Try a Drush command.
drush email-log:export --status=failed --date-preset=30dexports every failed send from the last 30 days to a CSV file. Rundrush listafter enabling the module to see all availableemail-log:*commands.
For staging or QA environments, enable Reroute all outgoing email to a single debug address on the settings form so nobody accidentally emails a real customer while testing.
Additional Requirements
- Drupal 10.x or 11.x
- PHP 8.1 or higher
- Drupal core modules: User, Views, System, Options, Text, File (all included in Drupal core)
- Drush 12 or later, only if you want the
email-log:*commands (PHP-attribute command discovery). No other feature depends on Drush.
No third-party PHP libraries or JavaScript frameworks are required. Attachment archiving needs a private file system path configured; everything else works out of the box.
Recommended Modules / Libraries
-
Mailsystem —
When installed, Mail Delivery Logging automatically wrapsMailsystemManagerinstead of core'sMailManager, preserving all of Mailsystem's formatter/sender routing rules and recording its configured sender plugin as the Mail Backend/Provider on each log entry. -
SMTP Authentication Support —
Its on/off setting and configured host feed directly into this module's Mail Backend detection, so logged entries correctly show "SMTP" as the delivery transport. -
Views Bulk Operations (VBO) —
Not required — bulk resend/delete works out of the box via Drupal core's built-in bulk-form mechanism. If VBO is already installed, it will offer this module's same resend Action plugin in its own richer multi-step configuration UI. -
Queue UI —
Gives administrators a visual view of themail_delivery_logging_resendqueue between cron runs, useful when triaging a large bulk-resend operation. -
A Redis or Memcache cache backend — recommended on high-volume transactional sites so the admin log's summary-statistics queries stay fast as the
email_logtable grows.
Similar Projects
-
Maillog —
Maillog is a lightweight module focused purely on writing sent emails to the Drupal log (dblog) for debugging. Mail Delivery Logging instead persists every email as a queryable, filterable entity with its own admin UI, statistics, failure charting, attachment archiving, resend, and redaction — a complete audit system rather than a debug trace. -
Reroute Email —
Reroute Email does one thing well: redirecting all outgoing mail to a single address on non-production environments. Mail Delivery Logging includes an equivalent "Send Test" reroute mode as one feature among many — logging, resend, failure alerts, and redaction are all still active while reroute mode is on, giving you the same safety net plus full audit visibility in one module. -
Mailsystem —
Mailsystem is a pluggable framework for choosing which formatter and sender handle outgoing mail — it does not log, audit, or resend anything itself. Mail Delivery Logging is designed to sit on top of Mailsystem (detected and wrapped automatically) rather than replace it, adding the audit trail Mailsystem does not provide. -
Symfony Mailer (core, Drupal 11) —
Symfony Mailer is Drupal core's modern mail-sending transport layer, replacing the legacyMailManagerpipeline. Mail Delivery Logging is a logging/audit layer, not a transport — on sites where Symfony Mailer's own service structure is in use, integrating with it is on this module's roadmap.
Supporting This Module
Mail Delivery Logging currently lives as a custom module inside the Carepoint platform codebase and is not yet published as a standalone project on Drupal.org. If it's helpful on your own site once released, the best ways to support it are the same as any contributed module:
- Report bugs and suggest features in the project issue queue once published.
- Contribute patches, automated test coverage, or documentation improvements.
- Help validate the module against upcoming Drupal 10 and 11 minor releases.
When reporting an issue, please include your Drupal core version, PHP version, the steps to reproduce, and any relevant log output from Reports → Recent log messages filtered by the mail_delivery_logging channel.
Community Documentation
- Full installation, configuration, permissions, Feature Guide, Views integration, extension hook, Drush, theming, and accessibility documentation is available in the
README.mdfile included in the module. - The module's own
.modulefile implementshook_help(), so an in-context overview is also available at Help → Mail Delivery Logging once the module is enabled. - Example custom module code demonstrating the hook system (type-map overrides, resend param injection, webhook payload normalisation) is documented under "Extension Hooks" in the README.
Use Cases
Mail Delivery Logging is well suited for:
- Debugging delivery issues — see exactly what was sent, the raw SMTP response, and why a send failed, without digging through server logs.
- Compliance and audit trails — prove what was communicated to a user and when, for disputes, GDPR data requests, or support escalations.
- Recovering from outages — if an SMTP relay goes down for an hour, bulk-resend everything that failed during that window instead of losing those emails.
- Safe staging and QA testing — reroute all outgoing mail to a single debug inbox so QA can verify email content without spamming real users.
- Reducing support burden — "I never got my confirmation email" → look it up, confirm delivery status, resend in one click instead of re-running checkout or order logic.
- Ops and DevOps visibility — alert the team in Slack or PagerDuty the moment a critical email fails to deliver, instead of discovering it days later.