Ruby
Rails
Rails5

ActionMailer の送信ログをとりたい

More than 1 year has passed since last update.

たまたま Rails の ActionMailer について調べてみる機会がありましたので、そこに見られた Observer について書きたいと思います。

ご存知のように、メールの送信というものは失敗する可能性があります。ですので、できうるかぎりメールはユーザに届けたい!という場合は、再送の手間をかけなければいけないこともあるわけですが、そうした際に必要になってくるのは、そう、送信ログです。ActionMailer では簡単に送信ログがとれますので、今回はそれについて書いてみます。

たとえば次のようなメールアクションがあったとします。

app/mailers/foo_mailer.rb
class FooMailer < ActionMailer::Base
  def foo(recipient_email_address)
    mail(to: recipient_email_address, body: 'foo')
  end
end

このメールを送信するには、ご存知のように

FooMailer.foo('to@example.com').deliver_now

というふうにするわけですが、今回はログを取得したいので、これを実行するまえにあらかじめ ActiveSupport::Notification でサブスクライブしておきます。

ActiveSupport::Notification.subscribe("deliver.action_mailer") do |name, start, finish, id, payload|
  puts "name: #{name}"
  puts "payload: #{payload}"
end

こうしておくと、送信処理の直前に次のようなアウトプットが得られるようになります。

name: deliver.action_mailer
payload: {:mailer=>"FooMailer", :message_id=>nil, :subject=>"Foo", :to=>["to@example.com"], ...}

なお、 "deliver.action_mailer" は「送信時」(実際に送信する直前)のイベントですが、これを "process.action_mailer" にすれば「送信処理の前」(アクション、つまり今回の場合であれば FooMailer.foo 実行の直前)のイベントを購読できます。

interceptors と observers

ActiveSupport::NotificationActionMailerdeliver_mail(送信処理)や process(アクションの実行)で発火するようになっていますが、Mail 自体にも Observer を加えるしくみが用意されており、ActionMailer からは register_interceptorsregister_observers からそうした Observer を登録しておくことができます。

たとえば

class MyInterceptor
  def self.delivering_email(mail)
    puts mail.inspect
  end
end

class MyObserver
  def self.delivered_email(mail)
    puts mail.inspect
  end
end

ActionMailer::Base.register_interceptors(:my_interceptor)
ActionMailer::Base.register_observers(:my_observer)

としておけば、送信処理の直前で MyInterceptor.delivering_email を割り込ませることができ、送信後に MyObserver.delivered_email を実行させることができます。

さいごに

もちろん、 ActionMailer::Base#deliver_mail メソッドを直接上書きしても、送信処理の前に割り込みをすることはできるだろうと思います(この場合、super を呼ばなければ送信そのものをキャンセルすることもできそうです)。

しかし、こうした Observer を使えるしくみが用意されているので、もし用途に適しているようであれば、活用してみたほうがキレイな実装になるかな?と思います。