newrelic_rpm 6.2.0.354 → 6.3.0.355
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +17 -0
- data/CHANGELOG.md +35 -6
- data/lib/new_relic/agent/agent.rb +38 -130
- data/lib/new_relic/agent/configuration/default_source.rb +49 -10
- data/lib/new_relic/agent/configuration/event_data.rb +39 -0
- data/lib/new_relic/agent/configuration/server_source.rb +11 -1
- data/lib/new_relic/agent/connect/request_builder.rb +63 -0
- data/lib/new_relic/agent/connect/response_handler.rb +61 -0
- data/lib/new_relic/agent/hostname.rb +1 -1
- data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +3 -7
- data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +161 -0
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +7 -0
- data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +34 -3
- data/lib/new_relic/agent/instrumentation/{rails5 → rails_notifications}/action_cable.rb +3 -3
- data/lib/new_relic/agent/instrumentation/{rails5 → rails_notifications}/action_controller.rb +3 -3
- data/lib/new_relic/agent/instrumentation/{rails5 → rails_notifications}/action_view.rb +3 -3
- data/lib/new_relic/agent/parameter_filtering.rb +18 -5
- data/lib/new_relic/control/class_methods.rb +7 -1
- data/lib/new_relic/control/frameworks/{rails5.rb → rails_notifications.rb} +1 -1
- data/lib/new_relic/version.rb +1 -1
- data/test/agent_helper.rb +11 -5
- metadata +10 -11
- data/lib/new_relic/agent/instrumentation/active_record_4.rb +0 -42
- data/lib/new_relic/agent/instrumentation/active_record_5.rb +0 -41
- data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +0 -32
- data/lib/new_relic/agent/instrumentation/rails4/action_view.rb +0 -27
- data/lib/new_relic/control/frameworks/rails6.rb +0 -14
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
module Configuration
|
8
|
+
module EventData
|
9
|
+
|
10
|
+
extend self
|
11
|
+
|
12
|
+
EVENT_DATA_CONFIG_KEY_MAPPING = {
|
13
|
+
:analytic_event_data => :'analytics_events.max_samples_stored',
|
14
|
+
:custom_event_data => :'custom_insights_events.max_samples_stored',
|
15
|
+
:error_event_data => :'error_collector.max_event_samples_stored'
|
16
|
+
}
|
17
|
+
|
18
|
+
def from_config(config)
|
19
|
+
{:harvest_limits => EVENT_DATA_CONFIG_KEY_MAPPING.inject({}) do
|
20
|
+
|event_data, (event_data_key, config_key)|
|
21
|
+
event_data[event_data_key] = config[config_key]
|
22
|
+
event_data
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_config_hash(connect_reply)
|
28
|
+
config_hash = EVENT_DATA_CONFIG_KEY_MAPPING.inject({}) do
|
29
|
+
|event_data, (event_data_key, config_key)|
|
30
|
+
event_data[config_key] = connect_reply['event_data']['harvest_limits'][event_data_key.to_s]
|
31
|
+
event_data
|
32
|
+
end
|
33
|
+
config_hash[:event_report_period] = connect_reply['event_data']['report_period_ms'] / 1000
|
34
|
+
config_hash
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -20,7 +20,6 @@ module NewRelic
|
|
20
20
|
"browser_monitoring.loader_version",
|
21
21
|
"cross_process_id",
|
22
22
|
"data_report_period",
|
23
|
-
"data_report_periods.analytic_event_data",
|
24
23
|
"encoding_key",
|
25
24
|
"error_beacon",
|
26
25
|
"js_agent_file",
|
@@ -39,6 +38,7 @@ module NewRelic
|
|
39
38
|
merge_top_level_keys(merged_settings, connect_reply)
|
40
39
|
merge_agent_config_hash(merged_settings, connect_reply)
|
41
40
|
fix_transaction_threshold(merged_settings)
|
41
|
+
add_event_data(merged_settings, connect_reply)
|
42
42
|
filter_keys(merged_settings)
|
43
43
|
|
44
44
|
apply_feature_gates(merged_settings, connect_reply, existing_config)
|
@@ -74,6 +74,16 @@ module NewRelic
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
+
def add_event_data(merged_settings, connect_reply)
|
78
|
+
if connect_reply['event_data']
|
79
|
+
merged_settings.merge! EventData.to_config_hash(connect_reply)
|
80
|
+
else
|
81
|
+
NewRelic::Agent.logger.warn "No event data configuration found " \
|
82
|
+
"in connect response; using default event report period."
|
83
|
+
NewRelic::Agent.record_metric('Supportability/Agent/Collector/MissingEventData', 1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
77
87
|
def filter_keys(merged_settings)
|
78
88
|
merged_settings.delete_if do |key, _|
|
79
89
|
setting_spec = DEFAULTS[key.to_sym]
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
require 'new_relic/environment_report'
|
6
|
+
require 'new_relic/agent/configuration/event_data'
|
7
|
+
|
8
|
+
module NewRelic
|
9
|
+
module Agent
|
10
|
+
module Connect
|
11
|
+
|
12
|
+
class RequestBuilder
|
13
|
+
|
14
|
+
def initialize(new_relic_service, config)
|
15
|
+
@service = new_relic_service
|
16
|
+
@config = config
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Initializes the hash of settings that we send to the
|
21
|
+
# server. Returns a literal hash containing the options
|
22
|
+
def connect_payload
|
23
|
+
{
|
24
|
+
:pid => $$,
|
25
|
+
:host => local_host,
|
26
|
+
:display_host => Agent.config[:'process_host.display_name'],
|
27
|
+
:app_name => Agent.config.app_names,
|
28
|
+
:language => 'ruby',
|
29
|
+
:labels => Agent.config.parsed_labels,
|
30
|
+
:agent_version => NewRelic::VERSION::STRING,
|
31
|
+
:environment => sanitize_environment_report(environment_report),
|
32
|
+
:settings => Agent.config.to_collector_hash,
|
33
|
+
:high_security => Agent.config[:high_security],
|
34
|
+
:utilization => UtilizationData.new.to_collector_hash,
|
35
|
+
:identifier => "ruby:#{local_host}:#{Agent.config.app_names.sort.join(',')}",
|
36
|
+
:event_data => Configuration::EventData.from_config(Agent.config)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# We've seen objects in the environment report (Rails.env in
|
41
|
+
# particular) that can't seralize to JSON. Cope with that here and
|
42
|
+
# clear out so downstream code doesn't have to check again.
|
43
|
+
def sanitize_environment_report(environment_report)
|
44
|
+
return [] unless @service.valid_to_marshal?(environment_report)
|
45
|
+
environment_report
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks whether we should send environment info, and if so,
|
49
|
+
# returns the snapshot from the local environment.
|
50
|
+
# Generating the EnvironmentReport has the potential to trigger
|
51
|
+
# require calls in Rails environments, so this method should only
|
52
|
+
# be called synchronously from on the main thread.
|
53
|
+
def environment_report
|
54
|
+
Agent.config[:send_environment_info] ? Array(EnvironmentReport.new) : []
|
55
|
+
end
|
56
|
+
|
57
|
+
def local_host
|
58
|
+
NewRelic::Agent::Hostname.get
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
|
6
|
+
module NewRelic
|
7
|
+
module Agent
|
8
|
+
module Connect
|
9
|
+
|
10
|
+
class ResponseHandler
|
11
|
+
|
12
|
+
def initialize(agent, config)
|
13
|
+
@agent = agent
|
14
|
+
@config = config
|
15
|
+
end
|
16
|
+
|
17
|
+
# Takes a hash of configuration data returned from the
|
18
|
+
# server and uses it to set local variables and to
|
19
|
+
# initialize various parts of the agent that are configured
|
20
|
+
# separately.
|
21
|
+
#
|
22
|
+
# Can accommodate most arbitrary data - anything extra is
|
23
|
+
# ignored unless we say to do something with it here.
|
24
|
+
def configure_agent(config_data)
|
25
|
+
return if config_data == nil
|
26
|
+
|
27
|
+
@agent.agent_id = config_data['agent_run_id']
|
28
|
+
|
29
|
+
security_policies = config_data.delete('security_policies')
|
30
|
+
|
31
|
+
add_server_side_config(config_data)
|
32
|
+
add_security_policy_config(security_policies) if security_policies
|
33
|
+
|
34
|
+
@agent.transaction_rules = RulesEngine.create_transaction_rules(config_data)
|
35
|
+
@agent.stats_engine.metric_rules = RulesEngine.create_metric_rules(config_data)
|
36
|
+
|
37
|
+
# If you're adding something else here to respond to the server-side config,
|
38
|
+
# use Agent.instance.events.subscribe(:finished_configuring) callback instead!
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_server_side_config(config_data)
|
42
|
+
if config_data['agent_config']
|
43
|
+
::NewRelic::Agent.logger.debug "Using config from server"
|
44
|
+
end
|
45
|
+
|
46
|
+
::NewRelic::Agent.logger.debug "Server provided config: #{config_data.inspect}"
|
47
|
+
server_config = NewRelic::Agent::Configuration::ServerSource.new(config_data, @config)
|
48
|
+
@config.replace_or_add_config(server_config)
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_security_policy_config(security_policies)
|
52
|
+
::NewRelic::Agent.logger.info 'Installing security policies'
|
53
|
+
security_policy_source = NewRelic::Agent::Configuration::SecurityPolicySource.new(security_policies)
|
54
|
+
@config.replace_or_add_config(security_policy_source)
|
55
|
+
# drop data collected before applying security policies
|
56
|
+
@agent.drop_buffered_data
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -48,7 +48,9 @@ module NewRelic
|
|
48
48
|
category: :controller,
|
49
49
|
options: {
|
50
50
|
request: event.request,
|
51
|
-
filtered_params:
|
51
|
+
filtered_params: NewRelic::Agent::ParameterFiltering.filter_using_rails(
|
52
|
+
event.payload[:params],
|
53
|
+
Rails.application.config.filter_parameters),
|
52
54
|
apdex_start_time: event.queue_start,
|
53
55
|
ignore_apdex: event.apdex_ignored?,
|
54
56
|
ignore_enduser: event.enduser_ignored?
|
@@ -59,12 +61,6 @@ module NewRelic
|
|
59
61
|
def stop_transaction(event)
|
60
62
|
(finishable = event.payload[:finishable]) && finishable.finish
|
61
63
|
end
|
62
|
-
|
63
|
-
def filter(params)
|
64
|
-
munged_params = NewRelic::Agent::ParameterFiltering.filter_rails_request_parameters(params)
|
65
|
-
filters = Rails.application.config.filter_parameters
|
66
|
-
ActionDispatch::Http::ParameterFilter.new(filters).filter(munged_params)
|
67
|
-
end
|
68
64
|
end
|
69
65
|
|
70
66
|
class ControllerEvent < Event
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
require 'new_relic/agent/instrumentation/active_record_subscriber'
|
6
|
+
require 'new_relic/agent/instrumentation/active_record_prepend'
|
7
|
+
|
8
|
+
|
9
|
+
# Provides a way to send :connection through ActiveSupport notifications to avoid
|
10
|
+
# looping through connection handlers to locate a connection by connection_id
|
11
|
+
# This is not needed in Rails 6+: https://github.com/rails/rails/pull/34602
|
12
|
+
module NewRelic
|
13
|
+
module Agent
|
14
|
+
module Instrumentation
|
15
|
+
module ActiveRecordNotifications
|
16
|
+
module BaseExtensions41
|
17
|
+
# https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L371
|
18
|
+
def log(sql, name = "SQL", binds = [], statement_name = nil)
|
19
|
+
@instrumenter.instrument(
|
20
|
+
"sql.active_record",
|
21
|
+
:sql => sql,
|
22
|
+
:name => name,
|
23
|
+
:connection_id => object_id,
|
24
|
+
:connection => self,
|
25
|
+
:statement_name => statement_name,
|
26
|
+
:binds => binds) { yield }
|
27
|
+
rescue => e
|
28
|
+
raise translate_exception(e, sql)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module BaseExtensions50
|
33
|
+
# https://github.com/rails/rails/blob/5-0-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L582
|
34
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
|
35
|
+
@instrumenter.instrument(
|
36
|
+
"sql.active_record",
|
37
|
+
sql: sql,
|
38
|
+
name: name,
|
39
|
+
binds: binds,
|
40
|
+
type_casted_binds: type_casted_binds,
|
41
|
+
statement_name: statement_name,
|
42
|
+
connection_id: object_id,
|
43
|
+
connection: self) { yield }
|
44
|
+
rescue => e
|
45
|
+
raise translate_exception_class(e, sql)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module BaseExtensions51
|
50
|
+
# https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L603
|
51
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
|
52
|
+
@instrumenter.instrument(
|
53
|
+
"sql.active_record",
|
54
|
+
sql: sql,
|
55
|
+
name: name,
|
56
|
+
binds: binds,
|
57
|
+
type_casted_binds: type_casted_binds,
|
58
|
+
statement_name: statement_name,
|
59
|
+
connection_id: object_id,
|
60
|
+
connection: self) do
|
61
|
+
@lock.synchronize do
|
62
|
+
yield
|
63
|
+
end
|
64
|
+
end
|
65
|
+
rescue => e
|
66
|
+
raise translate_exception_class(e, sql)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
DependencyDetection.defer do
|
75
|
+
named :active_record_notifications
|
76
|
+
|
77
|
+
depends_on do
|
78
|
+
defined?(::ActiveRecord) && defined?(::ActiveRecord::Base) &&
|
79
|
+
defined?(::ActiveRecord::VERSION) &&
|
80
|
+
::ActiveRecord::VERSION::MAJOR.to_i >= 4
|
81
|
+
end
|
82
|
+
|
83
|
+
depends_on do
|
84
|
+
!NewRelic::Agent.config[:disable_activerecord_instrumentation] &&
|
85
|
+
!NewRelic::Agent::Instrumentation::ActiveRecordSubscriber.subscribed?
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
depends_on do
|
90
|
+
# If the deprecated :disable_active_record_4 setting is true, and
|
91
|
+
# the active record version is four, disable!
|
92
|
+
NewRelic::Agent.config[:disable_active_record_4] == false \
|
93
|
+
|| ::ActiveRecord::VERSION::MAJOR.to_i != 4
|
94
|
+
end
|
95
|
+
|
96
|
+
depends_on do
|
97
|
+
# If the deprecated :disable_active_record_5 setting is true, and
|
98
|
+
# the active record version is five, disable!
|
99
|
+
NewRelic::Agent.config[:disable_active_record_5] == false \
|
100
|
+
|| ::ActiveRecord::VERSION::MAJOR.to_i != 5
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
executes do
|
105
|
+
::NewRelic::Agent.logger.info 'Installing notifications based Active Record instrumentation'
|
106
|
+
end
|
107
|
+
|
108
|
+
executes do
|
109
|
+
ActiveSupport::Notifications.subscribe('sql.active_record',
|
110
|
+
NewRelic::Agent::Instrumentation::ActiveRecordSubscriber.new)
|
111
|
+
end
|
112
|
+
|
113
|
+
executes do
|
114
|
+
ActiveSupport.on_load(:active_record) do
|
115
|
+
::NewRelic::Agent::PrependSupportability.record_metrics_for(
|
116
|
+
::ActiveRecord::Base,
|
117
|
+
::ActiveRecord::Relation)
|
118
|
+
|
119
|
+
# Default to .prepending, unless the ActiveRecord version is <=4
|
120
|
+
# **AND** the :prepend_active_record_instrumentation config is false
|
121
|
+
if ::ActiveRecord::VERSION::MAJOR > 4 \
|
122
|
+
|| ::NewRelic::Agent.config[:prepend_active_record_instrumentation]
|
123
|
+
|
124
|
+
::ActiveRecord::Base.send(:prepend,
|
125
|
+
::NewRelic::Agent::Instrumentation::ActiveRecordPrepend::BaseExtensions)
|
126
|
+
::ActiveRecord::Relation.send(:prepend,
|
127
|
+
::NewRelic::Agent::Instrumentation::ActiveRecordPrepend::RelationExtensions)
|
128
|
+
else
|
129
|
+
::NewRelic::Agent::Instrumentation::ActiveRecordHelper.instrument_additional_methods
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
executes do
|
135
|
+
if NewRelic::Agent.config[:backport_fast_active_record_connection_lookup]
|
136
|
+
|
137
|
+
activerecord_extension = if ::ActiveRecord::VERSION::MAJOR.to_i == 4
|
138
|
+
::NewRelic::Agent::Instrumentation::ActiveRecordNotifications::BaseExtensions41
|
139
|
+
elsif ::ActiveRecord::VERSION::MAJOR.to_i == 5
|
140
|
+
if ::ActiveRecord::VERSION::MINOR.to_i == 0
|
141
|
+
::NewRelic::Agent::Instrumentation::ActiveRecordNotifications::BaseExtensions50
|
142
|
+
elsif ::ActiveRecord::VERSION::MINOR.to_i >= 1
|
143
|
+
::NewRelic::Agent::Instrumentation::ActiveRecordNotifications::BaseExtensions51
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
unless activerecord_extension.nil?
|
148
|
+
::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:prepend, activerecord_extension)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
executes do
|
154
|
+
if ::ActiveRecord::VERSION::MAJOR == 5 \
|
155
|
+
&& ::ActiveRecord::VERSION::MINOR.to_i == 1 \
|
156
|
+
&& ::ActiveRecord::VERSION::TINY.to_i >= 6
|
157
|
+
|
158
|
+
::ActiveRecord::Base.prepend ::NewRelic::Agent::Instrumentation::ActiveRecordPrepend::BaseExtensions516
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -78,6 +78,13 @@ module NewRelic
|
|
78
78
|
def active_record_config(payload)
|
79
79
|
return unless payload[:connection_id]
|
80
80
|
|
81
|
+
# handle if the notification payload provides the AR connection
|
82
|
+
# available in Rails 6+ & our ActiveRecordNotifications#log extension
|
83
|
+
if payload[:connection]
|
84
|
+
connection_config = payload[:connection].instance_variable_get(:@config)
|
85
|
+
return connection_config if connection_config
|
86
|
+
end
|
87
|
+
|
81
88
|
connection = nil
|
82
89
|
connection_id = payload[:connection_id]
|
83
90
|
|
@@ -11,10 +11,33 @@ module NewRelic
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.subscribed?
|
14
|
+
find_all_subscribers.find{|s| s.instance_variable_get(:@delegate).class == self }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_all_subscribers
|
14
18
|
# TODO: need to talk to Rails core about an API for this,
|
15
19
|
# rather than digging through Listener ivars
|
16
|
-
|
17
|
-
|
20
|
+
instance_variable_names = [:@subscribers, :@string_subscribers, :@other_subscribers]
|
21
|
+
all_subscribers = []
|
22
|
+
|
23
|
+
notifier = ActiveSupport::Notifications.notifier
|
24
|
+
|
25
|
+
instance_variable_names.each do |name|
|
26
|
+
if notifier.instance_variable_defined?(name)
|
27
|
+
subscribers = notifier.instance_variable_get(name)
|
28
|
+
if subscribers.is_a? Array
|
29
|
+
# Rails 5 @subscribers, and Rails 6 @other_subscribers is a
|
30
|
+
# plain array of subscriber objects
|
31
|
+
all_subscribers += subscribers
|
32
|
+
elsif subscribers.is_a? Hash
|
33
|
+
# Rails 6 @string_subscribers is a Hash mapping the pattern
|
34
|
+
# string of a subscriber to an array of subscriber objects
|
35
|
+
subscribers.values.each { |array| all_subscribers += array }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
all_subscribers
|
18
41
|
end
|
19
42
|
|
20
43
|
def self.subscribe(pattern)
|
@@ -51,7 +74,15 @@ module NewRelic
|
|
51
74
|
|
52
75
|
def pop_event(transaction_id)
|
53
76
|
event = event_stack[transaction_id].pop
|
54
|
-
|
77
|
+
|
78
|
+
if event.respond_to?(:finish!)
|
79
|
+
# ActiveSupport version 6 and greater use a finish! method rather
|
80
|
+
# that allowing us to set the end directly
|
81
|
+
event.finish!
|
82
|
+
else
|
83
|
+
event.end = Time.now
|
84
|
+
end
|
85
|
+
|
55
86
|
return event
|
56
87
|
end
|
57
88
|
|