From caa8fbc766f67e945836e6f78bc61ce98d8e0288 Mon Sep 17 00:00:00 2001 From: Noemi Lapresta Date: Mon, 13 Jul 2026 15:37:56 +0200 Subject: [PATCH 1/3] Merge the enqueue changesets into one The last few releases added enqueue instrumentation one library at a time, each with its own changeset. They now ship together in one minor release, so combine them into a single changeset that lists every library. No changelog entry is lost, it is just told as one story. --- ...add-delayed-job-enqueue-instrumentation.md | 8 ------- .../add-shoryuken-enqueue-instrumentation.md | 8 ------- .../emit-mongodb-body-as-json-string.md | 6 ----- .changesets/instrument-active-job-enqueues.md | 10 -------- .../instrument-background-job-enqueues.md | 24 +++++++++++++++++++ .changesets/instrument-que-enqueues.md | 6 ----- .changesets/instrument-resque-enqueues.md | 8 ------- .changesets/instrument-sidekiq-enqueues.md | 8 ------- ...itle-sidekiq-delayed-enqueues-by-target.md | 9 ------- 9 files changed, 24 insertions(+), 63 deletions(-) delete mode 100644 .changesets/add-delayed-job-enqueue-instrumentation.md delete mode 100644 .changesets/add-shoryuken-enqueue-instrumentation.md delete mode 100644 .changesets/emit-mongodb-body-as-json-string.md delete mode 100644 .changesets/instrument-active-job-enqueues.md create mode 100644 .changesets/instrument-background-job-enqueues.md delete mode 100644 .changesets/instrument-que-enqueues.md delete mode 100644 .changesets/instrument-resque-enqueues.md delete mode 100644 .changesets/instrument-sidekiq-enqueues.md delete mode 100644 .changesets/title-sidekiq-delayed-enqueues-by-target.md diff --git a/.changesets/add-delayed-job-enqueue-instrumentation.md b/.changesets/add-delayed-job-enqueue-instrumentation.md deleted file mode 100644 index b6b5a640f..000000000 --- a/.changesets/add-delayed-job-enqueue-instrumentation.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -bump: minor -type: add ---- - -Instrument Delayed Job enqueues. Enqueuing a job now records an -`enqueue.delayed_job` event on the active transaction, so enqueues made from -within a web request or another job show up in the event timeline. diff --git a/.changesets/add-shoryuken-enqueue-instrumentation.md b/.changesets/add-shoryuken-enqueue-instrumentation.md deleted file mode 100644 index 1a3ef60ae..000000000 --- a/.changesets/add-shoryuken-enqueue-instrumentation.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -bump: minor -type: add ---- - -Instrument Shoryuken job enqueues. Enqueuing a job now records an -`enqueue.shoryuken` event on the active transaction, so enqueues made from -within a web request or another job show up in the event timeline. diff --git a/.changesets/emit-mongodb-body-as-json-string.md b/.changesets/emit-mongodb-body-as-json-string.md deleted file mode 100644 index c6ce93e9f..000000000 --- a/.changesets/emit-mongodb-body-as-json-string.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -bump: patch -type: change ---- - -Emit the MongoDB query event body as a JSON string instead of a structured data object. The recorded query body is unchanged. diff --git a/.changesets/instrument-active-job-enqueues.md b/.changesets/instrument-active-job-enqueues.md deleted file mode 100644 index bd36d1eac..000000000 --- a/.changesets/instrument-active-job-enqueues.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -bump: patch -type: change ---- - -Record the `enqueue.active_job` event from AppSignal's own Active Job -instrumentation rather than from Rails' native `enqueue.active_job` -notification, which is now suppressed so the enqueue is recorded once. The -event still shows up on the active transaction when enqueuing from within a web -request or another job, and is now titled after the job being enqueued. diff --git a/.changesets/instrument-background-job-enqueues.md b/.changesets/instrument-background-job-enqueues.md new file mode 100644 index 000000000..dcd9c5eb7 --- /dev/null +++ b/.changesets/instrument-background-job-enqueues.md @@ -0,0 +1,24 @@ +--- +bump: minor +type: add +--- + +Instrument background job enqueues. Enqueuing a job now records an enqueue +event on the active transaction, so enqueues made from within a web request or +another job show up in the event timeline. This is recorded for Sidekiq +(`enqueue.sidekiq`), Que (`enqueue.que`, plus `bulk_enqueue.que` for bulk +enqueues on Que 2), Resque (`enqueue.resque`), Shoryuken (`enqueue.shoryuken`) +and Delayed Job (`enqueue.delayed_job`). Each event is titled after the job +being enqueued. + +For Active Job, the `enqueue.active_job` event is now recorded by AppSignal's +own instrumentation rather than by Rails' native `enqueue.active_job` +notification. The native notification is suppressed so the enqueue is recorded +once, and the event is now titled after the job being enqueued. + +These enqueue events can be turned off with the +`enable_job_enqueue_instrumentation` config option. Set it to `false` to stop +recording enqueue events across all integrations, without affecting the +instrumentation of the jobs themselves. It defaults to `true` and can also be +set through the `APPSIGNAL_ENABLE_JOB_ENQUEUE_INSTRUMENTATION` environment +variable. diff --git a/.changesets/instrument-que-enqueues.md b/.changesets/instrument-que-enqueues.md deleted file mode 100644 index 0c342568b..000000000 --- a/.changesets/instrument-que-enqueues.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -bump: minor -type: add ---- - -Instrument Que job enqueues. AppSignal now records an `enqueue.que` event when a Que job is enqueued, and a `bulk_enqueue.que` event for bulk enqueues on Que 2, so the enqueue shows up in the transaction timeline. diff --git a/.changesets/instrument-resque-enqueues.md b/.changesets/instrument-resque-enqueues.md deleted file mode 100644 index 5744a13d7..000000000 --- a/.changesets/instrument-resque-enqueues.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -bump: minor -type: add ---- - -Instrument Resque job enqueues. Enqueuing a job now records an `enqueue.resque` -event on the active transaction, so enqueues made from within a web request or -another job show up in the event timeline. diff --git a/.changesets/instrument-sidekiq-enqueues.md b/.changesets/instrument-sidekiq-enqueues.md deleted file mode 100644 index f56342664..000000000 --- a/.changesets/instrument-sidekiq-enqueues.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -bump: minor -type: add ---- - -Instrument Sidekiq job enqueues. Enqueuing a job now records an -`enqueue.sidekiq` event on the active transaction, so enqueues made from within -a web request or another job show up in the event timeline. diff --git a/.changesets/title-sidekiq-delayed-enqueues-by-target.md b/.changesets/title-sidekiq-delayed-enqueues-by-target.md deleted file mode 100644 index 9a5d086a2..000000000 --- a/.changesets/title-sidekiq-delayed-enqueues-by-target.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -bump: patch -type: change ---- - -Title `enqueue.sidekiq` events for Sidekiq delayed extension jobs after the -delayed target and method, such as `enqueue MyClass.my_method job`, instead of -the internal wrapper class. This matches the name the job is given when it -later performs, so the enqueue and perform of the same job read the same name. From d899994e23e32d6afac6ab457b905399a8ea68e3 Mon Sep 17 00:00:00 2001 From: Noemi Lapresta Date: Mon, 13 Jul 2026 15:38:22 +0200 Subject: [PATCH 2/3] Add config options to control instrumentation Add an instrument option for each background job framework and library that lacked one. Sidekiq, Shoryuken, Que, Resque, Delayed Job, Active Job, Excon and MongoDB can now each be turned off through instrument_, matching the existing options for Faraday, http.rb, Net::HTTP, Redis and Sequel. Setting one to false makes the hook report its dependencies as absent, so the integration never installs. That turns off both the job or request instrumentation and the enqueue instrumentation for that framework. Add the enable_job_enqueue_instrumentation option. When it is false, job_enqueue_events_suppressed? reports true, which every enqueue integration already checks before recording its event. This reuses the existing enqueue suppression path, so one option turns the enqueue events off everywhere without touching how the jobs themselves are instrumented. The Active Job integration did not check that flag for its own event yet, so it now does, the same way the standalone adapters do. --- .changesets/add-instrument-config-options.md | 13 +++++++ lib/appsignal/config.rb | 37 +++++++++++++++++++ lib/appsignal/hooks/active_job.rb | 12 +++++- lib/appsignal/hooks/delayed_job.rb | 3 +- lib/appsignal/hooks/excon.rb | 2 +- lib/appsignal/hooks/mongo_ruby_driver.rb | 3 +- lib/appsignal/hooks/que.rb | 2 +- lib/appsignal/hooks/resque.rb | 2 +- lib/appsignal/hooks/shoryuken.rb | 2 +- lib/appsignal/hooks/sidekiq.rb | 3 +- lib/appsignal/transaction.rb | 5 +++ sig/appsignal.rbi | 36 ++++++++++++++++++ sig/appsignal.rbs | 27 ++++++++++++++ spec/lib/appsignal/config_spec.rb | 27 ++++++++++++++ spec/lib/appsignal/hooks/activejob_spec.rb | 28 +++++++++++++- spec/lib/appsignal/hooks/delayed_job_spec.rb | 8 ++++ spec/lib/appsignal/hooks/excon_spec.rb | 9 ++++- .../appsignal/hooks/mongo_ruby_driver_spec.rb | 8 ++++ spec/lib/appsignal/hooks/que_spec.rb | 12 +++++- spec/lib/appsignal/hooks/resque_spec.rb | 12 +++++- spec/lib/appsignal/hooks/shoryuken_spec.rb | 8 ++++ spec/lib/appsignal/hooks/sidekiq_spec.rb | 12 +++++- spec/lib/appsignal/transaction_spec.rb | 16 ++++++++ 23 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 .changesets/add-instrument-config-options.md diff --git a/.changesets/add-instrument-config-options.md b/.changesets/add-instrument-config-options.md new file mode 100644 index 000000000..bfe2fade8 --- /dev/null +++ b/.changesets/add-instrument-config-options.md @@ -0,0 +1,13 @@ +--- +bump: minor +type: add +--- + +Add config options to turn individual integrations off. Set +`instrument_sidekiq`, `instrument_shoryuken`, `instrument_que`, +`instrument_resque`, `instrument_delayed_job`, `instrument_active_job`, +`instrument_excon` or `instrument_mongo` to `false` to disable that +integration entirely. This turns off both the instrumentation of the jobs or +requests and the enqueue instrumentation for that integration. They all +default to `true`. Each can also be set through its environment variable, such +as `APPSIGNAL_INSTRUMENT_SIDEKIQ`. diff --git a/lib/appsignal/config.rb b/lib/appsignal/config.rb index b938342d1..f688a3f13 100644 --- a/lib/appsignal/config.rb +++ b/lib/appsignal/config.rb @@ -96,6 +96,7 @@ def dsl_config_file? :enable_at_exit_hook => "on_error", :enable_at_exit_reporter => true, :enable_host_metrics => true, + :enable_job_enqueue_instrumentation => true, :enable_minutely_probes => true, :enable_statsd => true, :enable_nginx_metrics => false, @@ -113,13 +114,21 @@ def dsl_config_file? :ignore_errors => [], :ignore_logs => [], :ignore_namespaces => [], + :instrument_active_job => true, :instrument_code_ownership => true, + :instrument_delayed_job => true, + :instrument_excon => true, :instrument_faraday => true, :instrument_http_rb => true, + :instrument_mongo => true, :instrument_net_http => true, :instrument_ownership => true, + :instrument_que => true, :instrument_redis => true, + :instrument_resque => true, :instrument_sequel => true, + :instrument_shoryuken => true, + :instrument_sidekiq => true, :log => "file", :logging_endpoint => "https://appsignal-endpoint.net", :ownership_set_namespace => false, @@ -181,6 +190,8 @@ def dsl_config_file? :enable_allocation_tracking => "APPSIGNAL_ENABLE_ALLOCATION_TRACKING", :enable_at_exit_reporter => "APPSIGNAL_ENABLE_AT_EXIT_REPORTER", :enable_host_metrics => "APPSIGNAL_ENABLE_HOST_METRICS", + :enable_job_enqueue_instrumentation => + "APPSIGNAL_ENABLE_JOB_ENQUEUE_INSTRUMENTATION", :enable_minutely_probes => "APPSIGNAL_ENABLE_MINUTELY_PROBES", :enable_statsd => "APPSIGNAL_ENABLE_STATSD", :enable_nginx_metrics => "APPSIGNAL_ENABLE_NGINX_METRICS", @@ -192,13 +203,21 @@ def dsl_config_file? :enable_rake_performance_instrumentation => "APPSIGNAL_ENABLE_RAKE_PERFORMANCE_INSTRUMENTATION", :files_world_accessible => "APPSIGNAL_FILES_WORLD_ACCESSIBLE", + :instrument_active_job => "APPSIGNAL_INSTRUMENT_ACTIVE_JOB", :instrument_code_ownership => "APPSIGNAL_INSTRUMENT_CODE_OWNERSHIP", + :instrument_delayed_job => "APPSIGNAL_INSTRUMENT_DELAYED_JOB", + :instrument_excon => "APPSIGNAL_INSTRUMENT_EXCON", :instrument_faraday => "APPSIGNAL_INSTRUMENT_FARADAY", :instrument_http_rb => "APPSIGNAL_INSTRUMENT_HTTP_RB", + :instrument_mongo => "APPSIGNAL_INSTRUMENT_MONGO", :instrument_net_http => "APPSIGNAL_INSTRUMENT_NET_HTTP", :instrument_ownership => "APPSIGNAL_INSTRUMENT_OWNERSHIP", + :instrument_que => "APPSIGNAL_INSTRUMENT_QUE", :instrument_redis => "APPSIGNAL_INSTRUMENT_REDIS", + :instrument_resque => "APPSIGNAL_INSTRUMENT_RESQUE", :instrument_sequel => "APPSIGNAL_INSTRUMENT_SEQUEL", + :instrument_shoryuken => "APPSIGNAL_INSTRUMENT_SHORYUKEN", + :instrument_sidekiq => "APPSIGNAL_INSTRUMENT_SIDEKIQ", :ownership_set_namespace => "APPSIGNAL_OWNERSHIP_SET_NAMESPACE", :running_in_container => "APPSIGNAL_RUNNING_IN_CONTAINER", :send_environment_metadata => "APPSIGNAL_SEND_ENVIRONMENT_METADATA", @@ -828,6 +847,8 @@ def activate_if_environment(*envs) # @return [Boolean] Configure whether the at_exit reporter is enabled # @!attribute [rw] enable_host_metrics # @return [Boolean] Configure whether host metrics collection is enabled + # @!attribute [rw] enable_job_enqueue_instrumentation + # @return [Boolean] Configure whether to record an event when a background job is enqueued # @!attribute [rw] enable_minutely_probes # @return [Boolean] Configure whether minutely probes are enabled # @!attribute [rw] enable_statsd @@ -846,18 +867,34 @@ def activate_if_environment(*envs) # @return [Boolean] Configure whether Rake performance instrumentation is enabled # @!attribute [rw] files_world_accessible # @return [Boolean] Configure whether files created by AppSignal should be world accessible + # @!attribute [rw] instrument_active_job + # @return [Boolean] Configure whether to instrument Active Job + # @!attribute [rw] instrument_delayed_job + # @return [Boolean] Configure whether to instrument Delayed Job + # @!attribute [rw] instrument_excon + # @return [Boolean] Configure whether to instrument requests made with the Excon gem # @!attribute [rw] instrument_faraday # @return [Boolean] Configure whether to instrument requests made with the Faraday gem # @!attribute [rw] instrument_http_rb # @return [Boolean] Configure whether to instrument requests made with the http.rb gem + # @!attribute [rw] instrument_mongo + # @return [Boolean] Configure whether to instrument MongoDB queries # @!attribute [rw] instrument_net_http # @return [Boolean] Configure whether to instrument requests made with Net::HTTP # @!attribute [rw] instrument_ownership # @return [Boolean] Configure whether to instrument the Ownership gem + # @!attribute [rw] instrument_que + # @return [Boolean] Configure whether to instrument Que # @!attribute [rw] instrument_redis # @return [Boolean] Configure whether to instrument Redis queries + # @!attribute [rw] instrument_resque + # @return [Boolean] Configure whether to instrument Resque # @!attribute [rw] instrument_sequel # @return [Boolean] Configure whether to instrument Sequel queries + # @!attribute [rw] instrument_shoryuken + # @return [Boolean] Configure whether to instrument Shoryuken + # @!attribute [rw] instrument_sidekiq + # @return [Boolean] Configure whether to instrument Sidekiq # @!attribute [rw] ownership_set_namespace # @return [Boolean] Configure whether the Ownership gem instrumentation should set namespace # @!attribute [rw] running_in_container diff --git a/lib/appsignal/hooks/active_job.rb b/lib/appsignal/hooks/active_job.rb index 2b6446d0b..b49c8e18d 100644 --- a/lib/appsignal/hooks/active_job.rb +++ b/lib/appsignal/hooks/active_job.rb @@ -22,7 +22,8 @@ def self.dependencies_present? end def dependencies_present? - self.class.dependencies_present? + self.class.dependencies_present? && Appsignal.config && + Appsignal.config[:instrument_active_job] end def install @@ -56,6 +57,15 @@ def install # @!visibility private module ActiveJobEnqueueInstrumentation def enqueue(*, **) + # Skip recording the event when enqueue events are suppressed. That is + # the case when enqueue instrumentation is disabled, and it keeps this + # integration consistent with the standalone adapters (Sidekiq, ...), + # which already gate their own enqueue event on this check. + if Appsignal::Transaction.current? && + Appsignal::Transaction.current.job_enqueue_events_suppressed? + return super + end + Appsignal.instrument("enqueue.active_job", "enqueue #{self.class.name} job") do # Active Job enqueues through an adapter (Sidekiq, Resque, ...) that # has its own enqueue instrumentation. Suppress it so the enqueue is diff --git a/lib/appsignal/hooks/delayed_job.rb b/lib/appsignal/hooks/delayed_job.rb index 8406fbdd9..f70573814 100644 --- a/lib/appsignal/hooks/delayed_job.rb +++ b/lib/appsignal/hooks/delayed_job.rb @@ -7,7 +7,8 @@ class DelayedJobHook < Appsignal::Hooks::Hook register :delayed_job def dependencies_present? - defined?(::Delayed::Plugin) + defined?(::Delayed::Plugin) && Appsignal.config && + Appsignal.config[:instrument_delayed_job] end def install diff --git a/lib/appsignal/hooks/excon.rb b/lib/appsignal/hooks/excon.rb index ff09de8bf..4a3a1216a 100644 --- a/lib/appsignal/hooks/excon.rb +++ b/lib/appsignal/hooks/excon.rb @@ -7,7 +7,7 @@ class ExconHook < Appsignal::Hooks::Hook register :excon def dependencies_present? - Appsignal.config && defined?(::Excon) + Appsignal.config && defined?(::Excon) && Appsignal.config[:instrument_excon] end def install diff --git a/lib/appsignal/hooks/mongo_ruby_driver.rb b/lib/appsignal/hooks/mongo_ruby_driver.rb index 98259ec28..7ca2e5dc7 100644 --- a/lib/appsignal/hooks/mongo_ruby_driver.rb +++ b/lib/appsignal/hooks/mongo_ruby_driver.rb @@ -7,7 +7,8 @@ class MongoRubyDriverHook < Appsignal::Hooks::Hook register :mongo_ruby_driver def dependencies_present? - defined?(::Mongo::Monitoring::Global) + defined?(::Mongo::Monitoring::Global) && Appsignal.config && + Appsignal.config[:instrument_mongo] end def install diff --git a/lib/appsignal/hooks/que.rb b/lib/appsignal/hooks/que.rb index 22b77e5a1..84494b57b 100644 --- a/lib/appsignal/hooks/que.rb +++ b/lib/appsignal/hooks/que.rb @@ -7,7 +7,7 @@ class QueHook < Appsignal::Hooks::Hook register :que def dependencies_present? - defined?(::Que::Job) + defined?(::Que::Job) && Appsignal.config && Appsignal.config[:instrument_que] end def install diff --git a/lib/appsignal/hooks/resque.rb b/lib/appsignal/hooks/resque.rb index 01183fd25..75cc6ad59 100644 --- a/lib/appsignal/hooks/resque.rb +++ b/lib/appsignal/hooks/resque.rb @@ -7,7 +7,7 @@ class ResqueHook < Appsignal::Hooks::Hook register :resque def dependencies_present? - defined?(::Resque) + defined?(::Resque) && Appsignal.config && Appsignal.config[:instrument_resque] end def install diff --git a/lib/appsignal/hooks/shoryuken.rb b/lib/appsignal/hooks/shoryuken.rb index 4ab7cd205..82d9dce88 100644 --- a/lib/appsignal/hooks/shoryuken.rb +++ b/lib/appsignal/hooks/shoryuken.rb @@ -7,7 +7,7 @@ class ShoryukenHook < Appsignal::Hooks::Hook register :shoryuken def dependencies_present? - defined?(::Shoryuken) + defined?(::Shoryuken) && Appsignal.config && Appsignal.config[:instrument_shoryuken] end def install diff --git a/lib/appsignal/hooks/sidekiq.rb b/lib/appsignal/hooks/sidekiq.rb index f78a0839f..62178323e 100644 --- a/lib/appsignal/hooks/sidekiq.rb +++ b/lib/appsignal/hooks/sidekiq.rb @@ -20,7 +20,8 @@ def self.dependencies_present? end def dependencies_present? - self.class.dependencies_present? + self.class.dependencies_present? && Appsignal.config && + Appsignal.config[:instrument_sidekiq] end def install diff --git a/lib/appsignal/transaction.rb b/lib/appsignal/transaction.rb index de328628d..c901c275b 100644 --- a/lib/appsignal/transaction.rb +++ b/lib/appsignal/transaction.rb @@ -329,6 +329,11 @@ def suppress_job_enqueue_events # @!visibility private def job_enqueue_events_suppressed? + # When enqueue instrumentation is disabled, every enqueue integration + # treats its event as suppressed. That is how the config option turns the + # enqueue events off across all integrations at once. + return true if Appsignal.config && !Appsignal.config[:enable_job_enqueue_instrumentation] + store("job_enqueue")[:suppressed] == true end diff --git a/sig/appsignal.rbi b/sig/appsignal.rbi index ac31143d8..35b78c669 100644 --- a/sig/appsignal.rbi +++ b/sig/appsignal.rbi @@ -1182,6 +1182,10 @@ module Appsignal sig { returns(T::Boolean) } attr_accessor :enable_host_metrics + # _@return_ — Configure whether to record an event when a background job is enqueued + sig { returns(T::Boolean) } + attr_accessor :enable_job_enqueue_instrumentation + # _@return_ — Configure whether minutely probes are enabled sig { returns(T::Boolean) } attr_accessor :enable_minutely_probes @@ -1218,6 +1222,18 @@ module Appsignal sig { returns(T::Boolean) } attr_accessor :files_world_accessible + # _@return_ — Configure whether to instrument Active Job + sig { returns(T::Boolean) } + attr_accessor :instrument_active_job + + # _@return_ — Configure whether to instrument Delayed Job + sig { returns(T::Boolean) } + attr_accessor :instrument_delayed_job + + # _@return_ — Configure whether to instrument requests made with the Excon gem + sig { returns(T::Boolean) } + attr_accessor :instrument_excon + # _@return_ — Configure whether to instrument requests made with the Faraday gem sig { returns(T::Boolean) } attr_accessor :instrument_faraday @@ -1226,6 +1242,10 @@ module Appsignal sig { returns(T::Boolean) } attr_accessor :instrument_http_rb + # _@return_ — Configure whether to instrument MongoDB queries + sig { returns(T::Boolean) } + attr_accessor :instrument_mongo + # _@return_ — Configure whether to instrument requests made with Net::HTTP sig { returns(T::Boolean) } attr_accessor :instrument_net_http @@ -1234,14 +1254,30 @@ module Appsignal sig { returns(T::Boolean) } attr_accessor :instrument_ownership + # _@return_ — Configure whether to instrument Que + sig { returns(T::Boolean) } + attr_accessor :instrument_que + # _@return_ — Configure whether to instrument Redis queries sig { returns(T::Boolean) } attr_accessor :instrument_redis + # _@return_ — Configure whether to instrument Resque + sig { returns(T::Boolean) } + attr_accessor :instrument_resque + # _@return_ — Configure whether to instrument Sequel queries sig { returns(T::Boolean) } attr_accessor :instrument_sequel + # _@return_ — Configure whether to instrument Shoryuken + sig { returns(T::Boolean) } + attr_accessor :instrument_shoryuken + + # _@return_ — Configure whether to instrument Sidekiq + sig { returns(T::Boolean) } + attr_accessor :instrument_sidekiq + # _@return_ — Configure whether the Ownership gem instrumentation should set namespace sig { returns(T::Boolean) } attr_accessor :ownership_set_namespace diff --git a/sig/appsignal.rbs b/sig/appsignal.rbs index b2d4a0a91..7e08531b7 100644 --- a/sig/appsignal.rbs +++ b/sig/appsignal.rbs @@ -1105,6 +1105,9 @@ module Appsignal # _@return_ — Configure whether host metrics collection is enabled attr_accessor enable_host_metrics: bool + # _@return_ — Configure whether to record an event when a background job is enqueued + attr_accessor enable_job_enqueue_instrumentation: bool + # _@return_ — Configure whether minutely probes are enabled attr_accessor enable_minutely_probes: bool @@ -1132,24 +1135,48 @@ module Appsignal # _@return_ — Configure whether files created by AppSignal should be world accessible attr_accessor files_world_accessible: bool + # _@return_ — Configure whether to instrument Active Job + attr_accessor instrument_active_job: bool + + # _@return_ — Configure whether to instrument Delayed Job + attr_accessor instrument_delayed_job: bool + + # _@return_ — Configure whether to instrument requests made with the Excon gem + attr_accessor instrument_excon: bool + # _@return_ — Configure whether to instrument requests made with the Faraday gem attr_accessor instrument_faraday: bool # _@return_ — Configure whether to instrument requests made with the http.rb gem attr_accessor instrument_http_rb: bool + # _@return_ — Configure whether to instrument MongoDB queries + attr_accessor instrument_mongo: bool + # _@return_ — Configure whether to instrument requests made with Net::HTTP attr_accessor instrument_net_http: bool # _@return_ — Configure whether to instrument the Ownership gem attr_accessor instrument_ownership: bool + # _@return_ — Configure whether to instrument Que + attr_accessor instrument_que: bool + # _@return_ — Configure whether to instrument Redis queries attr_accessor instrument_redis: bool + # _@return_ — Configure whether to instrument Resque + attr_accessor instrument_resque: bool + # _@return_ — Configure whether to instrument Sequel queries attr_accessor instrument_sequel: bool + # _@return_ — Configure whether to instrument Shoryuken + attr_accessor instrument_shoryuken: bool + + # _@return_ — Configure whether to instrument Sidekiq + attr_accessor instrument_sidekiq: bool + # _@return_ — Configure whether the Ownership gem instrumentation should set namespace attr_accessor ownership_set_namespace: bool diff --git a/spec/lib/appsignal/config_spec.rb b/spec/lib/appsignal/config_spec.rb index c5a2fb8c7..27d3fa446 100644 --- a/spec/lib/appsignal/config_spec.rb +++ b/spec/lib/appsignal/config_spec.rb @@ -717,6 +717,7 @@ def on_load :enable_gvl_global_timer => false, :enable_gvl_waiting_threads => false, :enable_host_metrics => false, + :enable_job_enqueue_instrumentation => false, :enable_minutely_probes => false, :enable_nginx_metrics => false, :enable_rails_error_reporter => false, @@ -735,13 +736,21 @@ def on_load :ignore_errors => ["ExampleStandardError", "AnotherError"], :ignore_logs => ["^start$", "^Completed 2.* in .*ms (.*)"], :ignore_namespaces => ["admin", "private_namespace"], + :instrument_active_job => false, :instrument_code_ownership => false, + :instrument_delayed_job => false, + :instrument_excon => false, :instrument_faraday => false, :instrument_http_rb => false, + :instrument_mongo => false, :instrument_net_http => false, :instrument_ownership => false, + :instrument_que => false, :instrument_redis => false, + :instrument_resque => false, :instrument_sequel => false, + :instrument_shoryuken => false, + :instrument_sidekiq => false, :log => "file", :log_level => "debug", :log_path => "/tmp/something", @@ -791,6 +800,7 @@ def on_load "APPSIGNAL_ENABLE_GVL_GLOBAL_TIMER" => "false", "APPSIGNAL_ENABLE_GVL_WAITING_THREADS" => "false", "APPSIGNAL_ENABLE_HOST_METRICS" => "false", + "APPSIGNAL_ENABLE_JOB_ENQUEUE_INSTRUMENTATION" => "false", "APPSIGNAL_ENABLE_MINUTELY_PROBES" => "false", "APPSIGNAL_ENABLE_NGINX_METRICS" => "false", "APPSIGNAL_ENABLE_RAILS_ERROR_REPORTER" => "false", @@ -798,13 +808,21 @@ def on_load "APPSIGNAL_ENABLE_RAKE_PERFORMANCE_INSTRUMENTATION" => "false", "APPSIGNAL_ENABLE_STATSD" => "false", "APPSIGNAL_FILES_WORLD_ACCESSIBLE" => "false", + "APPSIGNAL_INSTRUMENT_ACTIVE_JOB" => "false", "APPSIGNAL_INSTRUMENT_CODE_OWNERSHIP" => "false", + "APPSIGNAL_INSTRUMENT_DELAYED_JOB" => "false", + "APPSIGNAL_INSTRUMENT_EXCON" => "false", "APPSIGNAL_INSTRUMENT_FARADAY" => "false", "APPSIGNAL_INSTRUMENT_HTTP_RB" => "false", + "APPSIGNAL_INSTRUMENT_MONGO" => "false", "APPSIGNAL_INSTRUMENT_NET_HTTP" => "false", "APPSIGNAL_INSTRUMENT_OWNERSHIP" => "false", + "APPSIGNAL_INSTRUMENT_QUE" => "false", "APPSIGNAL_INSTRUMENT_REDIS" => "false", + "APPSIGNAL_INSTRUMENT_RESQUE" => "false", "APPSIGNAL_INSTRUMENT_SEQUEL" => "false", + "APPSIGNAL_INSTRUMENT_SHORYUKEN" => "false", + "APPSIGNAL_INSTRUMENT_SIDEKIQ" => "false", "APPSIGNAL_OWNERSHIP_SET_NAMESPACE" => "true", "APPSIGNAL_RUNNING_IN_CONTAINER" => "true", "APPSIGNAL_SEND_ENVIRONMENT_METADATA" => "false", @@ -926,6 +944,7 @@ def on_load :enable_gvl_global_timer => true, :enable_gvl_waiting_threads => true, :enable_host_metrics => true, + :enable_job_enqueue_instrumentation => true, :enable_minutely_probes => true, :enable_statsd => true, :enable_nginx_metrics => false, @@ -941,13 +960,21 @@ def on_load :ignore_errors => [], :ignore_logs => [], :ignore_namespaces => [], + :instrument_active_job => true, :instrument_code_ownership => true, + :instrument_delayed_job => true, + :instrument_excon => true, :instrument_faraday => true, :instrument_http_rb => true, + :instrument_mongo => true, :instrument_net_http => true, :instrument_ownership => true, + :instrument_que => true, :instrument_redis => true, + :instrument_resque => true, :instrument_sequel => true, + :instrument_shoryuken => true, + :instrument_sidekiq => true, :log => "file", :logging_endpoint => "https://appsignal-endpoint.net", :name => "TestApp", diff --git a/spec/lib/appsignal/hooks/activejob_spec.rb b/spec/lib/appsignal/hooks/activejob_spec.rb index 9d2a8e8ff..6a092ac87 100644 --- a/spec/lib/appsignal/hooks/activejob_spec.rb +++ b/spec/lib/appsignal/hooks/activejob_spec.rb @@ -9,7 +9,17 @@ context "when ActiveJob constant is found" do before { stub_const "ActiveJob", Class.new } - it { is_expected.to be_truthy } + context "when Active Job instrumentation is enabled" do + before { configure } + + it { is_expected.to be_truthy } + end + + context "when Active Job instrumentation is disabled" do + before { configure(:options => { :instrument_active_job => false }) } + + it { is_expected.to be_falsy } + end end context "when ActiveJob constant is not found" do @@ -385,6 +395,22 @@ def perform(*_args) expect(suppressed_during_enqueue).to be(true) end end + + context "when enqueue instrumentation is disabled" do + let(:options) { { :enable_job_enqueue_instrumentation => false } } + + it "does not record an enqueue event but still enqueues the job" do + transaction = http_request_transaction + set_current_transaction(transaction) + + ActiveJobTestJob.perform_later + + enqueue_events = + transaction.to_h["events"].select { |event| event["name"] == "enqueue.active_job" } + expect(enqueue_events).to be_empty + expect(ActiveJob::Base.queue_adapter.enqueued_jobs.count).to eq(1) + end + end end context "with params" do diff --git a/spec/lib/appsignal/hooks/delayed_job_spec.rb b/spec/lib/appsignal/hooks/delayed_job_spec.rb index fd448be29..ae6e5e084 100644 --- a/spec/lib/appsignal/hooks/delayed_job_spec.rb +++ b/spec/lib/appsignal/hooks/delayed_job_spec.rb @@ -1,5 +1,6 @@ describe Appsignal::Hooks::DelayedJobHook do context "with delayed job" do + let(:options) { {} } before do stub_const("Delayed::Plugin", Class.new do def self.callbacks @@ -10,6 +11,7 @@ def self.plugins @plugins ||= [] end end) + configure(:options => options) # Install the hook directly rather than through `start_agent`. Hooks # install once per process and are never reset, so relying on # `start_agent` made "adds the plugin" pass only when this spec was the @@ -23,6 +25,12 @@ def self.plugins subject { described_class.new.dependencies_present? } it { is_expected.to be_truthy } + + context "when Delayed Job instrumentation is disabled" do + let(:options) { { :instrument_delayed_job => false } } + + it { is_expected.to be_falsy } + end end it "adds the plugin" do diff --git a/spec/lib/appsignal/hooks/excon_spec.rb b/spec/lib/appsignal/hooks/excon_spec.rb index bf18e522a..4052faf3f 100644 --- a/spec/lib/appsignal/hooks/excon_spec.rb +++ b/spec/lib/appsignal/hooks/excon_spec.rb @@ -1,5 +1,6 @@ describe Appsignal::Hooks::ExconHook do - before { start_agent } + let(:options) { {} } + before { start_agent(:options => options) } context "with Excon" do before do @@ -15,6 +16,12 @@ def self.defaults subject { described_class.new.dependencies_present? } it { is_expected.to be_truthy } + + context "when Excon instrumentation is disabled" do + let(:options) { { :instrument_excon => false } } + + it { is_expected.to be_falsy } + end end describe "#install" do diff --git a/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb b/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb index 9c9a78df8..32e3f75a4 100644 --- a/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +++ b/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb @@ -2,6 +2,7 @@ require "appsignal/integrations/mongo_ruby_driver" context "with mongo ruby driver" do + let(:options) { {} } let(:subscriber) { Appsignal::Hooks::MongoMonitorSubscriber.new } before do allow(Appsignal::Hooks::MongoMonitorSubscriber).to receive(:new).and_return(subscriber) @@ -14,12 +15,19 @@ def subscribe end end) + configure(:options => options) end describe "#dependencies_present?" do subject { described_class.new.dependencies_present? } it { is_expected.to be_truthy } + + context "when MongoDB instrumentation is disabled" do + let(:options) { { :instrument_mongo => false } } + + it { is_expected.to be_falsy } + end end it "adds a subscriber to Mongo::Monitoring" do diff --git a/spec/lib/appsignal/hooks/que_spec.rb b/spec/lib/appsignal/hooks/que_spec.rb index 503c71c4b..8bd1647ea 100644 --- a/spec/lib/appsignal/hooks/que_spec.rb +++ b/spec/lib/appsignal/hooks/que_spec.rb @@ -3,7 +3,17 @@ describe "#dependencies_present?" do subject { described_class.new.dependencies_present? } - it { is_expected.to be_truthy } + context "when Que instrumentation is enabled" do + before { configure } + + it { is_expected.to be_truthy } + end + + context "when Que instrumentation is disabled" do + before { configure(:options => { :instrument_que => false }) } + + it { is_expected.to be_falsy } + end end it "installs the QuePlugin" do diff --git a/spec/lib/appsignal/hooks/resque_spec.rb b/spec/lib/appsignal/hooks/resque_spec.rb index 9ca5badb6..5cac30875 100644 --- a/spec/lib/appsignal/hooks/resque_spec.rb +++ b/spec/lib/appsignal/hooks/resque_spec.rb @@ -5,7 +5,17 @@ context "when Resque is loaded" do before { stub_const "Resque", 1 } - it { is_expected.to be_truthy } + context "when Resque instrumentation is enabled" do + before { configure } + + it { is_expected.to be_truthy } + end + + context "when Resque instrumentation is disabled" do + before { configure(:options => { :instrument_resque => false }) } + + it { is_expected.to be_falsy } + end end context "when Resque is not loaded" do diff --git a/spec/lib/appsignal/hooks/shoryuken_spec.rb b/spec/lib/appsignal/hooks/shoryuken_spec.rb index 3ccf6ff20..bcefc89e7 100644 --- a/spec/lib/appsignal/hooks/shoryuken_spec.rb +++ b/spec/lib/appsignal/hooks/shoryuken_spec.rb @@ -1,5 +1,6 @@ describe Appsignal::Hooks::ShoryukenHook do context "with shoryuken" do + let(:options) { {} } before do stub_const("Shoryuken", Module.new do def self.configure_server @@ -8,6 +9,7 @@ def self.configure_server def self.configure_client end end) + configure(:options => options) Appsignal::Hooks::ShoryukenHook.new.install end @@ -15,6 +17,12 @@ def self.configure_client subject { described_class.new.dependencies_present? } it { is_expected.to be_truthy } + + context "when Shoryuken instrumentation is disabled" do + let(:options) { { :instrument_shoryuken => false } } + + it { is_expected.to be_falsy } + end end end diff --git a/spec/lib/appsignal/hooks/sidekiq_spec.rb b/spec/lib/appsignal/hooks/sidekiq_spec.rb index 7a2ab9c27..c374aca33 100644 --- a/spec/lib/appsignal/hooks/sidekiq_spec.rb +++ b/spec/lib/appsignal/hooks/sidekiq_spec.rb @@ -5,7 +5,17 @@ context "when Sidekiq constant is found" do before { stub_const "Sidekiq", Class.new } - it { is_expected.to be_truthy } + context "when Sidekiq instrumentation is enabled" do + before { configure } + + it { is_expected.to be_truthy } + end + + context "when Sidekiq instrumentation is disabled" do + before { configure(:options => { :instrument_sidekiq => false }) } + + it { is_expected.to be_falsy } + end end context "when Sidekiq constant is not found" do diff --git a/spec/lib/appsignal/transaction_spec.rb b/spec/lib/appsignal/transaction_spec.rb index d3ab930dd..eae3aed2d 100644 --- a/spec/lib/appsignal/transaction_spec.rb +++ b/spec/lib/appsignal/transaction_spec.rb @@ -716,6 +716,22 @@ end end + describe "#job_enqueue_events_suppressed?" do + let(:transaction) { new_transaction } + + it "is not suppressed by default" do + expect(transaction.job_enqueue_events_suppressed?).to be(false) + end + + context "when enqueue instrumentation is disabled" do + let(:options) { { :enable_job_enqueue_instrumentation => false } } + + it "reports enqueue events as suppressed" do + expect(transaction.job_enqueue_events_suppressed?).to be(true) + end + end + end + describe "#add_params" do let(:transaction) { new_transaction } From 5aa8bf0a495e8c98c1c4c4b9e79def891bf0938e Mon Sep 17 00:00:00 2001 From: Noemi Lapresta Date: Mon, 13 Jul 2026 15:38:32 +0200 Subject: [PATCH 3/3] Update diagnose tests for the new config options The diagnose output lists every config option, so the diagnose test expectations have to include the new instrument options and the enable_job_enqueue_instrumentation option. Point the submodule at the diagnose_tests commit that adds them. --- spec/integration/diagnose | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/diagnose b/spec/integration/diagnose index 1e98e7d5e..8a4bcb7f7 160000 --- a/spec/integration/diagnose +++ b/spec/integration/diagnose @@ -1 +1 @@ -Subproject commit 1e98e7d5ef86f0bf3631d1b45fdfdc86f9ced736 +Subproject commit 8a4bcb7f70090cf8439ad204d366389b88593350