Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Maillog Laravel Package

tapp/filament-maillog

Filament plugin by Tapp that logs every outgoing Laravel email in your database and adds a Mail Log resource to your admin panel, letting you browse, search, preview, and resend messages with their attachments—handy for debugging delivery and auditing sent mail.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and publish

composer require tapp/filament-maillog
php artisan vendor:publish --tag="filament-maillog-migrations"
php artisan migrate

Optionally publish the config if you need to change retention, table name, or body storage:

php artisan vendor:publish --tag="filament-maillog-config"

2. Register the plugin on the panel

In your app/Providers/Filament/AdminPanelProvider.php:

use Tapp\FilamentMaillog\FilamentMaillogPlugin; // verify namespace in vendor/

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(FilamentMaillogPlugin::make());
}

3. Where to look first

  • vendor/tapp/filament-maillog/config/filament-maillog.php — retention days, table name, whether the HTML body/attachments are stored.
  • vendor/tapp/filament-maillog/src/ — the Resource class (columns, filters, actions) and the listener/observer that writes rows. That listener tells you exactly what is captured and when.
  • The published migration — confirms which columns exist and whether the body is longText/json.

4. First use case

Send one real email through Laravel's mailer (a password reset or Mail::raw() from php artisan tinker), then open the "Mail Log" (or similarly named) navigation item in your panel. That single loop — send, view, inspect headers/body — is the entire value proposition: debugging "did the email actually go out, and what did it contain?" without asking the recipient to forward it.

Implementation Patterns

Treat the log as a debugging tool, not an archive. The natural workflow is: a support ticket says "I never got the invite" → filter the log by recipient → confirm the message was handed to the transport, check the timestamp, open the rendered body. From there you can decide whether the problem is your app, the transport, or the recipient's spam filter.

Queue-aware verification. If you dispatch mail via queues, the log row appears when the worker actually sends, not when you dispatch. During local development with QUEUE_CONNECTION=sync you get instant rows; in staging with Redis you must watch the worker. Use the log's created_at versus your job's dispatch time to spot queue backlog.

Resending is the killer feature. Wrap the stored message in a Filament table action that re-sends to the same or a corrected address during support triage. Keep it restricted to super-admins — it's a footgun in production. Prefer a "resend to a different address" flow so you don't accidentally re-trigger "your password was reset" to the original user.

Panel scoping and multi-tenancy. If you have several panels, register the plugin only where it belongs (typically the admin panel, not a customer-facing one). For multi-tenant apps, check whether the resource applies a tenant scope; if the package doesn't support it out of the box, extend the resource's getEloquentQuery() to constrain by tenant_id — otherwise one tenant's admin can read another tenant's email bodies, which is a serious leak.

Permissions and policies. Don't rely on panel access alone. Gate the resource behind a policy or canViewAny() check tied to a dedicated permission (e.g. view_mail_logs). Email bodies routinely contain password-reset links, invoice data, and PII.

Retention as a scheduled job. Pair the package's retention config (if present) with a Schedule::command('model:prune') entry, or write a tiny scheduled cleanup. Logs grow fast: a modest app sending 5k emails/day with 30 KB HTML bodies produces ~150 MB/day of longText.

Integration with local debugging. Set MAIL_MAILER=log won't give you a UI; the point of this package is that it works against any mailer. Use it alongside Mailpit/HELO in local, and keep it enabled in staging so QA can self-serve — that's where it saves the most time.

Pair with your existing observability. Treat the mail log as the "content" layer and your APM/telescope as the "timing" layer. Query the log for the recipient, then cross-reference the send timestamp with queue metrics when diagnosing delays.

Gotchas and Tips

Mail::fake() writes nothing. In tests, faking the mailer short-circuits the send, so no MessageSent event fires and no log row is created. If you're asserting on log rows in feature tests, either send for real against the array transport or assert on Mail::assertSent() instead. This trips people up constantly.

Not everything is captured. The log almost certainly hooks Laravel's MessageSent event, which means: mail sent through Laravel's mailer (including notifications' mail channel) is logged; raw SMTP calls, third-party SDKs (SendGrid API directly, SES SDK), and mail sent from other services are invisible. Know the boundary before you trust the log as "all email."

Sync writes on the request path. If the listener writes synchronously, every send does an INSERT with a potentially large HTML body — noticeable in bulk-send loops. Check the config for a queued-logging option; if there isn't one, consider overriding the listener to dispatch a job or store bodies out-of-band.

Large bodies and search. Full-text searching a longText HTML column with LIKE '%…%' is slow and gets slower as the table grows. Search on subject/recipient and use date filters; add an index on to/created_at if the package's migration doesn't. If you need body search, that's a signals-you-should-export-to-a-log-service moment.

Rendering HTML safely. Email HTML expects to be its own document with inline styles and <style> blocks — dropping it into a Filament modal will wreck your panel's CSS. Good implementations render in a sandboxed <iframe> (via srcdoc). If this package renders inline, sanitize and isolate it yourself; also remember email HTML is attacker-influenced (a user can set their display name), so treat it as untrusted.

Attachments. Storing attachment blobs in the DB will bloat your database quickly. Check whether the package stores content, filenames only, or nothing. If it stores content, confirm the retention job actually deletes the rows (or the disk files if stored on a filesystem).

Pruning must be scheduled. Publishing a config with retention_days => 30 does nothing on its own — you need model:prune in your scheduler or the package's own command wired up. Verify in staging that php artisan schedule:list includes it.

Config and migration drift on upgrade. After composer update, re-publish migrations if the package adds columns (php artisan vendor:publish --tag="filament-maillog-migrations" --force then review the diff before running). Also run php artisan config:clear after editing the config — cached config silently ignores your retention change.

Filament version compatibility. This is a small package (≈30 stars); pin the major version in composer.json and check the release notes for the Filament v3/v4 split. Extension points are limited — expect to subclass the Resource class in app/Filament/Resources/ and register that instead of the package's, which is the standard escape hatch for changing columns, filters, and actions.

Local development noise. Every notification, password reset, and test email lands in the log on your dev machine. Keep retention short locally, or disable the plugin outside of staging/production via ->plugin(...) conditional on app()->environment().

License and maintenance. MIT, so forking is unrestricted. With a single-digit dependent count and a recent release date, budget for the possibility of lagging Filament upgrades — the resource class is small enough to vendor and maintain yourself if needed.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource