A WordPress admin plugin to Customize and secure your WordPress dashboard
I never got the email - now you can answer
Every message the site sends is recorded with recipient, subject, outcome and — the part that is usually missing — which plugin sent it. For form notifications you also get who wrote and from which IP address.The message body is never stored.
How WordPress email log works
The module hooks wp_mail at the lowest possible priority, so it sees the message exactly as WordPress receives it after any other plugin has changed it. If delivery fails, the row already written is updated with the error returned.
wp_mail() is a function, not a hook — there is no callback to inspect. The module walks back up the call stack, finds the first file belonging to a plugin or the theme, and turns the folder into the plugin's readable name. It answers the question people actually ask when a site sends forty emails a day.
When wp_mail runs during a form submission, the fields are still in the request: the module takes the first valid address that doesn't belong to the site, descending into nested fields too. It works with any form plugin and with hand-written forms, because it doesn't depend on their internal schemas.
For a contact form it is the person who wrote, because the send happens inside the same request. But cron and command-line emails have nobody behind them, and emails triggered by a dashboard action would carry the administrator's IP — writing that down would be a lie, so the field stays empty. The IP is read the same way geofencing reads it, so it respects the "behind a proxy" setting.
The message body is not stored, and the column that used to hold it is dropped from the table on upgrade: a column that once held password reset links and verification codes should not be left sitting there out of inertia.
Privacy: the log stores email addresses and IP addresses, which are personal data under the GDPR. Collection is switchable and the log can be emptied — mention it in your privacy policy with the retention you apply. Emergency way out: define('ADMIN3WEBBLUE_DISABLE_MAILLOG', true);.
Frequently asked
Five answers on what gets recorded, what deliberately doesn’t, and how to read an outcome.
Date and time, recipient, subject, outcome, any error message, the IP address of the request and the plugin that sent it. The module hooks WordPress’s wp_mail() function at the lowest possible priority, so it sees the message exactly as WordPress receives it after any other plugin has altered it.
If sending fails, the row already written is updated with the error returned. There are never two entries for the same message: one row, one outcome.
Every row in the WordPress email log names the originating plugin, and it is the field almost always missing elsewhere. wp_mail() is a function rather than a hook, so there is no callback to inspect: the module walks back up the call stack, finds the first file belonging to a plugin or the theme, and shows its readable name.
When a site sends forty messages a day across orders, contact forms and system notices, that is the only way to answer the real question: who sent this.
That the message was accepted by the mail server, not that it landed in the recipient’s inbox. Those are two different things and worth keeping apart: the plugin can only know the first, because it is all WordPress reports back.
So a green row plus a recipient insisting nothing arrived points to a deliverability problem, almost always missing or misaligned SPF and DKIM records, which send messages to spam or get them rejected upstream. The log tells you which side to look at, and that is half the work.
No, if you check the emails sent by WordPress, the column that used to hold it is dropped from the table on upgrade. That isn’t a missing feature but a decision: a column that once held password reset links and verification codes should not be left sitting there out of inertia.
What remains are the fields you need to diagnose a problem, without keeping the contents of your users’ correspondence. Worth remembering that email addresses and IPs are still personal data: document the processing in your privacy policy, with the retention period you actually apply.
Because the IP recorded is the one from the request during which the email went out. For a contact form that is the person who wrote, and it is useful. But messages generated by cron or the command line have nobody behind them, and ones triggered by a dashboard action would carry the administrator’s IP: writing it there would be a convenient lie, so the field stays empty.
The log keeps its ten thousand most recent entries in a dedicated, indexed table, can be cleared from the page with a confirmation, and can be switched off entirely with ADMIN3WEBBLUE_DISABLE_MAILLOG in wp-config.php.