Skip to content
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@

The experimental feature, Configurable Security Policies (CSP), is no longer supported and has been removed. [PR#3292](https://github.com/newrelic/newrelic-ruby-agent/pull/3292)

- **Feature: Add Active Support notification allowlist configuration option**

A new configuration option, `instrumentation.active_support_notifications.active_support_events`, allows users to define an allowlist of Active
Support notifications event names for the agent to subscribe to. By default, the agent subscribes to all [Active Support: Caching](https://guides.rubyonrails.org/active_support_instrumentation.html#active-support-caching) and [Active Support: Messages](https://guides.rubyonrails.org/active_support_instrumentation.html#active-support-messages) events. [PR#3327](https://github.com/newrelic/newrelic-ruby-agent/pull/3327)

- **Feature: Use Ruby's built-in Gzip compression**

The agent now uses the built-in `Zlib.gzip` method from the Ruby standard library for compression, replacing the previous custom implementation. [PR#3332](https://github.com/newrelic/newrelic-ruby-agent/pull/3332)
Expand Down
19 changes: 19 additions & 0 deletions lib/new_relic/agent/configuration/default_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

require 'forwardable'
require_relative '../../constants'
require_relative '../instrumentation/active_support_subscriber'

module NewRelic
module Agent
Expand Down Expand Up @@ -1495,6 +1496,24 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:description => 'Configures the TCP/IP port for the trace observer Host'
},
# Instrumentation
:'instrumentation.active_support_notifications.active_support_events' => {
:default => NewRelic::Agent::Instrumentation::ActiveSupportSubscriber::EVENT_NAME_TO_METHOD_NAME.keys,
:public => true,
:type => Array,
:allowed_from_server => false,
:description => <<~ACTIVE_SUPPORT_EVENTS.chomp.tr("\n", ' ')
An allowlist array of Active Support notifications events specific to the Active Support library
itself that the agent should subscribe to. The Active Support library specific events focus primarily
on caching. Any event name not included in this list will be ignored by the agent. Provide complete event
names such as 'cache_fetch_hit.active_support'. Do not provide asterisks or regex patterns, and do not
escape any characters with backslashes.

For a complete list of all possible Active Support event names, see the
[list of caching names](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#active-support-caching)
and the [list of messages names](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#active-support-messages)
from the official Rails documentation.
ACTIVE_SUPPORT_EVENTS
},
:'instrumentation.active_support_broadcast_logger' => {
:default => instrumentation_value_from_boolean(:'application_logging.enabled'),
:documentation_default => 'auto',
Expand Down
9 changes: 8 additions & 1 deletion lib/new_relic/agent/instrumentation/active_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
DependencyDetection.defer do
named :active_support

EVENT_NAMES_PARAMETER = :'instrumentation.active_support_notifications.active_support_events'

depends_on do
!NewRelic::Agent.config[:disable_active_support]
end
Expand All @@ -16,12 +18,17 @@
!NewRelic::Agent::Instrumentation::ActiveSupportSubscriber.subscribed?
end

depends_on do
!NewRelic::Agent.config[EVENT_NAMES_PARAMETER].empty?
end

executes do
NewRelic::Agent.logger.info('Installing ActiveSupport instrumentation')
end

executes do
ActiveSupport::Notifications.subscribe(/\.active_support$/,
event_names = NewRelic::Agent.config[EVENT_NAMES_PARAMETER].map { |n| Regexp.escape(n) }.join('|')
ActiveSupport::Notifications.subscribe(/\A(?:#{event_names})\z/,
NewRelic::Agent::Instrumentation::ActiveSupportSubscriber.new)
end
end
36 changes: 22 additions & 14 deletions lib/new_relic/agent/instrumentation/active_support_subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,30 @@
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require 'new_relic/agent/instrumentation/notifications_subscriber'
require_relative 'notifications_subscriber'

module NewRelic
module Agent
module Instrumentation
class ActiveSupportSubscriber < NotificationsSubscriber
EVENT_NAME_TO_METHOD_NAME = {
'cache_fetch_hit.active_support' => 'fetch_hit',
'cache_generate.active_support' => 'generate',
'cache_read.active_support' => 'read',
'cache_write.active_support' => 'write',
'cache_delete.active_support' => 'delete',
'cache_exist?.active_support' => 'exist?',
'cache_read_multi.active_support' => 'read_multi',
'cache_write_multi.active_support' => 'write_multi',
'cache_delete_multi.active_support' => 'delete_multi',
'cache_delete_matched.active_support' => 'delete_matched',
'cache_cleanup.active_support' => 'cleanup',
'cache_increment.active_support' => 'increment',
'cache_decrement.active_support' => 'decrement',
'cache_prune.active_support' => 'prune',
'message_serializer_fallback.active_support' => 'message_serializer_fallback'
}.freeze

def add_segment_params(segment, payload)
segment.params[:key] = payload[:key]
segment.params[:store] = payload[:store]
Expand All @@ -18,22 +36,12 @@ def add_segment_params(segment, payload)

def metric_name(name, payload)
store = payload[:store]
method = method_from_name(name)
method = method_name(name)
"Ruby/ActiveSupport#{"/#{store}" if store}/#{method}"
end

PATTERN = /\Acache_([^\.]*)\.active_support\z/

METHOD_NAME_MAPPING = Hash.new do |h, k|
if PATTERN =~ k
h[k] = $1
else
h[k] = NewRelic::UNKNOWN
end
end

def method_from_name(name)
METHOD_NAME_MAPPING[name]
def method_name(name)
EVENT_NAME_TO_METHOD_NAME.fetch(name, name.delete_prefix('cache_').delete_suffix('.active_support'))
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ def test_metric_recorded_for_new_event_names
end

def test_failsafe_if_event_does_not_match_expected_pattern
name = 'charcuterie_build_a_board_workshop'
in_transaction('test') do
generate_event('charcuterie_build_a_board_workshop')
generate_event(name)
end

assert_metrics_recorded "#{METRIC_PREFIX}#{DEFAULT_STORE}/Unknown"
assert_metrics_recorded "#{METRIC_PREFIX}#{DEFAULT_STORE}/#{name}"
end

def test_key_recorded_as_attribute_on_traces
Expand Down