ローカル環境におけるメール送信部分を確認する際に以前の記事でMailtrapMailhogを活用した方法を紹介していますが、もっと原始的且つ手っ取り早い方法としてLogに出力して確認するというオプションもあります。今回はそちらの方法をご紹介致します。

メールをログに出力する

メールをログに出力するにはメーラーにlogを指定するだけです。特定の場面においてlog出力に切り替えたいなら以下のようにmailer()を使って指定できます。過去の記事で紹介されたraw()を使えばMailableクラスを作成せずに簡単にテスト用のメールが送信できますよ。

Mail::mailer('log')->raw('This ia a test mail.', fn($m) => $m->subject('Test mail')->to('test@example.com'));

するとデフォルトの設定では、storage/logs/laravel.logに出力されるはずです。

[2024-01-21 02:23:12] local.DEBUG: From: Example <hello@example.com>
Subject: Test mail
To: test@example.com
MIME-Version: 1.0
Date: Sun, 21 Jan 2024 02:23:12 +0000
Message-ID: <2c6e264f57ffa38f94b5bee86bdb1493@example.com>
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

This ia a test mail.  

デフォルトのメーラーをlogに切り替えたい場合は.envMAIL_MAILERにlogを指定すればOKです。

...
#MAIL_MAILER=smtp    // デフォルトではsmtpが設定されていますね
MAIL_MAILER=log
...

そうすれば、mailer()で指定せずともlogメーラーが採用されます。

Mail::raw('This ia a test mail.', fn($m) => $m->subject('Test mail')->to('test@example.com'));

設定を理解する

何がどうしてlaravel.logに出力されたのか解説します。まず、MAIL_MAILERにlogを指定した場合、config/mail.phpにて以下の設定が使用されます。

...
'mailers' => [
...
        'log' => [
            'transport' => 'log',
            'channel' => env('MAIL_LOG_CHANNEL'),
        ],
...

出力先のログチャンネルを.envのMAIL_LOG_CHANNELから取得していますね。しかし、先ほどの実行時にはMAIL_LOG_CHANNELを定義していません。故にこちらの値はnullとなります。チャンネルの指定が無い場合、ログ出力においてはdefaultのチャンネルが採用されます。config/logging.phpを見てみましょう。

...
'default' => env('LOG_CHANNEL', 'stack'),    // ←①
...
    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['single'],    // ←②
            'ignore_exceptions' => false,
        ],

        'single' => [
            'driver' => 'single',
            'path' => storage_path('logs/laravel.log'),    // ←③
            'level' => env('LOG_LEVEL', 'debug'),
            'replace_placeholders' => true,
        ],
...

①defaultのチャンネルはstackです。②stackチャンネルではstackドライバが指定されており、ログの出力先を複数指定したい場合にそれらをバインドして指定できます。デフォルトの設定ではsingleチャンネルのみが指定されていました。そして、③このsingleチャンネルの出力先がlaravel.logと言うわけです。文章にするとやや複雑に感じますが、実際にconfigを辿るとそれほどでもありません。

出力先ログチャンネルを変更する

logメーラーを使用した際の出力先ログチャンネルを変更したい場合は、.envにおいてMAIL_LOG_CHANNELを指定すればOKです。試しにmail_debugログチャンネルを新たに作成し、そちらに出力してみましょう。まずは、config/logging.phpにおいて、channelsmail_debugチャンネルを追加します。

...
    'channels' => [
...
        'mail_debug' => [
            'driver'    => 'single',
            'path' => storage_path('logs/mail_debug.log'),
            'level' => env('LOG_LEVEL', 'debug'),
        ],
...

出力先のログファイルは、storage/logs/mail_debug.logとしました。次に、.envにおいてMAIL_LOG_CHANNELを指定します。

...
MAIL_LOG_CHANNEL=mail_debug
...

設定はこれだけです。メールを送信してmail_debug.logに出力されるか確認してみましょう。

Mail::raw('mail_log.logに出力されるはず。', fn($m) => $m->subject('Test mail')->to('test@example.com'));

mail_debug.logを確認すると意図通りにログが出力されているのが確認できるかと思います。

[2024-01-21 02:55:47] local.DEBUG: From: Example <hello@example.com>
Subject: Test mail
To: test@example.com
MIME-Version: 1.0
Date: Sun, 21 Jan 2024 02:55:47 +0000
Message-ID: <bbe1cccad557301198e97d3badb384f0@example.com>
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

mail_log.logに出力されるはず。

nullチャンネルの活用

ここからは少し応用編です。config/logging.phpの設定にnullと言うチャンネルがあります。

...
        'null' => [
            'driver' => 'monolog',
            'handler' => NullHandler::class,
        ],
...

これはログはどこにも出力せず破棄する設定です。以前から一体何に使うのだろう?と疑問でしたが先日、logメーラーと組み合わせる事で大量メールを送信する際のメール生成部分のパフォーマンステストに活用できる事が分かり、実際に以前の記事でkhino氏が紹介したDKIM署名のテストにおいてこれを活用しました。DKIM署名の記事はこちらです。

DKIM署名はMailableクラスのbuild()内で行うので、DKIM署名を行う場合と行わない場合でどのようにパフォーマンスが変わるのかを実際のメルマガと同じ送信件数でテストする必要がありました。ここでテストしたかったのはDKIM署名によりメール生成部分がボトルネックにならないか、のチェックであり送信部分ではありません。故に実際にメールを送信する必要は無く、生成したメールはlogメーラーでnullチャンネルに吐き出して破棄する方法を試みました。nullチャンネルを指定するには.envのMAIL_LOG_CHANNELにnullを指定するのですが、ここでも1つ工夫が必要でした。

...
MAIL_LOG_CHANNEL=null
...

このように.envに指定した場合、nullは文字列ではなくnull型として扱われてしまうのです。故に、出力先のチャンネルが未指定となりdefaultチャンネルが採用されlaravel.logに送信したメールが出力されてしまいます。そこで、config/logging.phpにてnullチャンネルをコピーしたsilentチャンネルを追加し、そちらを.envで指定するようにしました。

...
        'silent' => [
            'driver' => 'monolog',
            'handler' => NullHandler::class,
        ],
...
...
MAIL_LOG_CHANNEL=silent
...

今度は意図通り、送信したメールはどこにも出力されず破棄されました。かなりニッチな活用法ですが、覚えておくと他にも色々と応用が効くかもしれません。

By hikaru