【Laravel】メールの SMTP 通信ログを取る方法

  • 2021年10月26日
  • 2021年10月27日
  • Laravel, PHP

 しばしばメールの細部の通信ログが必要になる時があります。Laravelでこれをまとめて取れる様にするならば次のコードの方法があります。

<?php
// app/Providers/MailEchoLogServiceProvider.php
namespace App\Providers;

use Illuminate\Mail\Mailer;
use Illuminate\Support\ServiceProvider;
use Log;
use Swift_Plugins_Logger;
use Swift_Plugins_LoggerPlugin;

/**
 * Laravel 内のメーラーが SMTP 通信している内容をロギングするためのサービスプロバイダー
 *
 * ここ以外でこのロガーを使わない前提として、これ自身が Swift_Plugins_Logger インターフェースを満たすようにします。
 * コードをまとめられる点で便利ですが、サービスプロバイダーとメールロガーという違うインタフェースのクラスをまとめているため
 * どちらかのみの責務を果たそうしだすとコードが一気に散らかります(例.単にロガーとして使うためにこのクラスの外でこのクラスのインスタンスを扱う)。
 * そういった時は他にロガークラスを用意して、このクラス内ではメーラーへの登録のみにとどめます。
 * 必要になる貯めたログの読み出しなどはロガークラスの方に書いておいた方が良いです(Laravel の array キャッシュなど良さそうです)。
 */
class MailEchoLogServiceProvider extends ServiceProvider implements Swift_Plugins_Logger
{
    /**
     * Laravel起動時に実行される処理。ここでロガーを登録
     *
     * @return void
     */
    public function boot(): void
    {
        // Laravel のメーラーから順に送信実体である SwiftMailer を取り出す。
        /** @var Mailer $laravelMailer */
        $laravelMailer = \Mail::mailer();
        $swiftMailer = $laravelMailer->getSwiftMailer();

        // SwiftMailerにはロガーを仕込むためのメソッドがあり、それを差し込む
        // @see https://swiftmailer.symfony.com/docs/plugins.html#logger-plugin
        // このクラス自身が Swift_Plugins_Logger を満たしているロガークラスのため $this を渡す。
        $swiftMailer->registerPlugin(new Swift_Plugins_LoggerPlugin($this));
    }

    /**
     * 通信ログが追加される度に呼び出されるメソッド
     *
     * @param string $entry
     */
    public function add($entry)
    {
        // 手っ取り早く記録するならこれだけで十分。
        // Swift_Mailer が送って来たログを Laravel としてのログに転送
        Log::info($entry);
    }

    // ↑で済ますならば不要
    public function clear(){}
    public function dump(){}
}
// config/app.php
<?php

return [
    // 省略
    'providers' => [
        // 省略
        // 末尾に先述のサービスプロバイダーを追加
        App\Providers\MailEchoLogServiceProvider::class,
    ],
    // 省略
];

 これを用いることによって Laravel 内でメールが送られる度、次の様に SMTP 通信の中身が取れます。

[2021-10-26 18:07:40] local.INFO: ++ Starting Swift_SmtpTransport  
[2021-10-26 18:07:40] local.INFO: << 220 mailhog.example ESMTP MailHog
  
[2021-10-26 18:07:40] local.INFO: >> EHLO localhost
  
[2021-10-26 18:07:40] local.INFO: << 250-Hello localhost
250-PIPELINING
250 AUTH PLAIN
  
[2021-10-26 18:07:40] local.INFO: ++ Swift_SmtpTransport started  
[2021-10-26 18:07:40] local.INFO: >> MAIL FROM:
  
[2021-10-26 18:07:40] local.INFO: >> RCPT TO:<5Aszp0@example.com>
  
[2021-10-26 18:07:40] local.INFO: >> DATA
  
[2021-10-26 18:07:40] local.INFO: << 250 Sender test@test.example.com ok
  
[2021-10-26 18:07:40] local.INFO: << 250 Recipient 5Aszp0@example.com ok
  
[2021-10-26 18:07:40] local.INFO: << 354 End data with .
  
[2021-10-26 18:07:40] local.INFO: >> 
.
  
[2021-10-26 18:07:40] local.INFO: << 250 Ok: queued as J23nCC5MWK-sVRZkHrzLlQ8WFjVeyhF0bPIf-NmTtuQ=@mailhog.example
  
[2021-10-26 18:07:40] local.INFO: ++ Stopping Swift_SmtpTransport  
[2021-10-26 18:07:40] local.INFO: >> QUIT
  
[2021-10-26 18:07:40] local.INFO: << 221 Bye
  
[2021-10-26 18:07:40] local.INFO: ++ Swift_SmtpTransport stopped  

 例ではデフォルトのログ形式にしましたが、Laravel 自体のログ形式を変えるクラスを用意して各行がどのプロセスのものなのか把握できる様にするのが無難です。もしその様にせずに同時にメール送信が実行されると複数のSMTP通信ログが混ざってしっちゃかめっちゃかになります。
【PHP】【Laravel】Monolog のログフォーマットを操作する – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発
 まとめて1メッセージとなるログを取るならば、Swift_Mailer 用のロガークラスが add メソッドで貯めるログをシングルトン的なグローバルな領域に貯め、【Laravel】システム内で送信されたメールの eml ファイルを残す – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発で紹介したメール送信完了イベントなどの度にロガークラスの dump, clear メソッドを呼ぶのが良さそうです。ただこの案のみでは、メール送信失敗時のハンドリングができていなさそうですのでそれは注意と補完が必要です。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG