Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscribe to additional Action controller topics #1744

Merged
merged 40 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4607b3c
Subscribe to render_layout
hannahramadan Dec 30, 2022
a4d709f
WIP add subscribers for additional
tannalynn Jan 17, 2023
37f5010
add test for halted_callback
tannalynn Jan 19, 2023
d16512f
updated names/paths
tannalynn Jan 19, 2023
30346c9
added unpermitted parameter test
tannalynn Jan 19, 2023
4d82aa9
moved generic start and finish to parent class
tannalynn Jan 19, 2023
9224ab5
added parameters to tests
tannalynn Jan 20, 2023
d24827c
move action controller that aren't process_action
tannalynn Jan 20, 2023
a6ad626
Merge branch 'dev' into action_controller_subscriber_additional_topic
tannalynn Jan 20, 2023
ee1c789
moved test for start and finish into
tannalynn Jan 20, 2023
e2d9788
removed start and finish from subclasses that are
tannalynn Jan 20, 2023
7d1caec
rename file
tannalynn Jan 20, 2023
97da112
fix for rails 6 tests
tannalynn Jan 20, 2023
39ad8b2
skip when topic not available in rails version
tannalynn Jan 20, 2023
30b9939
removed binding.irb
tannalynn Jan 20, 2023
247bf5a
fix Gem::Version.new
tannalynn Jan 20, 2023
92402a8
update to match all rails version filter
tannalynn Jan 20, 2023
5363967
pr feedback updates
tannalynn Jan 20, 2023
fbccb6e
fix spelling
tannalynn Jan 20, 2023
512bfea
Merge branch 'action_controller_subscriber_additional_topic' into NR1…
hannahramadan Jan 20, 2023
319e972
Add test and refactor
hannahramadan Jan 20, 2023
c834c46
Add test for when there is no segment
hannahramadan Jan 20, 2023
e675cb8
added test for not implemented methods
tannalynn Jan 20, 2023
7e3a4f8
Protect for unknown action view topics
hannahramadan Jan 20, 2023
bbeff88
DRYed up notification subscriber some more
tannalynn Jan 21, 2023
57d6f61
Merge pull request #1748 from newrelic/NR1512_subscribe_actionview
hannahramadan Jan 21, 2023
8a1b890
Merge branch 'dev' into action_controller_subscriber_additional_topic
tannalynn Jan 21, 2023
ac50f7c
DRYed up notification subscriber subclasses
tannalynn Jan 21, 2023
7172943
Merge branch 'action_controller_subscriber_additional_topic' of githu…
tannalynn Jan 21, 2023
acf7507
comment out hanging test
tannalynn Jan 23, 2023
1e7e1bc
add test for segment getting created by
tannalynn Jan 23, 2023
66c6dd9
disable weird rubocop failure
tannalynn Jan 23, 2023
5fd2e2a
Merge branch 'dev' into action_controller_subscriber_additional_topic
tannalynn Jan 23, 2023
1c6ddda
added config
tannalynn Jan 23, 2023
f7a252f
Use NewRelic::UNKNOWN const
hannahramadan Jan 23, 2023
ac8f980
Merge pull request #1755 from newrelic/actionview-updates
hannahramadan Jan 24, 2023
55ed14f
fix for frozen segment name in action mailer
tannalynn Jan 24, 2023
6eb12b3
test fixes
tannalynn Jan 24, 2023
3a82667
rubocop
tannalynn Jan 24, 2023
a41dba4
don't test action mailer on ruby 2.3
tannalynn Jan 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This file is distributed under New Relic's license terms.
# 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 'new_relic/agent/instrumentation/ignore_actions'
require 'new_relic/agent/parameter_filtering'

module NewRelic
module Agent
module Instrumentation
class ActionControllerOtherSubscriber < NotificationsSubscriber
def start_segment(name, id, payload)
segment = Tracer.start_segment(name: metric_name(name, payload))

add_segment_params(segment, payload, name)
push_segment(id, segment)
end

def add_segment_params(segment, payload, name)
segment.params[:filter] = payload[:filter] if payload[:filter]
segment.params[:keys] = payload[:keys] if payload[:keys]
segment.params[:original_path] = payload[:request].original_fullpath if payload[:request]

if payload[:context]
segment.params[:action] = payload[:context][:action]
segment.params[:controller] = payload[:context][:controller].to_s
fallwith marked this conversation as resolved.
Show resolved Hide resolved
end
end

def metric_name(name, payload)
controller_path = controller_name_for_metric(payload)
"Ruby/ActionController#{"/#{controller_path}" if controller_path}/#{name.gsub(/\.action_controller/, '')}"
fallwith marked this conversation as resolved.
Show resolved Hide resolved
end

def controller_name_for_metric(payload)
# redirect_to
return payload[:request].controller_class.controller_path if payload[:request] && payload[:request].controller_class

# unpermitted_parameters
::NewRelic::LanguageSupport.constantize(payload[:context][:controller]).controller_path if payload[:context] && payload[:context][:controller]
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,6 @@ module NewRelic
module Agent
module Instrumentation
class ActiveStorageSubscriber < NotificationsSubscriber
def start(name, id, payload)
return unless state.is_execution_traced?

start_segment(name, id, payload)
rescue => e
log_notification_error(e, name, 'start')
end

def finish(name, id, payload)
return unless state.is_execution_traced?

finish_segment(id, payload)
rescue => e
log_notification_error(e, name, 'finish')
end

def start_segment(name, id, payload)
segment = Tracer.start_segment(name: metric_name(name, payload))
segment.params[:key] = payload[:key]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,6 @@ module NewRelic
module Agent
module Instrumentation
class ActiveSupportSubscriber < NotificationsSubscriber
def start(name, id, payload)
return unless state.is_execution_traced?

start_segment(name, id, payload)
rescue => e
log_notification_error(e, name, 'start')
end

def finish(name, id, payload)
return unless state.is_execution_traced?

finish_segment(id, payload)
rescue => e
log_notification_error(e, name, 'finish')
end

def start_segment(name, id, payload)
segment = Tracer.start_segment(name: metric_name(name, payload))

Expand Down
24 changes: 24 additions & 0 deletions lib/new_relic/agent/instrumentation/notifications_subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,30 @@ def self.subscribe(pattern)
end
end

def start(name, id, payload)
return unless state.is_execution_traced?

start_segment(name, id, payload)
rescue => e
log_notification_error(e, name, 'start')
end

def finish(name, id, payload)
return unless state.is_execution_traced?

finish_segment(id, payload)
rescue => e
log_notification_error(e, name, 'finish')
end

def start_segment(name, id, payload)
raise NotImplementedError
end

def finish_segment(name, id, payload)
tannalynn marked this conversation as resolved.
Show resolved Hide resolved
raise NotImplementedError
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great cleanup, thank you.

tannalynn marked this conversation as resolved.
Show resolved Hide resolved

def log_notification_error(error, name, event_type)
# These are important enough failures that we want the backtraces
# logged at error level, hence the explicit log_exception call.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# frozen_string_literal: true

require 'new_relic/agent/instrumentation/action_controller_subscriber'
require 'new_relic/agent/instrumentation/action_controller_other_subscriber'
require 'new_relic/agent/prepend_supportability'

DependencyDetection.defer do
Expand All @@ -29,5 +30,14 @@

NewRelic::Agent::Instrumentation::ActionControllerSubscriber \
.subscribe(/^process_action.action_controller$/)

subs = %w[send_file
send_data
redirect_to
halted_callback
unpermitted_parameters]

NewRelic::Agent::Instrumentation::ActionControllerOtherSubscriber \
.subscribe(Regexp.new("^(#{subs.join('|')}).action_controller$"))
fallwith marked this conversation as resolved.
Show resolved Hide resolved
end
end
86 changes: 86 additions & 0 deletions test/multiverse/suites/rails/action_controller_other_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require './app'

if defined?(ActionController::Live)

class DataController < ApplicationController
# send_file
def send_test_file
send_file(Rails.root + "../../../../README.md")
end

# send_data
def send_test_data
send_data("wow its a adata")
end

# halted_callback
before_action :do_a_redirect, only: :halt_my_callback
def halt_my_callback; end

# redirect_to
def do_a_redirect
redirect_to("http://foo.bar/")
end

# unpermitted_parameters
def not_allowed
params.permit(:only_this)
end
end

class ActionControllerDataTest < ActionDispatch::IntegrationTest
include MultiverseHelpers

def teardown
NewRelic::Agent.drop_buffered_data
end

def test_send_file
get('/data/send_test_file')

assert_metrics_recorded(['Controller/data/send_test_file', 'Ruby/ActionController/send_file'])
end

def test_send_data
get('/data/send_test_data')

assert_metrics_recorded(['Controller/data/send_test_data', 'Ruby/ActionController/send_data'])
end

def test_halted_callback
get('/data/halt_my_callback')

trace = last_transaction_trace
tt_node = find_node_with_name(trace, 'Ruby/ActionController/halted_callback')

assert_equal(:do_a_redirect, tt_node.params[:filter])
assert_metrics_recorded(['Controller/data/halt_my_callback', 'Ruby/ActionController/halted_callback'])
end

def test_redirect_to
get('/data/do_a_redirect')

trace = last_transaction_trace
tt_node = find_node_with_name(trace, 'Ruby/ActionController/data/redirect_to')

assert_equal('/data/do_a_redirect', tt_node.params[:original_path])
assert_metrics_recorded(['Controller/data/do_a_redirect', 'Ruby/ActionController/data/redirect_to'])
end

def test_unpermitted_parameters
get('/data/not_allowed', params: {this_is_a_param: 1})

trace = last_transaction_trace
tt_node = find_node_with_name(trace, 'Ruby/ActionController/data/unpermitted_parameters')

assert_equal(['this_is_a_param'], tt_node.params[:keys])
assert_equal('not_allowed', tt_node.params[:action])
assert_equal('DataController', tt_node.params[:controller])
assert_metrics_recorded(['Controller/data/not_allowed', 'Ruby/ActionController/data/unpermitted_parameters'])
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

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

if defined?(ActiveSupport)
require_relative 'rails/notifications_subscriber'
else
puts "Skipping tests in #{__FILE__} because Active Support is unavailable"
end
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,6 @@ def test_hit_recorded_as_attribute_on_traces
trace = last_transaction_trace
tt_node = find_node_with_name(trace, "#{METRIC_PREFIX}#{DEFAULT_STORE}/read")

puts txn.segments.last.inspect

assert tt_node.params.key?(:hit)
refute tt_node.params[:hit]
end
Expand All @@ -116,8 +114,6 @@ def test_super_operation_recorded_as_attribute_on_traces
trace = last_transaction_trace
tt_node = find_node_with_name(trace, "#{METRIC_PREFIX}#{DEFAULT_STORE}/read")

puts txn.segments.last.inspect
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for cleaning up these puts statements! Sorry I forgot to remove them!


assert tt_node.params.key?(:super_operation)
refute tt_node.params[:super_operation]
end
Expand Down Expand Up @@ -163,42 +159,6 @@ def test_pop_segment_returns_false
end
end

def test_start_logs_notification_error
Copy link
Contributor Author

@tannalynn tannalynn Jan 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved these tests into the notifications_subscriber.rb since these are testing what is now shared code

logger = MiniTest::Mock.new

NewRelic::Agent.stub :logger, logger do
logger.expect :error, nil, [/Error during .* callback/]
logger.expect :log_exception, nil, [:error, ArgumentError]

in_transaction do |txn|
@subscriber.stub :start_segment, -> { raise 'kaboom' } do
@subscriber.start(DEFAULT_EVENT, @id, {})
end

assert_equal 1, txn.segments.size
end
end
logger.verify
end

def test_finish_logs_notification_error
logger = MiniTest::Mock.new

NewRelic::Agent.stub :logger, logger do
logger.expect :error, nil, [/Error during .* callback/]
logger.expect :log_exception, nil, [:error, ArgumentError]

in_transaction do |txn|
@subscriber.stub :finish_segment, -> { raise 'kaboom' } do
@subscriber.finish(DEFAULT_EVENT, @id, {})
end

assert_equal 1, txn.segments.size
end
end
logger.verify
end

def test_an_actual_active_storage_cache_write
unless defined?(ActiveSupport::VERSION::MAJOR) && ActiveSupport::VERSION::MAJOR >= 5
skip 'Test restricted to Active Support v5+'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require_relative '../../../../test_helper'

module NewRelic
module Agent
module Instrumentation
class NotificationsSubscriberTest < Minitest::Test
DEFAULT_EVENT = "any.event"

def setup
nr_freeze_process_time
@subscriber = NotificationsSubscriber.new
NewRelic::Agent.drop_buffered_data
@id = fake_guid(32)
end

def teardown
NewRelic::Agent.drop_buffered_data
end

def test_start_logs_notification_error
logger = MiniTest::Mock.new

NewRelic::Agent.stub :logger, logger do
logger.expect :error, nil, [/Error during .* callback/]
logger.expect :log_exception, nil, [:error, ArgumentError]

in_transaction do |txn|
@subscriber.stub :start_segment, -> { raise 'kaboom' } do
@subscriber.start(DEFAULT_EVENT, @id, {})
end

assert_equal 1, txn.segments.size
end
end
logger.verify
end

def test_finish_logs_notification_error
logger = MiniTest::Mock.new

NewRelic::Agent.stub :logger, logger do
logger.expect :error, nil, [/Error during .* callback/]
logger.expect :log_exception, nil, [:error, ArgumentError]

in_transaction do |txn|
@subscriber.stub :finish_segment, -> { raise 'kaboom' } do
@subscriber.finish(DEFAULT_EVENT, @id, {})
end

assert_equal 1, txn.segments.size
end
end
logger.verify
end
end
end
end
end