フレームワーク内部イベントのペイロードフィルタリングを無効化
フレームワークの内部イベントに対するパラメータフィルタリングが無効化され、SQLクエリ名などのメタデータが正しく表示されるようになりました。これにより、config.filter_parameters の設定がログの可読性を損なう問題が解消されます。
背景
EventReporter は、イベントのペイロードに対して ParameterFilter を適用してから購読者に配信する仕組みになっていました。54264bde でフレームワークのログ購読者が EventReporter 経由でイベントを受け取るように変更された結果、SQLクエリ名や内部メタデータまでフィルタリングされる副作用が生じていました。
具体的には、config.filter_parameters += [:name] のような設定により、以下のようにログが破壊されていました:
意図しない動作:
[FILTERED] (0.4ms) SELECT "people".* FROM "people" WHERE ...
期待される動作:
Person Load (0.4ms) SELECT "people".* FROM "people" WHERE ...
この問題は、フレームワーク内部のメタデータ(SQLクエリの種類を示す「Person Load」など)がユーザーの機密パラメータと同じ扱いを受けてしまうことに起因していました。
技術的な変更
EventReporter の notify メソッドと debug メソッドに filter_payload: オプションが追加され、ペイロードのフィルタリングを制御できるようになりました。
EventReporter の拡張
activesupport/lib/active_support/event_reporter.rb で、メソッドシグネチャが以下のように変更されました:
変更後:
def notify(name_or_object, payload = nil, caller_depth: 1, filter_payload: true, **kwargs)
# ...
payload = resolve_payload(name_or_object, payload, filter_payload, **kwargs)
# ...
end
def debug(name_or_object, payload = nil, caller_depth: 1, filter_payload: true, **kwargs)
if debug_mode?
if block_given?
notify(name_or_object, payload, caller_depth: caller_depth + 1, filter_payload: filter_payload, **kwargs.merge(yield))
else
notify(name_or_object, payload, caller_depth: caller_depth + 1, filter_payload: filter_payload, **kwargs)
end
end
end
resolve_payload メソッドも filter パラメータを受け取るように変更され、filter が false の場合はフィルタリングをスキップします:
def resolve_payload(name_or_object, payload, filter, **kwargs)
case name_or_object
when String, Symbol
if kwargs.any?
resolved = kwargs.transform_keys(&:to_sym)
filter ? payload_filter.filter(resolved) : resolved
elsif payload
resolved = payload.transform_keys(&:to_sym)
filter ? payload_filter.filter(resolved) : resolved
end
# ...
end
end
StructuredEventSubscriber での適用
StructuredEventSubscriber の emit_event と emit_debug_event メソッドが、filter_payload: false を渡すように変更されました:
def emit_event(name, payload = nil, caller_depth: 1, **kwargs)
ActiveSupport.event_reporter.notify(name, payload, caller_depth: caller_depth + 1, filter_payload: false, **kwargs)
rescue => e
handle_event_error(name, e)
end
def emit_debug_event(name, payload = nil, caller_depth: 1, **kwargs)
ActiveSupport.event_reporter.debug(name, payload, caller_depth: caller_depth + 1, filter_payload: false, **kwargs)
rescue => e
handle_event_error(name, e)
end
この変更により、ActionView の render イベントを含む全てのフレームワーク購読者が、購読者ごとの修正なしに恩恵を受けます。actionview/lib/action_view/structured_event_subscriber.rb では、render_start イベント発行時に直接 event_reporter.debug を呼び出しているため、同様に filter_payload: false が追加されています。
テストケースの追加
変更の正しさを検証するため、複数のテストケースが追加されました:
-
EventReporterのnotifyとdebugメソッドでfilter_payload: falseが正しく動作することを確認するテスト(activesupport/test/event_reporter_test.rb) -
StructuredEventSubscriberのemit_eventとemit_debug_eventがペイロードをフィルタリングしないことを確認するテスト(activesupport/test/structured_event_subscriber_test.rb) - ActionView の
render_startイベントでidentifierがフィルタリングされないことを確認するテスト(actionview/test/template/structured_event_subscriber_test.rb)
設計判断
デフォルト値を true にして後方互換性を維持する設計 が採用されました。
filter_payload: オプションは既存の挙動を変えないよう true がデフォルトです。これにより、ユーザーコードで EventReporter#notify や EventReporter#debug を直接呼び出している場合でも、従来通りペイロードがフィルタリングされます。
フレームワーク内部の購読者は StructuredEventSubscriber を継承しており、その基底クラスで一括して filter_payload: false を指定する設計になっています。このアプローチにより、個別の購読者を修正することなく、フレームワーク全体に修正を適用できました。
まとめ
本PRは、ユーザーが設定した filter_parameters がフレームワーク内部のメタデータまで隠してしまう問題を解決しました。filter_payload: オプションの導入により、ユーザーデータと内部メタデータを区別してフィルタリングできるようになり、ログの可読性が向上します。既存コードへの影響を最小限に抑えつつ、フレームワーク全体で一貫した動作を実現した設計といえます。