Laravel全局存储邮件通知日志及发送异常的实现方法
Laravel全局记录邮件发送信息与异常处理方案
完全可以实现,不用修改各处的Mail助手调用,利用Laravel自带的邮件事件系统就能全局统一处理邮件记录与异常捕获。
步骤1:创建邮件日志数据表
先生成迁移文件,创建用于存储邮件信息的表:
php artisan make:migration create_mail_logs_table
编辑迁移文件,添加所需字段:
public function up() { Schema::create('mail_logs', function (Blueprint $table) { $table->id(); $table->text('to')->comment('收件人'); $table->text('cc')->nullable()->comment('抄送'); $table->text('bcc')->nullable()->comment('密送'); $table->string('subject')->comment('邮件主题'); $table->longText('content')->nullable()->comment('邮件内容'); $table->enum('status', ['pending', 'success', 'failed'])->default('pending')->comment('发送状态'); $table->text('error_message')->nullable()->comment('异常信息'); $table->timestamps(); }); }
执行迁移:
php artisan migrate
步骤2:创建事件监听器
Laravel提供了三个核心邮件事件,针对每个事件创建对应监听器:
1. 监听邮件发送前事件(记录基础信息)
php artisan make:listener LogMailSending --event=Illuminate\Mail\Events\MessageSending
编辑app/Listeners/LogMailSending.php:
namespace App\Listeners; use Illuminate\Mail\Events\MessageSending; use Illuminate\Support\Facades\DB; class LogMailSending { public function handle(MessageSending $event) { $message = $event->message; // 处理收件人/抄送/密送的格式(Swift_Message返回数组,转成逗号分隔字符串) $to = collect($message->getTo())->keys()->implode(','); $cc = $message->getCc() ? collect($message->getCc())->keys()->implode(',') : null; $bcc = $message->getBcc() ? collect($message->getBcc())->keys()->implode(',') : null; // 插入日志记录 DB::table('mail_logs')->insert([ 'to' => $to, 'cc' => $cc, 'bcc' => $bcc, 'subject' => $message->getSubject(), 'content' => $message->getBody(), 'status' => 'pending', 'created_at' => now(), 'updated_at' => now(), ]); } }
2. 监听邮件发送成功事件(更新状态)
php artisan make:listener LogMailSent --event=Illuminate\Mail\Events\MessageSent
编辑app/Listeners/LogMailSent.php:
namespace App\Listeners; use Illuminate\Mail\Events\MessageSent; use Illuminate\Support\Facades\DB; class LogMailSent { public function handle(MessageSent $event) { $message = $event->message; $to = collect($message->getTo())->keys()->implode(','); // 更新对应记录的发送状态为成功 DB::table('mail_logs') ->where('to', $to) ->where('subject', $message->getSubject()) ->where('status', 'pending') ->update([ 'status' => 'success', 'updated_at' => now(), ]); } }
3. 监听邮件发送失败事件(记录异常)
php artisan make:listener LogMailFailed --event=Illuminate\Mail\Events\MessageFailed
编辑app/Listeners/LogMailFailed.php:
namespace App\Listeners; use Illuminate\Mail\Events\MessageFailed; use Illuminate\Support\Facades\DB; class LogMailFailed { public function handle(MessageFailed $event) { $message = $event->message; $to = collect($message->getTo())->keys()->implode(','); $error = $event->exception->getMessage(); // 更新对应记录为失败,并存储异常信息 DB::table('mail_logs') ->where('to', $to) ->where('subject', $message->getSubject()) ->where('status', 'pending') ->update([ 'status' => 'failed', 'error_message' => $error, 'updated_at' => now(), ]); } }
步骤3:注册事件与监听器
打开app/Providers/EventServiceProvider.php,在$listen数组中添加事件与监听器的映射:
protected $listen = [ \Illuminate\Mail\Events\MessageSending::class => [ \App\Listeners\LogMailSending::class, ], \Illuminate\Mail\Events\MessageSent::class => [ \App\Listeners\LogMailSent::class, ], \Illuminate\Mail\Events\MessageFailed::class => [ \App\Listeners\LogMailFailed::class, ], ];
补充说明
- 如果使用队列发送邮件,这些事件依然会正常触发,无需额外配置
- 若邮件内容是HTML格式,
getBody()会返回HTML代码;如果需要存储纯文本,可以用getBodyText() - 若担心收件人+主题的组合重复,可以在日志表中添加
message_id字段,存储$message->getId(),以此作为更精准的匹配依据
内容的提问来源于stack exchange,提问作者Kavan Prajapati
相关产品推荐
相关产品推荐

