# 通知設計書 20-deliver.action_mailer

## 概要

本ドキュメントは、ActionMailerのメール配信計装イベントについて、設計情報を記載するものである。

### 本通知の処理概要

deliver.action_mailerは、ActionMailerによるメール配信時に発火するActiveSupport::Notificationsの計装イベントである。メールの実際の送信処理をラップし、送信の開始から完了までの情報を購読者に通知する。ログ記録、パフォーマンス監視、メトリクス収集、監査ログ等に使用される。

**業務上の目的・背景**：メール送信は重要なビジネス機能であり、その動作を監視・記録する必要がある。deliver.action_mailerイベントを購読することで、送信されたメールの内容、宛先、送信時間、成否などを統一的に監視できる。ActionMailer::LogSubscriberがデフォルトでこのイベントを購読し、ログ出力を行う。

**通知の送信タイミング**：メールの実際の配信処理（SMTPサーバーへの送信等）の開始時と終了時に発火する。

**通知の受信者**：ActiveSupport::Notifications.subscribeで「deliver.action_mailer」を購読しているすべての購読者。

**通知内容の概要**：メーラー名、メッセージID、件名、宛先、送信元、日時、配信成否などの情報を含むペイロード。

**期待されるアクション**：購読者がイベントを受信し、ログ出力、メトリクス送信、監査ログ記録等を実行する。

## 通知種別

フレームワーク計装イベント（Instrumentation Event）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（計装イベント） |
| 優先度 | 高（メール送信と同期） |
| リトライ | なし |

### イベント発火タイミング

| タイミング | 説明 |
|-----------|------|
| 開始 | メール配信処理開始時 |
| 終了 | メール配信処理完了時（成功/失敗問わず） |

## 通知テンプレート

### ペイロード構造

| キー | 説明 | 型 |
|-----|------|-----|
| :mail | エンコード済みメール全体 | String |
| :mailer | メーラークラス名 | String |
| :message_id | メッセージID | String |
| :subject | 件名 | String |
| :to | 宛先アドレス | Array |
| :from | 送信元アドレス | Array |
| :bcc | BCCアドレス（存在時のみ） | Array |
| :cc | CCアドレス（存在時のみ） | Array |
| :date | 送信日時 | DateTime |
| :perform_deliveries | 実際に配信されたか | Boolean |

### ペイロード例

```ruby
{
  mail: "Date: Sun, 01 Mar 2026 12:00:00 +0000\r\n...",
  mailer: "NotifierMailer",
  message_id: "abc123@example.com",
  subject: "Welcome to our service",
  to: ["user@example.com"],
  from: ["noreply@example.com"],
  date: Sun, 01 Mar 2026 12:00:00 +0000,
  perform_deliveries: true
}
```

### 例外発生時の追加キー

| キー | 説明 | 型 |
|-----|------|-----|
| :exception | [例外クラス名, メッセージ] | Array |
| :exception_object | 例外オブジェクト | Exception |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| event | イベントオブジェクト | ActiveSupport::Notifications::Event | 1引数購読時 |
| name | イベント名 | "deliver.action_mailer" | 5引数購読時 |
| start | 開始時刻 | Time | 5引数購読時 |
| finish | 終了時刻 | Time | 5引数購読時 |
| id | トランザクションID | String | 5引数購読時 |
| payload | ペイロード | Hash | 5引数購読時 |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| メール配信 | deliver_now | 常に発火 | 同期配信時 |
| メール配信 | deliver_later | ジョブ実行時 | 非同期配信時（ジョブ内で発火） |

### 発火コード

```ruby
# actionmailer/lib/action_mailer/base.rb
def self.deliver_mail(mail)
  ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
    set_payload_for_mail(payload, mail)
    yield # Let Mail do the delivery actions
  end
end
```

## 処理フロー

### 計装イベント発火フロー

```mermaid
flowchart TD
    A[メール配信開始] --> B[deliver_mail メソッド]
    B --> C[instrument deliver.action_mailer 開始]
    C --> D[ペイロード設定: set_payload_for_mail]
    D --> E[購読者へstart通知]
    E --> F{メール送信処理}
    F -->|成功| G[yield完了]
    F -->|失敗| H[例外発生]
    G --> I[購読者へfinish通知]
    H --> J[payload に例外情報追加]
    J --> I
    I --> K[Eventオブジェクト生成]
    K --> L[LogSubscriber.delivered イベント]
    L --> M[ログ出力]
    M --> N[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | 計装イベント自体はDBを参照しない |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | 計装イベント自体はDBを更新しない |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 配信エラー | SMTP接続失敗等 | 例外情報がペイロードに追加される |
| 購読者エラー | 購読者コールバック内で例外 | InstrumentationSubscriberError |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（計装イベントはリトライしない） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 設定なし |
| 1日あたり上限 | 設定なし |

### 配信時間帯

制限なし

## セキュリティ考慮事項

- payload[:mail]にメール本文全体が含まれるため、ログ出力時は注意
- 個人情報（メールアドレス、本文内容）の取り扱いに注意
- 外部サービスへの送信時は機密情報をフィルタリング

## 備考

- ActionMailer::LogSubscriberがデフォルトで購読している
- deliver_later使用時はActive Jobワーカー内でイベントが発火
- perform_deliveries: falseの場合もイベントは発火する（配信スキップをログで確認可能）
- 関連イベントとしてprocess.action_mailer（メーラー処理）がある

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: イベント発火箇所を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | base.rb | `actionmailer/lib/action_mailer/base.rb` | deliver_mailメソッド |

**主要処理フロー**:
1. **行593-598**: `deliver_mail(mail)` - 計装のエントリーポイント
2. **行594**: `ActiveSupport::Notifications.instrument("deliver.action_mailer")` - イベント発火
3. **行595**: `set_payload_for_mail(payload, mail)` - ペイロード設定
4. **行596**: `yield` - 実際のメール配信処理

#### Step 2: ペイロード設定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | base.rb | `actionmailer/lib/action_mailer/base.rb` | set_payload_for_mail |

**主要処理フロー**:
- **行611-622**: `set_payload_for_mail(payload, mail)` - ペイロードの各項目設定
- **行612**: `payload[:mail] = mail.encoded` - メール全体
- **行613-621**: 各ヘッダー情報の設定（mailer, message_id, subject等）

#### Step 3: デフォルト購読者（LogSubscriber）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | log_subscriber.rb | `actionmailer/lib/action_mailer/log_subscriber.rb` | delivered メソッド |

**主要処理フロー**:
- **行10-23**: `delivered(event)` - イベント受信時の処理
- **行12-18**: 成功/失敗/スキップの判定とログ出力
- **行22**: `debug { payload[:mail] }` - デバッグログでメール本文出力

#### Step 4: 購読方法を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | notifications.rb | `activesupport/lib/active_support/notifications.rb` | subscribeメソッド |

**カスタム購読例**:
```ruby
ActiveSupport::Notifications.subscribe("deliver.action_mailer") do |event|
  Rails.logger.info "Mail sent: #{event.payload[:message_id]} to #{event.payload[:to]}"
end
```

### プログラム呼び出し階層図

```
mail.deliver_now (または deliver_later のジョブ内)
    │
    └─ ActionMailer::Base.deliver_mail(mail) (行593-598)
           │
           └─ ActiveSupport::Notifications.instrument("deliver.action_mailer")
                  │
                  ├─ set_payload_for_mail(payload, mail) (行595)
                  │      │
                  │      └─ payload設定
                  │           ├─ :mail
                  │           ├─ :mailer
                  │           ├─ :message_id
                  │           ├─ :subject
                  │           ├─ :to
                  │           ├─ :from
                  │           ├─ :bcc (if present)
                  │           ├─ :cc (if present)
                  │           ├─ :date
                  │           └─ :perform_deliveries
                  │
                  ├─ yield (行596)
                  │      └─ Mail::Message#deliver
                  │             └─ SMTPサーバー送信
                  │
                  └─ 購読者へ通知
                         │
                         └─ ActionMailer::LogSubscriber#delivered
                                └─ ログ出力
```

### データフロー図

```
[入力]                          [処理]                              [出力]

Mail::Message ───────────▶ deliver_mail ───▶ instrument開始

mail.encoded ────────────▶ payload[:mail]
mail.subject ────────────▶ payload[:subject]
mail.to ─────────────────▶ payload[:to]
mail.from ───────────────▶ payload[:from]
mail.message_id ─────────▶ payload[:message_id]
...

                          │
                          ▼
                   yield (メール送信)
                          │
                          ▼
               ┌─────────────────────┐
               │   Subscribers       │
               │   (LogSubscriber等) │
               └─────────────────────┘
                          │
                          ▼
               Event {
                 name: "deliver.action_mailer",
                 duration: 150.5,
                 payload: { mail: ..., to: [...], ... }
               }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| base.rb | `actionmailer/lib/action_mailer/base.rb` | ソース | deliver_mail、set_payload_for_mail |
| log_subscriber.rb | `actionmailer/lib/action_mailer/log_subscriber.rb` | ソース | デフォルト購読者 |
| notifications.rb | `activesupport/lib/active_support/notifications.rb` | ソース | 計装基盤 |
| instrumenter.rb | `activesupport/lib/active_support/notifications/instrumenter.rb` | ソース | Eventクラス |
| message_delivery.rb | `actionmailer/lib/action_mailer/message_delivery.rb` | ソース | deliver_now、deliver_later |
