Corndog for Craft CMS

Free · no editions, no licensing code

Which code sent that email?

A customer got two order confirmations, a password reset never arrived, and somewhere a plugin is emailing people nightly. Craft's mailer is handed a message, not a reason — so none of those questions have an answer once the send is over. Corndog logs every message that goes through that mailer, keeps what was in it, and reads the call stack at the moment of the send so the log can say which plugin, template, queue job or system message asked for it.

Corndog

Two commands, then nothing

There is no configuration step and no template change. From the moment it installs, every message that goes through Craft's mailer is logged — and Twig gets the log too, if you would rather build your own screen.

composer require justinholtweb/craft-corndog
php craft plugin/install corndog

{# Nothing needs to go in a template. What Twig gets, if you want it: #}
{% for message in craft.corndog.recent(10) %}
    {{ message.subject }} → {{ message.toLine }} ({{ message.statusLabel }})
{% endfor %}

{{ craft.corndog.failures(60) }}      {# failures in the last hour #}
{{ craft.corndog.summary(30).sent }}  {# sent in the last 30 days #}

# And over SSH, when the question is "is mail still going out?"
php craft corndog/email/stats --days=30
php craft corndog/email/test you@example.com

Features

Everything Check & Log Email keeps for its premium tier, and the part Craft itself cannot tell you.

One hook, every email

Corndog attaches to Craft's mailer rather than to any one plugin, so system messages, Commerce, Formie, Freeform, the contact form, a craft.app.mailer call in a template and anything installed next month all land in the same log.

  • Not a list of supported plugins — the place they all go through
  • Queue jobs and console sends are logged with their context

It names what sent it

The stack is read at the moment of the send, because that is the only place the reason still exists. Every row carries the plugin, the system message key, the template path, the queue job class, and the file and line.

  • “Craft Commerce” and “Template: newsletter/_weekly.twig”
  • The premium feature in the plugin this one answers to

Failures with the reason attached

Yii fires its after-send event only when the send returns, and Craft catches the transport exception and answers false — so the mail server's actual words are lost. Corndog stands between the mailer and the transport and keeps them.

  • 550 5.7.1 relay access denied, on the message itself
  • Optional alert when several sends fail inside a window

What was actually sent

The HTML body as the recipient saw it, the plain-text part, the headers, the transport, the Message-ID and the attachment names — with the body rendered in a sandboxed frame, because an email is a document written by somebody else.

  • Resend it with edits, or send a test with your own headers
  • Reading bodies is its own permission, because bodies hold reset links

Tracking you have to ask for

Open and click tracking are both off until you switch them on: knowing whether a named person opened an email is personal data in most of the world. Click targets are signed, unsubscribe links are never rewritten, and IPs are blurred by default.

  • data-corndog-ignore leaves any single link alone
  • An unsigned target is refused rather than redirected

Staging cannot mail your customers

One switch logs every message in full and delivers none of them — for the staging site restored from a production database, where the addresses in it are real. Or send everything to one address instead, and blind-copy an archive.

  • Retention in days and a hard row cap, trimmed by garbage collection
  • CSV export, a dashboard widget, and prune from the console

Frequently Asked Questions

The questions worth answering before you install it.

Install it before you need it

Free, for Craft CMS 5.3 and later. The email you will want to look at is the one that has already been sent — so the useful time to install a mail log is the day before something goes wrong with it.