newrelic_rpm 7.1.0 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 768b946a5433cf79422bbbc2cda40878a70a6aad22a159e5eb0dcdb351aa9e6d
4
- data.tar.gz: efa504f22c2029103d7ccba6940a47b77d12ef4f1d13fbff2da34d9ba7443c1a
3
+ metadata.gz: e364456c599b5484bd0b031f9bc19fd1ec634954c6c060eee1ac85c38adfed78
4
+ data.tar.gz: aaaaa6a31504b5aac1da40ac2c0d69fefbbae8525ce96ef38e7fc01460c618d9
5
5
  SHA512:
6
- metadata.gz: d982762c9dadd2457e49a6ae480b39ad91d0484de4c8788cc0cb3ab3c677e001c57aa8047daaa0d1010b7a7b2b5b15c74790d60badecd81a3987bc4dc3a3d151
7
- data.tar.gz: '079081d14361839642d5339bf1a2be928cded80310cfd951f43f2d2885c6fb71c3a6a713db6ae9e03e7bcf9e244c4a8cf48d1e132a7ba47f928b4bff974426d6'
6
+ metadata.gz: c8a7d6825a9d05a99d45bffb9fc537f5630f9e8077cd939970f28b17e85991fa80443752bc05560b5fcea7f3a47058e68eb96621f66a0e3b2989206a0aa66ebd
7
+ data.tar.gz: 2586902aac108d89c355eccae23acf6b88a72c18abdc773ef4938f3a5aaf17568821e2890bd2ecc4e56695d3798f1f74bdb136f2df3fc6002aacaa563fd329ea
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # New Relic Ruby Agent Release Notes #
2
2
 
3
+ ## v7.2.0
4
+
5
+ * **Expected Errors and Ignore Errors**
6
+ This release adds support for configuration for expected/ignored errors by class name, status code, and message. The following configuration options are now available:
7
+ - `error_collector.ignore_classes`
8
+ - `error_collector.ignore_messages`
9
+ - `error_collector.ignore_status_codes`
10
+ - `error_collector.expected_classes`
11
+ - `error_collector.expected_messages`
12
+ - `error_collector.expected_status_codes`
13
+ For more details about expected and ignored errors, please see our [configuration documentation](https://docs.newrelic.com/docs/agents/ruby-agent/configuration/)
14
+
15
+ * **Bugfix: resolves "can't add a new key into hash during iteration" Errors**
16
+
17
+ Thanks to @wyhaines for this fix that prevents "can't add a new key into hash during iteration" errors from occuring when iterating over environment data.
18
+
19
+ * **Bugfix: kwarg support fixed for Rack middleware instrumentation**
20
+
21
+ Thanks to @walro for submitting this fix. This fixes the rack instrumentation when using kwargs.
22
+
23
+ * **Update known conflicts with use of Module#Prepend**
24
+
25
+ With our release of v7.0.0, we updated our instrumentation to use Module#Prepend by default, instead of method chaining. We have received reports of conflicts and added a check for these known conflicts. If a known conflict with prepend is detected while using the default value of 'auto' for gem instrumentation, the agent will instead install method chaining instrumentation in order to avoid this conflict. This check can be bypassed by setting the instrumentation method for the gem to 'prepend'.
26
+
27
+
3
28
  ## v7.1.0
4
29
 
5
30
  * **Add support for CSP nonces when using our API to insert the browser agent**
@@ -44,6 +44,7 @@ module NewRelic
44
44
  require 'new_relic/agent/sql_sampler'
45
45
  require 'new_relic/agent/commands/thread_profiler_session'
46
46
  require 'new_relic/agent/error_collector'
47
+ require 'new_relic/agent/error_filter'
47
48
  require 'new_relic/agent/sampler'
48
49
  require 'new_relic/agent/database'
49
50
  require 'new_relic/agent/database_adapter'
@@ -1297,7 +1297,56 @@ module NewRelic
1297
1297
  :public => true,
1298
1298
  :type => String,
1299
1299
  :allowed_from_server => true,
1300
- :description => 'Specify a comma-delimited list of error classes that the agent should ignore.'
1300
+ :dynamic_name => true,
1301
+ :description => 'DEPRECATED; use `error_collector.ignore_classes` instead. Specify a comma-delimited list of error classes that the agent should ignore.'
1302
+ },
1303
+ :'error_collector.ignore_classes' => {
1304
+ :default => [],
1305
+ :public => true,
1306
+ :type => Array,
1307
+ :allowed_from_server => true,
1308
+ :dynamic_name => true,
1309
+ :description => 'A list of error classes that the agent should ignore. *Note: this setting cannot be set via environment variable.*'
1310
+ },
1311
+ :'error_collector.ignore_messages' => {
1312
+ :default => {},
1313
+ :public => true,
1314
+ :type => Hash,
1315
+ :allowed_from_server => true,
1316
+ :dynamic_name => true,
1317
+ :description => 'A map of error classes to a list of messages. When an error of one of the classes specified here occurs, if its error message contains one of the strings corresponding to it here, that error will be ignored. *Note: this setting cannot be set via environment variable.*'
1318
+ },
1319
+ :'error_collector.ignore_status_codes' => {
1320
+ :default => '',
1321
+ :public => true,
1322
+ :type => String,
1323
+ :allowed_from_server => true,
1324
+ :dynamic_name => true,
1325
+ :description => 'A comma separated list of status codes, possibly including ranges. Errors associated with these status codes, where applicable, will be ignored.'
1326
+ },
1327
+ :'error_collector.expected_classes' => {
1328
+ :default => [],
1329
+ :public => true,
1330
+ :type => Array,
1331
+ :allowed_from_server => true,
1332
+ :dynamic_name => true,
1333
+ :description => 'A list of error classes that the agent should treat as expected. *Note: this setting cannot be set via environment variable.*'
1334
+ },
1335
+ :'error_collector.expected_messages' => {
1336
+ :default => {},
1337
+ :public => true,
1338
+ :type => Hash,
1339
+ :allowed_from_server => true,
1340
+ :dynamic_name => true,
1341
+ :description => 'A map of error classes to a list of messages. When an error of one of the classes specified here occurs, if its error message contains one of the strings corresponding to it here, that error will be treated as expected. *Note: this setting cannot be set via environment variable.*'
1342
+ },
1343
+ :'error_collector.expected_status_codes' => {
1344
+ :default => '',
1345
+ :public => true,
1346
+ :type => String,
1347
+ :allowed_from_server => true,
1348
+ :dynamic_name => true,
1349
+ :description => 'A comma separated list of status codes, possibly including ranges. Errors associated with these status codes, where applicable, will be treated as expected.'
1301
1350
  },
1302
1351
  :'error_collector.max_backtrace_frames' => {
1303
1352
  :default => 50,
@@ -80,6 +80,7 @@ module NewRelic
80
80
  was_finished = finished_configuring?
81
81
 
82
82
  invoke_callbacks(:add, source)
83
+
83
84
  case source
84
85
  when SecurityPolicySource then @security_policy_source = source
85
86
  when HighSecuritySource then @high_security_source = source
@@ -159,7 +160,6 @@ module NewRelic
159
160
  def invoke_callbacks(direction, source)
160
161
  return unless source
161
162
  source.keys.each do |key|
162
-
163
163
  if @cache[key] != source[key]
164
164
  @callbacks[key].each do |proc|
165
165
  if direction == :add
@@ -48,8 +48,10 @@ module NewRelic
48
48
  environment_report
49
49
  end
50
50
 
51
- def environment_metadata
52
- ENV.select {|k, v| k =~ /^NEW_RELIC_METADATA_/}
51
+ def environment_metadata
52
+ env_copy = {}
53
+ ENV.keys.each {|k| env_copy[k] = ENV[k] if k =~ /^NEW_RELIC_METADATA_/}
54
+ env_copy
53
55
  end
54
56
 
55
57
  def local_host
@@ -22,23 +22,18 @@ module NewRelic
22
22
  @error_trace_aggregator = ErrorTraceAggregator.new(MAX_ERROR_QUEUE_LENGTH)
23
23
  @error_event_aggregator = ErrorEventAggregator.new events
24
24
 
25
- # lookup of exception class names to ignore. Hash for fast access
26
- @ignore = {}
27
-
28
- initialize_ignored_errors(Agent.config[:'error_collector.ignore_errors'])
29
-
30
- Agent.config.register_callback(:'error_collector.ignore_errors') do |ignore_errors|
31
- initialize_ignored_errors(ignore_errors)
25
+ @error_filter = NewRelic::Agent::ErrorFilter.new
26
+
27
+ %w(
28
+ ignore_errors ignore_classes ignore_messages ignore_status_codes
29
+ expected_classes expected_messages expected_status_codes
30
+ ).each do |w|
31
+ Agent.config.register_callback(:"error_collector.#{w}") do |value|
32
+ @error_filter.load_from_config(w, value)
33
+ end
32
34
  end
33
35
  end
34
36
 
35
- def initialize_ignored_errors(ignore_errors)
36
- @ignore.clear
37
- ignore_errors = ignore_errors.split(",") if ignore_errors.is_a? String
38
- ignore_errors.each { |error| error.strip! }
39
- ignore(ignore_errors)
40
- end
41
-
42
37
  def enabled?
43
38
  error_trace_aggregator.enabled? || error_event_aggregator.enabled?
44
39
  end
@@ -76,30 +71,39 @@ module NewRelic
76
71
  defined?(@ignore_filter) ? @ignore_filter : nil
77
72
  end
78
73
 
79
- # errors is an array of Exception Class Names
80
- #
81
74
  def ignore(errors)
82
- errors.each do |error|
83
- @ignore[error] = true
84
- ::NewRelic::Agent.logger.debug("Ignoring errors of type '#{error}'")
85
- end
75
+ @error_filter.ignore(errors)
76
+ end
77
+
78
+ def ignore?(ex, status_code = nil)
79
+ @error_filter.ignore?(ex, status_code)
80
+ end
81
+
82
+ def expect(errors)
83
+ @error_filter.expect(errors)
84
+ end
85
+
86
+ def expected?(ex, status_code = nil)
87
+ @error_filter.expected?(ex, status_code)
88
+ end
89
+
90
+ def load_error_filters
91
+ @error_filter.load_all
92
+ end
93
+
94
+ def reset_error_filters
95
+ @error_filter.reset
86
96
  end
87
97
 
88
98
  # Checks the provided error against the error filter, if there
89
99
  # is an error filter
90
- def filtered_by_error_filter?(error)
100
+ def ignored_by_filter_proc?(error)
91
101
  respond_to?(:ignore_filter_proc) && !ignore_filter_proc(error)
92
102
  end
93
103
 
94
- # Checks the array of error names and the error filter against
95
- # the provided error
96
- def filtered_error?(error)
97
- @ignore[error.class.name] || filtered_by_error_filter?(error)
98
- end
99
-
100
104
  # an error is ignored if it is nil or if it is filtered
101
- def error_is_ignored?(error)
102
- error && filtered_error?(error)
105
+ def error_is_ignored?(error, status_code = nil)
106
+ error && (@error_filter.ignore?(error, status_code) || ignored_by_filter_proc?(error))
103
107
  rescue => e
104
108
  NewRelic::Agent.logger.error("Error '#{error}' will NOT be ignored. Exception '#{e}' while determining whether to ignore or not.", e)
105
109
  false
@@ -174,11 +178,18 @@ module NewRelic
174
178
  end
175
179
  end
176
180
 
177
- def skip_notice_error?(exception)
181
+ def increment_expected_error_count!(state, exception)
182
+ stats_engine = NewRelic::Agent.agent.stats_engine
183
+ stats_engine.record_unscoped_metrics(state, ['ErrorsExpected/all']) do |stats|
184
+ stats.increment_count
185
+ end
186
+ end
187
+
188
+ def skip_notice_error?(exception, status_code = nil)
178
189
  disabled? ||
179
- error_is_ignored?(exception) ||
180
190
  exception.nil? ||
181
- exception_tagged_with?(EXCEPTION_TAG_IVAR, exception)
191
+ exception_tagged_with?(EXCEPTION_TAG_IVAR, exception) ||
192
+ error_is_ignored?(exception, status_code)
182
193
  end
183
194
 
184
195
  # calls a method on an object, if it responds to it - used for
@@ -210,13 +221,17 @@ module NewRelic
210
221
 
211
222
  # See NewRelic::Agent.notice_error for options and commentary
212
223
  def notice_error(exception, options={}, span_id=nil)
213
- return if skip_notice_error?(exception)
224
+ state = ::NewRelic::Agent::Tracer.state
225
+ transaction = state.current_transaction
226
+ status_code = transaction ? transaction.http_response_code : nil
214
227
 
215
- tag_exception(exception)
228
+ return if skip_notice_error?(exception, status_code)
216
229
 
217
- state = ::NewRelic::Agent::Tracer.state
230
+ tag_exception(exception)
218
231
 
219
- unless options[:expected]
232
+ if options[:expected] || @error_filter.expected?(exception, status_code)
233
+ increment_expected_error_count!(state, exception)
234
+ else
220
235
  increment_error_count!(state, exception, options)
221
236
  end
222
237
 
@@ -258,7 +273,7 @@ module NewRelic
258
273
  noticed_error.line_number = sense_method(exception, :line_number)
259
274
  noticed_error.stack_trace = truncate_trace(extract_stack_trace(exception))
260
275
 
261
- noticed_error.expected = !! options.delete(:expected)
276
+ noticed_error.expected = !!options.delete(:expected) || expected?(exception)
262
277
 
263
278
  noticed_error.attributes_from_notice_error = options.delete(:custom_params) || {}
264
279
 
@@ -0,0 +1,167 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+
8
+ # Handles loading of ignored and expected errors from the agent configuration, and
9
+ # determining at runtime whether an exception is ignored or expected.
10
+ class ErrorFilter
11
+
12
+ def initialize
13
+ reset
14
+ end
15
+
16
+ def reset
17
+ @ignore_classes, @expected_classes = [], []
18
+ @ignore_messages, @expected_messages = {}, {}
19
+ @ignore_status_codes, @expected_status_codes = [], []
20
+ end
21
+
22
+ def load_all
23
+ %i(
24
+ ignore_errors ignore_classes ignore_messages ignore_status_codes
25
+ expected_classes expected_messages expected_status_codes
26
+ ).each { |setting| load_from_config(setting) }
27
+ end
28
+
29
+ def load_from_config(setting, value = nil)
30
+ errors = nil
31
+ new_value = value || fetch_agent_config(setting.to_sym)
32
+ return if new_value.nil? || new_value.empty?
33
+
34
+ case setting.to_sym
35
+ when :ignore_errors, :ignore_classes
36
+ new_value = new_value.split(',').map!(&:strip) if new_value.is_a?(String)
37
+ errors = @ignore_classes = new_value
38
+ when :ignore_messages
39
+ errors = @ignore_messages = new_value || {}
40
+ when :ignore_status_codes
41
+ errors = @ignore_status_codes = parse_status_codes(new_value) || []
42
+ when :expected_classes
43
+ errors = @expected_classes = new_value || []
44
+ when :expected_messages
45
+ errors = @expected_messages = new_value || {}
46
+ when :expected_status_codes
47
+ errors = @expected_status_codes = parse_status_codes(new_value) || []
48
+ end
49
+ log_filter(setting, errors) if errors
50
+ end
51
+
52
+ def ignore?(ex, status_code = nil)
53
+ @ignore_classes.include?(ex.class.name) ||
54
+ (@ignore_messages.keys.include?(ex.class.name) &&
55
+ @ignore_messages[ex.class.name].any? { |m| ex.message.include?(m) }) ||
56
+ @ignore_status_codes.include?(status_code.to_i)
57
+ end
58
+
59
+ def expected?(ex, status_code = nil)
60
+ @expected_classes.include?(ex.class.name) ||
61
+ (@expected_messages.keys.include?(ex.class.name) &&
62
+ @expected_messages[ex.class.name].any? { |m| ex.message.include?(m) }) ||
63
+ @expected_status_codes.include?(status_code.to_i)
64
+ end
65
+
66
+ def fetch_agent_config(cfg)
67
+ NewRelic::Agent.config[:"error_collector.#{cfg}"]
68
+ end
69
+
70
+ # A generic method for adding ignore filters manually. This is kept for compatibility
71
+ # with the previous ErrorCollector#ignore method, and adds some flexibility for adding
72
+ # different ignore/expected error types by examining each argument.
73
+ def ignore(*args)
74
+ args.each do |errors|
75
+ case errors
76
+ when Array
77
+ errors.each { |e| ignore(e) }
78
+ when Integer
79
+ @ignore_status_codes << errors
80
+ when Hash
81
+ @ignore_messages.update(errors)
82
+ log_filter(:ignore_messages, errors)
83
+ when String
84
+ if errors.match(/^[\d\,\-]+$/)
85
+ @ignore_status_codes |= parse_status_codes(errors)
86
+ log_filter(:ignore_status_codes, errors)
87
+ else
88
+ new_ignore_classes = errors.split(',').map!(&:strip)
89
+ @ignore_classes |= new_ignore_classes
90
+ log_filter(:ignore_classes, new_ignore_classes)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ # See #ignore above.
97
+ def expect(*args)
98
+ args.each do |errors|
99
+ case errors
100
+ when Array
101
+ errors.each { |e| expect(e) }
102
+ when Integer
103
+ @expected_status_codes << errors
104
+ when Hash
105
+ @expected_messages.update(errors)
106
+ log_filter(:expected_messages, errors)
107
+ when String
108
+ if errors.match(/^[\d\,\-]+$/)
109
+ @expected_status_codes |= parse_status_codes(errors)
110
+ log_filter(:expected_status_codes, errors)
111
+ else
112
+ new_expected_classes = errors.split(',').map!(&:strip)
113
+ @expected_classes |= new_expected_classes
114
+ log_filter(:expected_classes, new_expected_classes)
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def log_filter(setting, errors)
123
+ case setting
124
+ when :ignore_errors, :ignore_classes
125
+ errors.each do |error|
126
+ ::NewRelic::Agent.logger.debug("Ignoring errors of type '#{error}'")
127
+ end
128
+ when :ignore_messages
129
+ errors.each do |error,messages|
130
+ ::NewRelic::Agent.logger.debug("Ignoring errors of type '#{error}' with messages: #{messages.join(',')}")
131
+ end
132
+ when :ignore_status_codes
133
+ ::NewRelic::Agent.logger.debug("Ignoring errors associated with status codes: #{errors}")
134
+ when :expected_classes
135
+ errors.each do |error|
136
+ ::NewRelic::Agent.logger.debug("Expecting errors of type '#{error}'")
137
+ end
138
+ when :expected_messages
139
+ errors.each do |error,messages|
140
+ ::NewRelic::Agent.logger.debug("Expecting errors of type '#{error}' with messages: #{messages.join(',')}")
141
+ end
142
+ when :expected_status_codes
143
+ ::NewRelic::Agent.logger.debug("Expecting errors associated with status codes: #{errors}")
144
+ end
145
+ end
146
+
147
+ def parse_status_codes(codes)
148
+ code_list = codes.is_a?(String) ? codes.split(',') : codes
149
+ result = []
150
+ code_list.each do |code|
151
+ result << code && next if code.is_a?(Integer)
152
+ m = code.match(/(\d{3})(-\d{3})?/)
153
+ if m.nil? || m[1].nil?
154
+ ::NewRelic::Agent.logger.warn("Invalid HTTP status code: '#{code}'; ignoring config")
155
+ next
156
+ end
157
+ if m[2]
158
+ result += (m[1]..m[2].tr('-', '')).to_a.map(&:to_i)
159
+ else
160
+ result << m[1].to_i
161
+ end
162
+ end
163
+ result.uniq
164
+ end
165
+ end
166
+ end
167
+ end
@@ -31,7 +31,8 @@ module NewRelic::Agent::Instrumentation
31
31
  def use_with_newrelic(middleware_class, *args, &block)
32
32
  use_with_tracing(middleware_class) { |wrapped_class| use_without_newrelic(wrapped_class, *args, &block) }
33
33
  end
34
-
34
+ ruby2_keywords(:use_with_newrelic) if respond_to?(:ruby2_keywords, true)
35
+
35
36
  alias_method :use_without_newrelic, :use
36
37
  alias_method :use, :use_with_newrelic
37
38
  end
@@ -54,4 +55,4 @@ module NewRelic::Agent::Instrumentation
54
55
  end
55
56
  end
56
57
  end
57
- end
58
+ end
@@ -31,6 +31,7 @@ module NewRelic::Agent::Instrumentation
31
31
  def use(middleware_class, *args, &blk)
32
32
  use_with_tracing(middleware_class) { |wrapped_class| super(wrapped_class, *args, &blk) }
33
33
  end
34
+ ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
34
35
  end
35
36
  end
36
- end
37
+ end
@@ -13,9 +13,9 @@ DependencyDetection.defer do
13
13
  defined?(::Resque::Job) && !NewRelic::Agent.config[:disable_resque]
14
14
  end
15
15
 
16
- # Airbrake uses method chaining on Resque::Job
16
+ # Airbrake uses method chaining on Resque::Job on versions < 11.0.3
17
17
  conflicts_with_prepend do
18
- defined?(::Airbrake)
18
+ defined?(::Airbrake) && defined?(::Airbrake::AIRBRAKE_VERSION) && ::Gem::Version.create(::Airbrake::AIRBRAKE_VERSION) < ::Gem::Version.create('11.0.3')
19
19
  end
20
20
 
21
21
  executes do
@@ -33,7 +33,7 @@ class NewRelic::NoticedError
33
33
  ERROR_CLASS_KEY = "#{ERROR_PREFIX_KEY}.class"
34
34
  ERROR_EXPECTED_KEY = "#{ERROR_PREFIX_KEY}.expected"
35
35
 
36
- def initialize(path, exception, timestamp = Time.now)
36
+ def initialize(path, exception, timestamp = Time.now, expected = false)
37
37
  @exception_id = exception.object_id
38
38
  @path = path
39
39
 
@@ -58,7 +58,7 @@ class NewRelic::NoticedError
58
58
  @attributes_from_notice_error = nil
59
59
  @attributes = nil
60
60
  @timestamp = timestamp
61
- @expected = false
61
+ @expected = expected
62
62
  end
63
63
 
64
64
  def ==(other)
@@ -104,7 +104,7 @@ class NewRelic::NoticedError
104
104
  params[:file_name] = file_name if file_name
105
105
  params[:line_number] = line_number if line_number
106
106
  params[:stack_trace] = stack_trace if stack_trace
107
- params[:'error.expected'] = expected
107
+ params[ERROR_EXPECTED_KEY.to_sym] = expected
108
108
  params
109
109
  end
110
110
 
@@ -11,7 +11,7 @@ module NewRelic
11
11
  end
12
12
 
13
13
  MAJOR = 7
14
- MINOR = 1
14
+ MINOR = 2
15
15
  TINY = 0
16
16
 
17
17
  begin
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: newrelic_rpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.0
4
+ version: 7.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Huntsman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-06-03 00:00:00.000000000 Z
13
+ date: 2021-07-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -276,6 +276,7 @@ files:
276
276
  - lib/new_relic/agent/encoding_normalizer.rb
277
277
  - lib/new_relic/agent/error_collector.rb
278
278
  - lib/new_relic/agent/error_event_aggregator.rb
279
+ - lib/new_relic/agent/error_filter.rb
279
280
  - lib/new_relic/agent/error_trace_aggregator.rb
280
281
  - lib/new_relic/agent/event_aggregator.rb
281
282
  - lib/new_relic/agent/event_buffer.rb