Railsでは config.filter_parameters を使うことで、ログへ出力したくないパラメータを設定できます。
3.2.34 config.filter_parameters | Rails アプリケーションの設定項目 - Railsガイド
ただ、この設定がデフォルトではActiveJobでのジョブ実行時のログには適用されないと、同僚から教わりました。
そこで、どんな感じになるのか気になったことから、調べたときのメモを残します。
目次
- 環境
- デフォルト設定での動作確認
- log_arguments=falseで、Jobの引数を表示しないようにする
- ログにJobの引数は出したいけど、フィルタリングもしたい
- ソースコード
環境
デフォルト設定での動作確認
まずは、デフォルトだとどのような挙動になるか確認します。
rails new --minimal で最低限のRailsアプリを作ります。minimalの場合、ActiveJobまわりの設定はスキップされます。
% bundle exec rails new . --minimal Based on the specified options, the following options will also be activated: --skip-active-job [due to --minimal] --skip-action-mailer [due to --skip-active-job, --minimal] --skip-active-storage [due to --skip-active-job, --minimal] --skip-solid [due to --minimal] --skip-action-mailbox [due to --skip-active-storage, --minimal] --skip-action-text [due to --skip-active-storage, --minimal] --skip-javascript [due to --minimal] --skip-hotwire [due to --skip-javascript, --minimal] --skip-action-cable [due to --minimal] --skip-bootsnap [due to --minimal] --skip-brakeman [due to --minimal] --skip-bundler-audit [due to --minimal] --skip-ci [due to --minimal] --skip-dev-gems [due to --minimal] --skip-docker [due to --minimal] --skip-jbuilder [due to --minimal] --skip-kamal [due to --minimal] --skip-rubocop [due to --minimal] --skip-system-test [due to --minimal] --skip-thruster [due to --minimal] ...
ジェネレータを使ってJob MyJob を生成します。ジェネレータを使うことで、スキップされたActiveJobまわりの設定も生成されました。
% bin/rails g job MyJob
invoke test_unit
create test/jobs/my_job_test.rb
create app/jobs/my_job.rb
create app/jobs/application_job.rb
今回はJobの中で puts したり logger.info する程度とします。
class MyJob < ApplicationJob queue_as :default def perform(payload) puts "MyJob received payload (puts): #{payload}" logger.info "MyJob received payload (logger): #{payload}" end end
Controllerも作ります。Controllerの中でも puts したり logger.info したりします。
class TokensController < ApplicationController skip_before_action :verify_authenticity_token, raise: false def create token = params[:token] puts "Received token in controller (puts): #{params}" logger.info "Received token in controller (logger): #{params}" MyJob.perform_later({ token: token }) head :ok end end
developmentモードで動かすと、 logger.infoでは development.logへログが出力されます。
今回は puts と同じように標準出力で確認できるよう、 dotenv-rails で環境変数 RAILS_LOG_TO_STDOUT を設定し、ログの出力先を変更します。
- https://github.com/bkeepers/dotenv
- Alternative to #23638 log to STDOUT via env var by schneems · Pull Request #23734 · rails/rails
.env ファイルを用意します。
RAILS_LOG_TO_STDOUT=1
routes.rb も適当に作ります。
Rails.application.routes.draw do post 'tokens', to: 'tokens#create' end
curlで動作確認します。
% curl -X POST http://localhost:3000/tokens -d "token=my_secret_token"
ログを見てみます。
まずはController部分です。リクエストのログはフィルタリングされていますが、 puts や logger.info はそのまま出力されていました。
Started POST "/tokens" for 127.0.0.1 at 2026-01-24 17:57:00 +0900
# リクエストのログはフィルタリングされている
Processing by TokensController#create as */*
Parameters: {"token" => "[FILTERED]"}
# putsやlogger.infoはそのまま出力される
Received token in controller (puts): {"token" => "my_secret_token", "controller" => "tokens", "action" => "create"}
Received token in controller (logger): {"token" => "my_secret_token", "controller" => "tokens", "action" => "create"}
続いてJob部分です。Jobの設定は修正していないため、デフォルトの async queue adapterが使われます。
3.14 Active Jobを設定する | Rails アプリケーションを設定する - Railsガイド
ジョブがエンキューされたときのログです。フィルタリングはされず、そのまま出力されていました。
[ActiveJob] Enqueued MyJob (Job ID: a26d803a-c514-4a73-809a-e3120a6ead3e) to Async(default) with arguments: {token: "my_secret_token"}
[ActiveJob] Enqueued MySecretJob (Job ID: 6f56f581-2907-4f1a-b78a-6be6965656fb) to Async(default)
ジョブ実行時のログです。こちらも、通常のログに加えて、 puts 、logger.info の出力がフィルタリングされないままでした。
# 通常のログ
[ActiveJob] [MyJob] [a26d803a-c514-4a73-809a-e3120a6ead3e] Performing MyJob (Job ID: a26d803a-c514-4a73-809a-e3120a6ead3e) from Async(default) enqueued at 2026-01-24T09:02:49.949194000Z with arguments: {token: "my_secret_token"}
# puts
MyJob received payload (puts): {token: "my_secret_token"}
# logger.info
[ActiveJob] [MyJob] [a26d803a-c514-4a73-809a-e3120a6ead3e] MyJob received payload (logger): {token: "my_secret_token"}
log_arguments=falseで、Jobの引数を表示しないようにする
Jobで引数をなんとかする方法を探したところ、ログにJobの引数を表示しないようにする設定 log_arguments がありました。
- Add an option to disable logging for jobs with sensitive arguments by rafaelfranca · Pull Request #37660 · rails/rails
- 新機能: 重要な情報を引数とするジョブでログ出力を抑制するオプションを追加 | 週刊Railsウォッチ(20191118前編)ActiveJob引数のログ抑制、RailsガイドProプランお試し、ファイルアップロードのレジュームgemほか|TechRacho by BPS株式会社
- ActiveJob::Logging
実際に試してみます。まずはJob MySecretJob をジェネレータで生成します。
% bin/rails g job MySecretJob
invoke test_unit
create test/jobs/my_secret_job_test.rb
create app/jobs/my_secret_job.rb
ApplicationJobを継承した MySecretJobに self.log_arguments = false を設定します。
class MySecretJob < ApplicationJob queue_as :default self.log_arguments = false def perform(payload) puts "MySecretJob received payload (puts): #{payload}" logger.info "MySecretJob received payload (logger): #{payload}" end end
Controllerに MySecretJob の実行を追加します。
def create ... # 追加 MySecretJob.perform_later({ token: token }) head :ok end
先ほどと同じように curlでテストしたところ、ログに引数が出なくなっていました。
... # エンキュー [ActiveJob] Enqueued MySecretJob (Job ID: 33e55d02-f990-432a-bf9d-0f714f0ffbc3) to Async(default) ... # 実行 [ActiveJob] [MySecretJob] [33e55d02-f990-432a-bf9d-0f714f0ffbc3] Performing MySecretJob (Job ID: 33e55d02-f990-432a-bf9d-0f714f0ffbc3) from Async(default) enqueued at 2026-01-24T09:14:11.782581000Z
ログにJobの引数は出したいけど、フィルタリングもしたい
「重要な情報を含む処理ではログに一切出力しない」は適切ですが、状況によっては
- 普通の引数はログを追いかけるときに必要なので、そのまま出力
- 重要な一部の引数はフィルタリングして出力
を行いたいこともあります。
そこで実現できる方法を調べてみました。
ActiveJob::LogSubscriberをパッチする
実装
Railsのissueを調べたところ、以下が見つかりました。
[WIP] Filter parameters in ActiveJob logs by davidpiegza · Pull Request #34438 · rails/rails
そのissueでは、実装例が含まれるコメントもありました。
https://github.com/rails/rails/pull/34438#issuecomment-2336749226
コメントによると、 ActiveJob::LogSubscriber のprivateメソッド format をパッチすれば良さそうです。
今回は以下の点を修正して試してみます。
config.after_initialize+alias_methodではなく、ActiveSupport.on_load(:active_job)+prependを使う- Railsガイドに従い、読み込みフックを使う方が良さそうと考えたため
alias_methodではなく、superを使うprependの探索順で動作してほしいため
まとめるとこんな感じです。
ActiveSupport.on_load(:active_job) do require "active_job/log_subscriber" module ActiveJobLogSubscriberFilter private def format(arg) if arg.is_a?(Hash) # Rails.application.config.filter_parameters を利用してフィルタリングを適用 parameter_filter = ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters) parameter_filter.filter(arg.transform_values { |value| super(value) }) else # superを使って、元々のformatメソッドを呼び出す。 # なお、元々のformatメソッドの中でformatメソッドを使ったときは、ここで定義したformatメソッドが呼び出される。 super(arg) end end end # ActiveJob::LogSubscriber にパッチを適用 ActiveJob::LogSubscriber.prepend(ActiveJobLogSubscriberFilter) end
queue_adapter :sync (デフォルト)での動作確認
では実際にパッチできているかを確認します。
まずはデフォルト設定(:sync)での動作確認です。
キューイングの時もフィルタリングされたログが出力されました。
Processing by TokensController#create as */*
Parameters: {"token" => "[FILTERED]"}
...
[ActiveJob] Enqueued MyJob (Job ID: d8ef1e1e-1f10-4b0f-8e7b-a2f7bd594d9c) to Async(default) with arguments: [{request: {token: "[FILTERED]"}}]
続いてJobの実行時です。こちらもフィルタリングされていました。良さそうです。
[ActiveJob] [MyJob] [d8ef1e1e-1f10-4b0f-8e7b-a2f7bd594d9c] Performing MyJob (Job ID: d8ef1e1e-1f10-4b0f-8e7b-a2f7bd594d9c) from Async(default) enqueued at 2026-01-24T11:34:08.230302000Z with arguments: [{request: {token: "[FILTERED]"}}]
queue_adapter :solid_queue での動作確認
デフォルトの queue_adapter では問題なく動作しましたが、実運用では別の queue_adapter を使うことが多いはずです。
そこで、次はRails8.0から導入された Solid Queue での動作を確認します。
Ruby on Rails 8.0 Release Notes — Ruby on Rails Guides
Solid Queueのセットアップ
今回は rails new --minimal でアプリを生成したため、Solid Queueが導入されていません。
そこで、 Solid Queue のREADMEに従ってインストールします。
https://github.com/rails/solid_queue?tab=readme-ov-file#installation
% bundle add solid_queue
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Fetching gem metadata from https://rubygems.org/.........
Fetching raabro 1.4.0
Fetching et-orbi 1.4.0
Installing raabro 1.4.0
Installing et-orbi 1.4.0
Fetching fugit 1.12.1
Installing fugit 1.12.1
Fetching solid_queue 1.3.1
Installing solid_queue 1.3.1
% bin/rails solid_queue:install
create config/queue.yml
create config/recurring.yml
create db/queue_schema.rb
create bin/jobs
gsub config/environments/production.rb
続いて、Railsガイドに従い、Solid Queueをセットアップします。
3 Solid Queue: デフォルトのバックエンド | Active Job の基礎 - Railsガイド
config/database.yml に追記します。
development: primary: <<: *default database: storage/development.sqlite3 queue: <<: *default database: storage/development_queue.sqlite3 migrations_paths: db/queue_migrate
DBを準備します。
% bin/rails db:prepare
最後に、 config/environment/development.rbの queue_adapter まわりを修正します。
Rails.application.configure do # ... config.active_job.queue_adapter = :solid_queue config.solid_queue.connects_to = { database: { writing: :queue } } end
動作確認
ここでも curl で動作確認します。
まずはキューイングのログです。DBアクセスのログとともに以下のログが出ていました。フィルタリングされています。
Started POST "/tokens" for 127.0.0.1 at 2026-01-24 20:46:43 +0900
...
Processing by TokensController#create as */*
Parameters: {"token" => "[FILTERED]"}
...
[ActiveJob] Enqueued MyJob (Job ID: 5d714d02-c662-4702-9393-f77341beceaf) to SolidQueue(default) with arguments: [{request: {token: "[FILTERED]"}}]
...
[ActiveJob] Enqueued MySecretJob (Job ID: 9d8d1555-16b9-422b-9dae-c0b1034f1429) to SolidQueue(default)
続いて、 bin/jobs start にてJobを実行します。
実行ログは log/development.log へと出力されます。確認するとフィルタリングされていました。
[ActiveJob] [MyJob] [5d714d02-c662-4702-9393-f77341beceaf] Performing MyJob (Job ID: 5d714d02-c662-4702-9393-f77341beceaf) from SolidQueue(default) enqueued at 2026-01-24T11:46:44.063828000Z with arguments: [{request: {token: "[FILTERED]"}}]
[ActiveJob] [MyJob] [5d714d02-c662-4702-9393-f77341beceaf] Performed MyJob (Job ID: 5d714d02-c662-4702-9393-f77341beceaf) from SolidQueue(default) in 2.78ms
queue_adapter :delayed_job での動作確認
続いて、サードパーティのJobバックエンドでの動作を確認します。
今回はDelayed::Jobで確認します。まずは、READMEに従いセットアップを行います。
collectiveidea/delayed_job: Database based asynchronous priority queue system -- Extracted from Shopify
% bundle add delayed_job_active_record
% bin/rails generate delayed_job:active_record
create bin/delayed_job
chmod bin/delayed_job
create db/migrate/20260118111515_create_delayed_jobs.rb
% bin/rails db:migrate
== 20260118111515 CreateDelayedJobs: migrating ================================
-- create_table(:delayed_jobs)
-> 0.0008s
-- add_index(:delayed_jobs, [:priority, :run_at], {name: "delayed_jobs_priority"})
-> 0.0003s
== 20260118111515 CreateDelayedJobs: migrated (0.0011s) =======================
続いて、 queue_adapter を :delayed_job へと修正します。
config.active_job.queue_adapter = :delayed_job
動作確認です。エンキューしたときはフィルタリングされていました。
[ActiveJob] Enqueued MyJob (Job ID: 3ba66b0e-a06d-47bb-9957-2ebf540c934f) to DelayedJob(default) with arguments: [{request: {token: "[FILTERED]"}}]
次に bin/rails jobs:work にてJobを実行します。すると、フィルタリングされずにそのままログへ出力されました。
% bin/rails jobs:work
[Worker(host:*** pid:87720)] Starting job worker
# RUNNINGのログでは、そのまま出力されている
[Worker(host:*** pid:87720)] Job MyJob [e6ab8b99-944f-498f-936f-0c6d0ea8c872] from DelayedJob(default) with arguments: [[{"request" => {"token" => "my_secret_token", "_aj_symbol_keys" => ["token"]}, "_aj_symbol_keys" => ["request"]}]] (id=23) (queue=default) RUNNING
# COMPLETEDのログでも、そのまま出力されている
[Worker(host:*** pid:87720)] Job MyJob [e6ab8b99-944f-498f-936f-0c6d0ea8c872] from DelayedJob(default) with arguments: [[{"request" => {"token" => "my_secret_token", "_aj_symbol_keys" => ["token"]}, "_aj_symbol_keys" => ["request"]}]] (id=23) (queue=default) COMPLETED after 0.0066
これより、Delayed::Jobの場合は他にもパッチが必要そうでした。
Delayed::Jobをパッチする
パッチする適切な場所を探す
まずは、どこにパッチをすればよいかを確認します。
まず、ログの RUNNING などを出力いるところを探すと、 lib/delayed/worker.rb にある Delayed::Worker#runにありました。この中では job_sayを呼んでいました。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/worker.rb#L228-L229
def run(job) job_say job, 'RUNNING' # ... end
job_sayでは、 job.nameを使っていました。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/worker.rb#L273-L276
def job_say(job, text, level = default_log_level) text = "Job #{job.name} (id=#{job.id})#{say_queue(job.queue)} #{text}" say text, level end
job.nameは lib/delayed/backend/base.rb に定義されていました。ここで、 payload_object#display_name があれば、それを返しているようでした。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/backend/base.rb#L60-L64
def name @name ||= payload_object.respond_to?(:display_name) ? payload_object.display_name : payload_object.class.name # ... end
payload_object の定義を見ると、YAMLとして保存されている handler の値からデシリアライズしたオブジェクトでした。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/backend/base.rb#L71-L75
def payload_object @payload_object ||= YAML.load_dj(handler) # ... end
この handlerは、 payload_object= にて設定されるものでした。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/backend/base.rb#L66-L69
def payload_object=(object) @payload_object = object self.handler = object.to_yaml end
payload_object=を直接使っているところはありません。ただ、 enqueue_job で new(options)するときに、delayed_jobのバックエンドがActiveRecord、かつ、オプションとしてハッシュが渡されてくれば、 ActiveModel::AttributeAssignment の働きで属性へとセットするときに呼び出されそうです。
1.4 AttributeAssignmentモジュール | Active Model の基礎 - Railsガイド
enqueue_job は enqueue から呼ばれます。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/backend/base.rb#L10-L22
def enqueue(*args) job_options = Delayed::Backend::JobPreparer.new(*args).prepare enqueue_job(job_options) end def enqueue_job(options) new(options).tap do |job| # ... end end
Delayed::Backend::JobPreparer を見ると、 set_payloadで options ハッシュのキー payload_object へ設定してそうでした。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/backend/job_preparer.rb#L23-L25
def set_payload options[:payload_object] ||= args.shift end
enqueue はどこで呼ばれるか確認すると、 lib/active_job/queue_adapter/delayed_job_adapter.rb で呼ばれてそうでした。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/active_job/queue_adapters/delayed_job_adapter.rb#L11-L15
def enqueue(job) delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), :queue => job.queue_name, :priority => job.priority) # ... end
ここの Delayed::Job.enqueue の第一引数 JobWrapper が payload_object として設定されてそうでした。
JobWrapperの定義を確認すると、 display_name がありました。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/active_job/queue_adapters/delayed_job_adapter.rb#L30-L36
def display_name base_name = "#{job_data['job_class']} [#{job_data['job_id']}] from DelayedJob(#{job_data['queue_name']})" return base_name unless log_arguments? "#{base_name} with arguments: #{job_data['arguments']}" end
ここに job_data['arguments'] 、つまりジョブの引数が含まれてそうでした。
よって、 JobWrapper#display_name をパッチするのが良さそうと分かりました。
ちなみに、JobWrapper を渡す enqueue メソッドを持つ Delayed::Job の定義を探したところ、worker.rb の self.backend=(backend) で別名をつけてそうでした。
https://github.com/collectiveidea/delayed_job/blob/eaef62d844ea980433b807a496136bbd0a6ea625/lib/delayed/worker.rb#L65-L73
def self.backend=(backend) if backend.is_a? Symbol require "delayed/serialization/#{backend}" require "delayed/backend/#{backend}" backend = "Delayed::Backend::#{backend.to_s.classify}::Job".constantize end @@backend = backend # rubocop:disable ClassVars silence_warnings { ::Delayed.const_set(:Job, backend) } end
今回の場合だと delayed_job_active_record gemを使っているため、 Delayed::Backend::ActiveRecord::Job が該当しそうです。
https://github.com/collectiveidea/delayed_job_active_record/blob/e5b4fbf8c65698d575d1afc95e4ae55e4646d24e/lib/delayed/backend/active_record.rb#L4-L33
module Delayed module Backend module ActiveRecord # A job object that is persisted to the database. # Contains the work object as a YAML field. class Job < ::ActiveRecord::Base # ...
JobWrapper#display_nameをパッチする
パッチする際は Rails.application.config.filter_parametersを使います。
また、Jobの引数のトップレベルがHashやArrayであっても、 filter_parameters で定義されているキーに対してはフィルタリングを行いたいです。
ActiveSupport::ParameterFilter の定義を見ると、 #filter の中で #callを呼んでいました。
https://github.com/rails/rails/blob/2b4feede1b6ead327b40c3415749de58af896e05/activesupport/lib/active_support/parameter_filter.rb#L83-L85
def filter(params) @no_filters ? params.dup : call(params) end
#call を確認すると、トップレベルをHashにしておけば、あとはいい感じにやってくれそうでした。
https://github.com/rails/rails/blob/2b4feede1b6ead327b40c3415749de58af896e05/activesupport/lib/active_support/parameter_filter.rb#L125-L133
def call(params, full_parent_key = nil, original_params = params) filtered_params = params.class.new params.each do |key, value| filtered_params[key] = value_for_key(key, value, full_parent_key, original_params) end filtered_params end
そこで、 #filter を呼ぶときにどんな型が来ても args キーを持つHashとなるようにします。
filtered_arguments = filter.filter({ args: job_data["arguments"] })[:args]
また、ActiveJobと異なり、ActiveSupport.on_load が使えないことから、 Rails.application.config.after_initialize でパッチを当てることにします。
パッチの全体はこんな感じです。
Rails.application.config.after_initialize do # DelayedJob の JobWrapper が存在する場合のみ、パッチを行う if defined?(ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper) require "active_job/queue_adapters/delayed_job_adapter" # パッチ用のモジュールを定義 module DelayedJobArgumentsFilter def display_name base_name = "#{job_data["job_class"]} [#{job_data["job_id"]}] from DelayedJob(#{job_data["queue_name"]})" return base_name unless log_arguments? filter = ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters) filtered_arguments = filter.filter({ args: job_data["arguments"] })[:args] "#{base_name} with arguments: #{filtered_arguments.inspect}" end end # ターゲットのクラスが確実に存在する状態で、モンキーパッチを行う prepend # 対象のクラスの前にモジュールが挿入されるため、クラスに同名メソッドがあったとしても、モジュールのほうが優先して使用される ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.prepend(DelayedJobArgumentsFilter) end end
動作確認
再度 curl で動作確認します。すると、フィルタリングされた状態でログへ出力されました。
[Worker(*** pid:37897)] Job MyJob [7ab16f93-9265-4510-b9ea-35e20c85d82c] from DelayedJob(default) with arguments: [[{"request" => {"token" => "[FILTERED]", "_aj_symbol_keys" => "[FILTERED]"}, "_aj_symbol_keys" => "[FILTERED]"}]] (id=25) (queue=default) RUNNING
[Worker(*** pid:37897)] Job MyJob [7ab16f93-9265-4510-b9ea-35e20c85d82c] from DelayedJob(default) with arguments: [[{"request" => {"token" => "[FILTERED]", "_aj_symbol_keys" => "[FILTERED]"}, "_aj_symbol_keys" => "[FILTERED]"}]] (id=25) (queue=default) COMPLETED after 0.0083
テストコードを書く
毎回 curl で動作確認をするのは手間なので、各パターンのテストコードを書きます。今回はRSpecで書くため、セットアップします。
% bin/rails g rspec:install
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
また、Jobの動作確認で perform_enqueued_jobs を使いたいため、 ActiveJob::TestHelper を rails_helper.rb でincludeします。
config.include ActiveJob::TestHelper, type: :job
log_arguments=falseのテストコード
テストで確認しやすくするため、標準出力を StringIO へと差し替えます。
around do |example| # 元の設定を退避 original_base_logger = ActiveJob::Base.logger original_subscriber_logger = ActiveJob::LogSubscriber.logger original_stdout = $stdout # ロガーと標準出力を StringIO に差し替え ActiveJob::Base.logger = logger ActiveJob::LogSubscriber.logger = logger $stdout = log_output example.run ensure # テスト終了後に設定を元に戻す ActiveJob::Base.logger = original_base_logger ActiveJob::LogSubscriber.logger = original_subscriber_logger $stdout = original_stdout end
log_arguments=false の場合は、ログにJobの引数が表示されないことを確認します。
なお、ログ出力のタイミングはエンキュー時と実行時の2つあるため、テストの中でエンキューと実行のできる perform_enqueued_jobs を使います。
https://api.rubyonrails.org/v8.1/classes/ActiveJob/TestHelper.html#method-i-perform_enqueued_jobs
RSpec.describe MySecretJob, type: :job do describe "引数の非表示化の検証" do # 参考: # ログの出力タイミングはエンキュー時・実行時などがあるが、ActiveJob::LogSubscriberはどちらのタイミングでも動作する # ここでは、常にログへ出力されないことを確認するため、perform_enqueued_jobs + perform_laterを使っている it "Hash形式の引数がログや標準出力に表示されないこと" do payload = { token: "my_secret_data", body: "my_data" } # ジョブをエンキューして実行 perform_enqueued_jobs do MySecretJob.perform_later(payload) end logs = log_output.string # ジョブがエンキュー・実行されたこと自体のログは出力されることを確認 expect(logs).to include("Enqueued MySecretJob") expect(logs).to include("Performing MySecretJob") # log_arguments = false なので、引数自体がログに含まれないこと expect(logs).not_to include("my_secret_data") expect(logs).not_to include("my_data") expect(logs).not_to include('token') expect(logs).not_to include('[FILTERED]') end # ... end
ActiveJobのテストコード
こちらも log_arguments=false と同じく、ログの出力先を StrngIOに差し替えつつ、テストコードを書きます。
以下のパターンでは適切にフィルタリングされていることを確認しています。
it "Hashの場合に、tokenキーのみがフィルタリングされていること" do perform_enqueued_jobs do MyJob.perform_later({ token: "PII", body: "my_data" }) end logs = log_output.string expect(logs).to include("Enqueued MyJob") expect(logs).to include("Performing MyJob") expect(logs).to include('token: "[FILTERED]"') expect(logs).to include('body: "my_data"') end
Delayed::Jobのテストコード
Delayed::Jobに対するテストコードでは、ログの出力先の差し替えに加えて queue_adapter も差し替えます。テストコードでは test adapterになるためです。
around do |example| old_adapter = ActiveJob::Base.queue_adapter ActiveJob::Base.queue_adapter = :delayed_job # ... ensure ActiveJob::Base.queue_adapter = old_adapter end
queue_adapterを delayed_job とした場合、Delayed::Jobのジョブは実行されません。
そこで、テストコードの中で明示的に Delayed::Worker.new.work_off してジョブを実行します。
it "Hashの場合に、tokenキーのみがフィルタリングされていること" do MyJob.perform_later({ token: "PII", body: "my_data" }) enqueued_job = Delayed::Job.last display_name = enqueued_job.payload_object.display_name expect(display_name).to include('"token" => "[FILTERED]"') expect(display_name).to include('"body" => "my_data"') Delayed::Worker.new.work_off logs = log_output.string expect(logs).to include("Enqueued MyJob") expect(logs).to include("Performing MyJob") expect(logs).to include('token: "[FILTERED]"') expect(logs).to include('body: "my_data"') end
ソースコード
GitHubに上げました。
https://github.com/thinkAmi-sandbox/parameter_filter_job-example
今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/parameter_filter_job-example/pull/1