Skip to content

Commit 8c55fee

Browse files
Add support for ActiveSupport::EventReporter (#1481)
* Add Appsignal::Integrations::ActiveSupportEventReporter Rails 8.1 adds ActiveSupport::EventReporter, an interface for reporting structured events. To catch these, report them as info-level logs in the rails_events category through the newly added Appsignal::Integrations::ActiveSupportEventReporter. * Update .changesets/report-events-from-rails-8-1-s-activesupport--eventreporter-as-logs.md Co-authored-by: Tom de Bruijn <[email protected]> * Update spec/lib/appsignal/hooks/active_support_event_reporter_spec.rb Co-authored-by: Tom de Bruijn <[email protected]> * Update spec/lib/appsignal/hooks/active_support_event_reporter_spec.rb Co-authored-by: Tom de Bruijn <[email protected]> * Add enable_active_support_event_reporter config Add a configuration option to allow users to opt out of the ActiveSupport::EventReporter integration. The option is enabled by default and can be disabled via APPSIGNAL_ENABLE_ACTIVE_SUPPORT_EVENT_REPORTER or enable_active_support_event_reporter. * Rename enable_active_support_event_reporter config option Renames the configuration option from `enable_active_support_event_reporter` to `enable_active_support_event_log_reporter` to better reflect its purpose of reporting events as logs. Changes: - Updated config default and BOOLEAN_OPTIONS mapping - Updated environment variable name to APPSIGNAL_ENABLE_ACTIVE_SUPPORT_EVENT_LOG_REPORTER - Updated YARD documentation - Updated all test fixtures and specs * Format active_support_event_reporter and tests * Fix ActiveSupport EventReporter hook for non-Rails apps The ActiveSupportEventReporterHook was attempting to call Rails.event.subscribe even in non-Rails environments where Rails is not defined, causing a NameError that would be logged as an error during hook installation. This caused test failures when running the grape gemfile suite. The fix adds a check for Rails being defined in the dependencies_present? method, ensuring the hook only attempts to install in Rails applications where Rails.event is available. This prevents the hook from trying to install in applications that use ActiveSupport (like Grape) but don't have Rails defined. * Update type signatures for new config option Add enable_active_support_event_log_reporter config option to both RBI and RBS type signature files. * Add enable_active_support_event_log_reporter to tests Add enable_active_support_event_log_reporter configuration option to the diagnose integration test expectations. * Fix rubocop line length issues in diagnose spec Break long regex patterns onto separate lines to fix Layout/LineLength rubocop offenses. * Fix ActiveSupport EventReporter hook test for non-Rails apps Update the test to properly handle the case where ActiveSupport is loaded without Rails. The test now correctly mocks the event reporter and its subscribers array to verify the hook installation works in non-Rails environments. * Use DependencyHelper for test conditional consistency Replace the ad-hoc `defined?(::Rails)` check with `DependencyHelper.rails8_1_present?` to ensure the #install test block only runs in appropriate Rails 8.1+ environments. This fixes test failures in JRuby with the no_dependencies gemfile where Rails may not be properly defined. * Fix diagnose spec JSON error pattern for JRuby Update the regex patterns in the diagnose spec to handle JRuby's JSON parse error messages. JRuby returns "unexpected token at 'invalid..." with the full invalid content in the quote, while MRI Ruby returns "unexpected token at 'invalid'" with just the first token. Changed the pattern from matching a closing quote after 'invalid' to just matching the word 'invalid', making it compatible with both MRI Ruby and JRuby error message formats. --------- Co-authored-by: Tom de Bruijn <[email protected]>
1 parent 4c69a0a commit 8c55fee

File tree

13 files changed

+181
-5
lines changed

13 files changed

+181
-5
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
bump: minor
3+
type: add
4+
---
5+
6+
Report events from Rails 8.1's Structured Event Reporting (`ActiveSupport::EventReporter`) as logs.

lib/appsignal/config.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def dsl_config_file?
102102
:enable_gvl_global_timer => true,
103103
:enable_gvl_waiting_threads => true,
104104
:enable_rails_error_reporter => true,
105+
:enable_active_support_event_log_reporter => true,
105106
:enable_rake_performance_instrumentation => false,
106107
:endpoint => "https://push.appsignal.com",
107108
:files_world_accessible => true,
@@ -184,6 +185,8 @@ def dsl_config_file?
184185
:enable_gvl_global_timer => "APPSIGNAL_ENABLE_GVL_GLOBAL_TIMER",
185186
:enable_gvl_waiting_threads => "APPSIGNAL_ENABLE_GVL_WAITING_THREADS",
186187
:enable_rails_error_reporter => "APPSIGNAL_ENABLE_RAILS_ERROR_REPORTER",
188+
:enable_active_support_event_log_reporter =>
189+
"APPSIGNAL_ENABLE_ACTIVE_SUPPORT_EVENT_LOG_REPORTER",
187190
:enable_rake_performance_instrumentation =>
188191
"APPSIGNAL_ENABLE_RAKE_PERFORMANCE_INSTRUMENTATION",
189192
:files_world_accessible => "APPSIGNAL_FILES_WORLD_ACCESSIBLE",
@@ -802,6 +805,8 @@ def activate_if_environment(*envs)
802805
# @return [Boolean] Configure whether GVL waiting threads instrumentation is enabled
803806
# @!attribute [rw] enable_rails_error_reporter
804807
# @return [Boolean] Configure whether Rails error reporter integration is enabled
808+
# @!attribute [rw] enable_active_support_event_log_reporter
809+
# @return [Boolean] Configure whether ActiveSupport::EventReporter integration is enabled
805810
# @!attribute [rw] enable_rake_performance_instrumentation
806811
# @return [Boolean] Configure whether Rake performance instrumentation is enabled
807812
# @!attribute [rw] files_world_accessible

lib/appsignal/hooks.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def truncate(text)
7979
require "appsignal/hooks/action_mailer"
8080
require "appsignal/hooks/active_job"
8181
require "appsignal/hooks/active_support_notifications"
82+
require "appsignal/hooks/active_support_event_reporter"
8283
require "appsignal/hooks/celluloid"
8384
require "appsignal/hooks/code_ownership"
8485
require "appsignal/hooks/delayed_job"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
module Appsignal
4+
class Hooks
5+
# @!visibility private
6+
class ActiveSupportEventReporterHook < Appsignal::Hooks::Hook
7+
register :active_support_event_reporter
8+
9+
def dependencies_present?
10+
defined?(::Rails) &&
11+
defined?(::ActiveSupport::EventReporter) &&
12+
Appsignal.config &&
13+
Appsignal.config[:enable_active_support_event_log_reporter]
14+
end
15+
16+
def install
17+
require "appsignal/integrations/active_support_event_reporter"
18+
Rails.event.subscribe(Appsignal::Integrations::ActiveSupportEventReporter::Subscriber.new)
19+
end
20+
end
21+
end
22+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module Appsignal
4+
module Integrations
5+
# @!visibility private
6+
module ActiveSupportEventReporter
7+
class Subscriber
8+
def initialize
9+
@logger = Appsignal::Logger.new("rails_events")
10+
end
11+
12+
def emit(event)
13+
@logger.info(event[:name], event[:payload])
14+
end
15+
end
16+
end
17+
end
18+
end

sig/appsignal.rbi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,10 @@ module Appsignal
12061206
sig { returns(T::Boolean) }
12071207
attr_accessor :enable_rails_error_reporter
12081208

1209+
# _@return_ — Configure whether ActiveSupport::EventReporter integration is enabled
1210+
sig { returns(T::Boolean) }
1211+
attr_accessor :enable_active_support_event_log_reporter
1212+
12091213
# _@return_ — Configure whether Rake performance instrumentation is enabled
12101214
sig { returns(T::Boolean) }
12111215
attr_accessor :enable_rake_performance_instrumentation

sig/appsignal.rbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,9 @@ module Appsignal
11231123
# _@return_ — Configure whether Rails error reporter integration is enabled
11241124
attr_accessor enable_rails_error_reporter: bool
11251125

1126+
# _@return_ — Configure whether ActiveSupport::EventReporter integration is enabled
1127+
attr_accessor enable_active_support_event_log_reporter: bool
1128+
11261129
# _@return_ — Configure whether Rake performance instrumentation is enabled
11271130
attr_accessor enable_rake_performance_instrumentation: bool
11281131

spec/lib/appsignal/cli/diagnose_spec.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,13 +490,19 @@ def dont_accept_prompt_to_send_diagnostics_report
490490
"Agent diagnostics",
491491
" Error while parsing agent diagnostics report:",
492492
" Output: invalid agent\njson"
493-
expect(output)
494-
.to match(/Error:( \d+:)? unexpected (token at|character:) 'invalid/)
493+
# Ruby 3.x / JRuby: "unexpected token at 'invalid...'"
494+
# Ruby 4.x: "unexpected character: 'invalid'" or "source is not valid JSON!"
495+
error_pattern =
496+
/Error:.*(?:unexpected (?:token at|character:) 'invalid|source is not valid JSON!)/
497+
expect(output).to match(error_pattern)
495498
end
496499

497500
it "adds the output to the report" do
498-
expect(received_report["agent"]["error"])
499-
.to match(/unexpected (token at|character:) 'invalid/)
501+
# Ruby 3.x / JRuby: "unexpected token at 'invalid...'"
502+
# Ruby 4.x: "unexpected character: 'invalid'" or "source is not valid JSON!"
503+
error_pattern =
504+
/(?:unexpected (?:token at|character:) 'invalid|source is not valid JSON!)/
505+
expect(received_report["agent"]["error"]).to match(error_pattern)
500506
expect(received_report["agent"]["output"]).to eq(["invalid agent", "json"])
501507
end
502508
end

spec/lib/appsignal/config_spec.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ def on_load
720720
:enable_minutely_probes => false,
721721
:enable_nginx_metrics => false,
722722
:enable_rails_error_reporter => false,
723+
:enable_active_support_event_log_reporter => false,
723724
:enable_rake_performance_instrumentation => false,
724725
:enable_statsd => false,
725726
:endpoint => "https://test.appsignal.com",
@@ -792,6 +793,7 @@ def on_load
792793
"APPSIGNAL_ENABLE_MINUTELY_PROBES" => "false",
793794
"APPSIGNAL_ENABLE_NGINX_METRICS" => "false",
794795
"APPSIGNAL_ENABLE_RAILS_ERROR_REPORTER" => "false",
796+
"APPSIGNAL_ENABLE_ACTIVE_SUPPORT_EVENT_LOG_REPORTER" => "false",
795797
"APPSIGNAL_ENABLE_RAKE_PERFORMANCE_INSTRUMENTATION" => "false",
796798
"APPSIGNAL_ENABLE_STATSD" => "false",
797799
"APPSIGNAL_FILES_WORLD_ACCESSIBLE" => "false",
@@ -926,6 +928,7 @@ def on_load
926928
:enable_statsd => true,
927929
:enable_nginx_metrics => false,
928930
:enable_rails_error_reporter => true,
931+
:enable_active_support_event_log_reporter => true,
929932
:enable_rake_performance_instrumentation => false,
930933
:endpoint => "https://push.appsignal.com",
931934
:files_world_accessible => true,

0 commit comments

Comments
 (0)