Centralizing Email Logging in Laravel with a Custom EmailLogChannel
When building real-world Laravel applications, email handling quickly becomes scattered. You send emails from controllers, jobs, listeners, notifications… and suddenly, logging those emails (for debugging, auditing, or compliance) turns into repetitive boilerplate everywhere.
A cleaner approach?
Create a custom mail channel that logs every email from a single place.
In this post, we’ll implement a reusable EmailLogChannel in Laravel, walk through a complete use case, and show how it simplifies your architecture.
Problem: Scattered Email Logging:
<?php
Mail::to($user->email)->send(new WelcomeMail($user));
// Somewhere else...
EmailLog::create([
'to' => $user->email,
'subject' => 'Welcome',
'body' => '...',
]);
Now multiply that across:
- User registration
- Password reset
- Order confirmations
- Notifications
Result:
- Duplicate logic everywhere
- Easy to forget logging
- Hard to maintain
Solution: Custom EmailLogChannel
We’ll create a custom notification channel that:
- Sends the email
- Logs it automatically.
All from one centralized class.
Step 1: Create EmailLog Model & Migration
php artisan make:model EmailLog -m
Add following code in Migration file.
Schema::create('email_logs', function (Blueprint $table) {
$table->id();
$table->string('to');
$table->string('subject')->nullable();
$table->text('body')->nullable();
$table->string('status')->default('sent');
$table->timestamps();
});
Add fill ables in Model:
class EmailLog extends Model
{
protected $fillable = [
'to',
'subject',
'body',
'status',
];
}
Step 2: Create EmailLogChannel:
In your project root directory, run following command to create Channels folder.
mkdir app/Channels
Then create a file in Channels folder named EmailLogChannel and add following code.
namespace App\Channels;
use Illuminate\Support\Facades\Mail;
use App\Models\EmailLog;
class EmailLogChannel
{
public function send($notifiable, $notification)
{
if (!method_exists($notification, 'toMail')) {
return;
}
$mailMessage = $notification->toMail($notifiable);
// Send email
Mail::to($notifiable->email)->send($mailMessage->toMailable());
// Log email
EmailLog::create([
'to' => $notifiable->email,
'subject' => $mailMessage->subject ?? null,
'body' => $this->formatBody($mailMessage),
'status' => 'sent',
]);
}
protected function formatBody($mailMessage)
{
return collect($mailMessage->introLines)
->merge($mailMessage->outroLines)
->implode("\n");
}
}
Step 3: Create Notification
php artisan make:notification WelcomeUserNotification
Now add following code in notification class.
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use App\Channels\EmailLogChannel;
class WelcomeUserNotification extends Notification
{
public function via($notifiable)
{
return [EmailLogChannel::class];
}
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Welcome to Our Platform')
->greeting('Hello ' . $notifiable->name)
->line('Thank you for joining us!')
->line('We are glad to have you onboard.');
}
}
Step 4: Sample Use Case (User Registration)
As example here is the code for user registration controller method.
use App\Models\User;
use App\Notifications\WelcomeUserNotification;
public function register(Request $request)
{
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
$user->notify(new WelcomeUserNotification());
return response()->json(['message' => 'User registered successfully']);
}
What Just Happened?
Instead of:
Mail::send(...); EmailLog::create(...);
You now simply do:
$user->notify(new WelcomeUserNotification());
By implementing EmailLogChannel class, emails will be logged and there will be no any duplicate code. I hope this will help. Comment if you have any question.
