You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:32:03