Formatter の freeze でタグスタックをメモ化し Ractor 共有を実現
ActiveSupport::TaggedLogging::Formatter が freeze された状態でも正しく動作し、Ractor で安全に共有できるようになりました。これによりロガー設定をスレッド間だけでなく Ractor 間でも再利用でき、マルチスレッド・マルチプロセス環境でのロギングが統一されます。
背景
Ractor 環境でロガーを共有できないことが、マルチスレッド・マルチプロセスアプリケーションにおけるロギング統一の障壁となっていました。ActiveSupport::TaggedLogging::Formatter は内部で tag_stack を保持し、タグ付きメッセージの生成に利用しますが、オブジェクトが freeze されると遅延評価された tag_stack が評価されず、Ractor にコピーされた際に正しく機能しませんでした。PR #57644 はこの問題を解消し、凍結されたフォーマッタでもタグ情報を保持できるようにします。
技術的な変更
Formatter に freeze メソッドが追加され、tag_stack を先に評価してから super を呼び出す実装になっています。これによりインスタンスが凍結されるタイミングでスタックが確定し、Ractor へ安全に渡せるようになります。
class Formatter
# 既存の実装は省略
def freeze
tag_stack
super
end
end
テストが新たに追加され、Formatter を凍結した後でも info ログが期待通り出力されることが確認されています。テストはロガーのフォーマッタを明示的に freeze し、続いて info 呼び出しで出力内容を検証します。
test "formatter works when frozen" do
@logger.formatter.freeze
@logger.info "frozen"
assert_equal "frozen\n", @output.string
end
設計判断
インスタンス凍結時に内部状態を明示的に評価する アプローチが採用されました。代替案としては tag_stack の遅延評価を維持しつつ別途メモ化処理を入れる方法も考えられましたが、freeze 内で一度アクセスするだけの実装がシンプルで既存 API への変更が不要でした。 この設計により後方互換性を保ちつつ、Ractor 共有という新機能を提供できます。
まとめ
ActiveSupport::TaggedLogging::Formatter に freeze が実装されたことで、フォーマッタを凍結した状態でも tag_stack が正しく保持され、Ractor 間で安全に共有できるようになりました。変更はコードベースに最小限の追加で済み、既存のロギング設定やテストへの影響はほぼありません。