# 通知設計書 17-ActiveSupport::Notifications.subscribe

## 概要

本ドキュメントは、ActiveSupportの計装イベント購読機能について、設計情報を記載するものである。

### 本通知の処理概要

ActiveSupport::Notifications.subscribeは、計装イベント（Instrumentation Event）を購読するためのメソッドである。特定のイベント名または正規表現パターンに一致するイベントを購読し、イベント発生時にコールバックを実行する。ログ記録、パフォーマンス監視、メトリクス収集などの横断的関心事の実装に使用される。

**業務上の目的・背景**：アプリケーションやフレームワークが発火するイベントを購読し、カスタムの処理（ログ出力、外部サービスへのメトリクス送信、アラート発火等）を実行可能にする。Railsフレームワークの内部イベント（sql.active_record、process.action_controller等）を監視することで、アプリケーションの動作を可視化する。

**通知の送信タイミング**：subscribeは購読を登録するメソッドであり、実際のイベント受信はinstrument呼び出し時に発生する。

**通知の受信者**：subscribeメソッドで登録されたコールバック（ブロックまたはCallableオブジェクト）。

**通知内容の概要**：Eventオブジェクト（引数1つのブロック）または5引数（name, start, finish, id, payload）形式でイベント情報が渡される。

**期待されるアクション**：購読者が受信したイベントに基づいてログ出力、メトリクス送信、その他のカスタム処理を実行する。

## 通知種別

計装イベント購読（Instrumentation Subscription）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（イベント発生時に即座にコールバック実行） |
| 優先度 | 高（計装イベントと同期） |
| リトライ | なし |

### 購読パターン

| パターン種別 | 説明 | 例 |
|------------|------|-----|
| 文字列完全一致 | 指定したイベント名に完全一致 | "sql.active_record" |
| 正規表現 | パターンに一致するすべてのイベント | /\.active_record$/ |
| nil | すべてのイベント | nil |

## 通知テンプレート

### コールバック形式

#### 1引数形式（Eventオブジェクト）

```ruby
ActiveSupport::Notifications.subscribe("process.action_controller") do |event|
  event.name       # => "process.action_controller"
  event.duration   # => 10.5 (ミリ秒)
  event.payload    # => { controller: "UsersController", ... }
  event.allocations # => 1826
end
```

#### 5引数形式（従来形式）

```ruby
ActiveSupport::Notifications.subscribe("sql.active_record") do |name, start, finish, id, payload|
  name    # => "sql.active_record"
  start   # => Time (開始時刻)
  finish  # => Time (終了時刻)
  id      # => String (トランザクションID)
  payload # => Hash (カスタムデータ)
end
```

#### Callableオブジェクト形式

```ruby
class SqlSubscriber
  def call(name, start, finish, id, payload)
    Rails.logger.info "SQL: #{payload[:sql]}"
  end
end

ActiveSupport::Notifications.subscribe("sql.active_record", SqlSubscriber.new)
```

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| pattern | 購読パターン（String, Regexp, nil） | 呼び出し元で指定 | No |
| callback | コールバックオブジェクト | 引数またはブロック | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | subscribe | なし（即座に登録） | 購読登録 |
| イベント発生 | instrument | パターン一致時 | コールバック実行 |

### 購読解除条件

| 条件 | 説明 |
|-----|------|
| unsubscribe | subscriberオブジェクトまたはイベント名を指定して購読解除 |
| subscribed | ブロック内でのみ購読（終了後自動解除） |

## 処理フロー

### 購読登録フロー

```mermaid
flowchart TD
    A[subscribe pattern, callback 呼び出し] --> B[Fanout#subscribe]
    B --> C{pattern種別判定}
    C -->|String| D[string_subscribers に登録]
    C -->|Regexp/nil| E[other_subscribers に登録]
    D --> F[キャッシュクリア: pattern]
    E --> G[キャッシュクリア: 全体]
    F --> H[Subscriberオブジェクト返却]
    G --> H
```

### イベント受信フロー

```mermaid
flowchart TD
    A[instrument イベント発生] --> B[all_listeners_for name]
    B --> C[パターンマッチング]
    C --> D[マッチした購読者を取得]
    D --> E{購読者タイプ}
    E -->|1引数| F[EventObjectGroup]
    E -->|5引数| G[TimedGroup]
    E -->|start/finish| H[EventedGroup]
    F --> I[Event.new → callback.call event]
    G --> J[callback.call name, start, finish, id, payload]
    H --> K[listener.start → listener.finish]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | subscribe自体はDBを参照しない |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | subscribe自体はDBを更新しない |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 不正パターン | String/Regexp/nil以外のパターン | ArgumentError発生 |
| コールバック例外 | 購読者内で例外発生 | InstrumentationSubscriberError |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし（常時有効）

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

- 購読者内でペイロードの機密情報を適切にフィルタリングする
- ログ出力時は個人情報をマスキングする
- 購読者のパフォーマンス影響を考慮する（重い処理は非同期化）

## 備考

- 長期間有効な購読はアプリケーション起動時に登録することを推奨
- 一時的な購読は`subscribed`メソッドを使用（ブロック終了後に自動解除）
- 購読解除時はキャッシュがクリアされるため、頻繁な登録解除は避ける
- `monotonic_subscribe`でモノトニック時間を使用した高精度計測が可能

---

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

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

### 推奨読解順序

#### Step 1: subscribeメソッドを理解する

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

**主要処理フロー**:
1. **行244-246**: `subscribe(pattern = nil, callback = nil, &block)` - エントリーポイント
2. **行245**: `notifier.subscribe(pattern, callback, monotonic: false, &block)` - Fanoutへ委譲

#### Step 2: Fanout#subscribeを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fanout.rb | `activesupport/lib/active_support/notifications/fanout.rb` | Fanout#subscribe |

**主要処理フロー**:
- **行69-84**: `subscribe(pattern, callable, monotonic:, &block)`
- **行70**: `Subscribers.new(pattern, callable || block, monotonic)` - Subscriberオブジェクト生成
- **行72-81**: パターン種別に応じた購読者リストへの登録
- **行83**: Subscriberオブジェクトを返却

#### Step 3: Subscribersファクトリを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fanout.rb | `activesupport/lib/active_support/notifications/fanout.rb` | Subscribers module |

**主要処理フロー**:
- **行340-358**: `Subscribers.new` - 購読者タイプの決定
- **行342-345**: start/finishメソッド対応 → Evented
- **行350-354**: 1引数ブロック → EventObject
- **行342/349**: それ以外 → Timed or MonotonicTimed

#### Step 4: 購読者タイプを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | fanout.rb | `activesupport/lib/active_support/notifications/fanout.rb` | Evented, Timed, EventObject |

**主要処理フロー**:
- **行397-437**: `Evented` - start/finishを持つオブジェクト用
- **行439-447**: `Timed` - 5引数コールバック用（壁時計時間）
- **行449-453**: `MonotonicTimed` - 5引数コールバック用（モノトニック時間）
- **行455-463**: `EventObject` - 1引数（Eventオブジェクト）コールバック用

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

```
ActiveSupport::Notifications.subscribe(pattern, callback, &block)
    │
    └─ notifier.subscribe(pattern, callback, monotonic: false, &block) (行245)
           │
           └─ Fanout#subscribe (fanout.rb 行69-84)
                  │
                  ├─ Subscribers.new(pattern, callable, monotonic) (行70)
                  │      │
                  │      ├─ listener.respond_to?(:start) && :finish
                  │      │      └─ Evented
                  │      │
                  │      ├─ procish.arity == 1 && parameters.length == 1
                  │      │      └─ EventObject
                  │      │
                  │      └─ otherwise
                  │             └─ Timed or MonotonicTimed
                  │
                  ├─ case pattern
                  │      ├─ String → @string_subscribers[pattern] << subscriber
                  │      └─ Regexp/nil → @other_subscribers << subscriber
                  │
                  └─ clear_cache(pattern or all)
                         │
                         └─ return subscriber
```

### データフロー図

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

pattern ─────────────────▶ Matcher.wrap ───▶ Matcher or String
(String/Regexp/nil)

callback/block ──────────▶ Subscribers.new ───▶ Subscriber
                                │
                                ├─ Evented (start/finish対応)
                                ├─ EventObject (1引数)
                                ├─ Timed (5引数、壁時計)
                                └─ MonotonicTimed (5引数、モノトニック)

monotonic ───────────────▶ Timed/MonotonicTimed選択

                          │
                          ▼
               ┌─────────────────────┐
               │   Fanout            │
               │   @string_subscribers│
               │   @other_subscribers │
               └─────────────────────┘
                          │
                          ▼
               subscriber (返却)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notifications.rb | `activesupport/lib/active_support/notifications.rb` | ソース | subscribe, unsubscribe, subscribed |
| fanout.rb | `activesupport/lib/active_support/notifications/fanout.rb` | ソース | Fanout, Subscribers |
| instrumenter.rb | `activesupport/lib/active_support/notifications/instrumenter.rb` | ソース | Event クラス |
| log_subscriber.rb | `activesupport/lib/active_support/log_subscriber.rb` | ソース | LogSubscriber基底クラス |
