newrelic_rpm 6.13.1 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +247 -0
  4. data/CONTRIBUTING.md +13 -2
  5. data/README.md +2 -2
  6. data/lib/new_relic/agent.rb +6 -8
  7. data/lib/new_relic/agent/agent.rb +4 -5
  8. data/lib/new_relic/agent/audit_logger.rb +10 -0
  9. data/lib/new_relic/agent/autostart.rb +1 -2
  10. data/lib/new_relic/agent/configuration/default_source.rb +462 -221
  11. data/lib/new_relic/agent/configuration/manager.rb +3 -3
  12. data/lib/new_relic/agent/connect/request_builder.rb +4 -2
  13. data/lib/new_relic/agent/database_adapter.rb +33 -0
  14. data/lib/new_relic/agent/datastores/redis.rb +0 -4
  15. data/lib/new_relic/agent/distributed_tracing.rb +0 -66
  16. data/lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb +2 -2
  17. data/lib/new_relic/agent/error_collector.rb +52 -37
  18. data/lib/new_relic/agent/error_filter.rb +167 -0
  19. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +4 -5
  20. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +21 -68
  21. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +0 -16
  22. data/lib/new_relic/agent/instrumentation/active_record_prepend.rb +23 -57
  23. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +1 -3
  24. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +0 -15
  25. data/lib/new_relic/agent/instrumentation/bunny.rb +10 -196
  26. data/lib/new_relic/agent/instrumentation/bunny/chain.rb +45 -0
  27. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +152 -0
  28. data/lib/new_relic/agent/instrumentation/bunny/prepend.rb +35 -0
  29. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +1 -0
  30. data/lib/new_relic/agent/instrumentation/curb.rb +9 -259
  31. data/lib/new_relic/agent/instrumentation/curb/chain.rb +93 -0
  32. data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +222 -0
  33. data/lib/new_relic/agent/instrumentation/curb/prepend.rb +63 -0
  34. data/lib/new_relic/agent/instrumentation/delayed_job/chain.rb +38 -0
  35. data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +53 -0
  36. data/lib/new_relic/agent/instrumentation/delayed_job/prepend.rb +34 -0
  37. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +8 -84
  38. data/lib/new_relic/agent/instrumentation/excon.rb +2 -1
  39. data/lib/new_relic/agent/instrumentation/grape.rb +13 -113
  40. data/lib/new_relic/agent/instrumentation/grape/chain.rb +25 -0
  41. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +100 -0
  42. data/lib/new_relic/agent/instrumentation/grape/prepend.rb +17 -0
  43. data/lib/new_relic/agent/instrumentation/httpclient.rb +8 -30
  44. data/lib/new_relic/agent/instrumentation/httpclient/chain.rb +25 -0
  45. data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +38 -0
  46. data/lib/new_relic/agent/instrumentation/httpclient/prepend.rb +17 -0
  47. data/lib/new_relic/agent/instrumentation/httprb.rb +29 -0
  48. data/lib/new_relic/agent/instrumentation/httprb/chain.rb +22 -0
  49. data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +30 -0
  50. data/lib/new_relic/agent/instrumentation/httprb/prepend.rb +15 -0
  51. data/lib/new_relic/agent/instrumentation/memcache.rb +54 -69
  52. data/lib/new_relic/agent/instrumentation/memcache/chain.rb +16 -0
  53. data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +38 -121
  54. data/lib/new_relic/agent/instrumentation/memcache/helper.rb +56 -0
  55. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +88 -0
  56. data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +88 -0
  57. data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +4 -10
  58. data/lib/new_relic/agent/instrumentation/mongo.rb +7 -0
  59. data/lib/new_relic/agent/instrumentation/net_http.rb +44 -0
  60. data/lib/new_relic/agent/instrumentation/net_http/chain.rb +25 -0
  61. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +40 -0
  62. data/lib/new_relic/agent/instrumentation/net_http/prepend.rb +21 -0
  63. data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +1 -1
  64. data/lib/new_relic/agent/instrumentation/padrino.rb +18 -75
  65. data/lib/new_relic/agent/instrumentation/padrino/chain.rb +34 -0
  66. data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +27 -0
  67. data/lib/new_relic/agent/instrumentation/padrino/prepend.rb +20 -0
  68. data/lib/new_relic/agent/instrumentation/rack.rb +29 -160
  69. data/lib/new_relic/agent/instrumentation/rack/chain.rb +58 -0
  70. data/lib/new_relic/agent/instrumentation/rack/helpers.rb +32 -0
  71. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +73 -0
  72. data/lib/new_relic/agent/instrumentation/rack/prepend.rb +37 -0
  73. data/lib/new_relic/agent/instrumentation/rake.rb +13 -188
  74. data/lib/new_relic/agent/instrumentation/rake/chain.rb +25 -0
  75. data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +144 -0
  76. data/lib/new_relic/agent/instrumentation/rake/prepend.rb +14 -0
  77. data/lib/new_relic/agent/instrumentation/redis.rb +12 -186
  78. data/lib/new_relic/agent/instrumentation/redis/chain.rb +34 -0
  79. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +65 -0
  80. data/lib/new_relic/agent/instrumentation/redis/prepend.rb +24 -0
  81. data/lib/new_relic/agent/instrumentation/resque.rb +21 -32
  82. data/lib/new_relic/agent/instrumentation/resque/chain.rb +22 -0
  83. data/lib/new_relic/agent/instrumentation/resque/helper.rb +19 -0
  84. data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +35 -0
  85. data/lib/new_relic/agent/instrumentation/resque/prepend.rb +16 -0
  86. data/lib/new_relic/agent/instrumentation/sidekiq.rb +1 -1
  87. data/lib/new_relic/agent/instrumentation/sinatra.rb +20 -198
  88. data/lib/new_relic/agent/instrumentation/sinatra/chain.rb +55 -0
  89. data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +29 -34
  90. data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +124 -0
  91. data/lib/new_relic/agent/instrumentation/sinatra/prepend.rb +33 -0
  92. data/lib/new_relic/agent/instrumentation/typhoeus.rb +10 -89
  93. data/lib/new_relic/agent/instrumentation/typhoeus/chain.rb +22 -0
  94. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +82 -0
  95. data/lib/new_relic/agent/instrumentation/typhoeus/prepend.rb +14 -0
  96. data/lib/new_relic/agent/javascript_instrumentor.rb +12 -7
  97. data/lib/new_relic/agent/method_tracer.rb +6 -16
  98. data/lib/new_relic/agent/new_relic_service.rb +16 -13
  99. data/lib/new_relic/agent/samplers/memory_sampler.rb +1 -1
  100. data/lib/new_relic/agent/span_event_primitive.rb +10 -8
  101. data/lib/new_relic/agent/sql_sampler.rb +3 -3
  102. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +1 -1
  103. data/lib/new_relic/agent/transaction.rb +1 -4
  104. data/lib/new_relic/agent/transaction/abstract_segment.rb +1 -1
  105. data/lib/new_relic/agent/transaction/distributed_tracer.rb +12 -6
  106. data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -0
  107. data/lib/new_relic/agent/vm/mri_vm.rb +6 -4
  108. data/lib/new_relic/cli/commands/deployments.rb +0 -1
  109. data/lib/new_relic/constants.rb +4 -0
  110. data/lib/new_relic/control/frameworks/rails.rb +11 -9
  111. data/lib/new_relic/control/instance_methods.rb +1 -0
  112. data/lib/new_relic/dependency_detection.rb +119 -9
  113. data/lib/new_relic/environment_report.rb +1 -7
  114. data/lib/new_relic/noticed_error.rb +4 -8
  115. data/lib/new_relic/supportability_helper.rb +3 -2
  116. data/lib/new_relic/version.rb +3 -3
  117. data/lib/tasks/config.html.erb +14 -25
  118. data/lib/tasks/config.rake +8 -7
  119. data/newrelic_rpm.gemspec +2 -2
  120. data/test/agent_helper.rb +1 -0
  121. metadata +56 -32
  122. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
  123. data/.github/ISSUE_TEMPLATE/config.yml +0 -5
  124. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
  125. data/.github/actions/annotate/README.md +0 -79
  126. data/.github/actions/annotate/action.yml +0 -6
  127. data/.github/actions/annotate/dist/index.js +0 -433
  128. data/.github/actions/annotate/index.js +0 -25
  129. data/.github/actions/annotate/package-lock.json +0 -172
  130. data/.github/actions/annotate/package.json +0 -30
  131. data/.github/actions/annotate/pre-commit +0 -5
  132. data/.github/actions/build-ruby/README.md +0 -79
  133. data/.github/actions/build-ruby/action.yml +0 -15
  134. data/.github/actions/build-ruby/dist/index.js +0 -52683
  135. data/.github/actions/build-ruby/index.js +0 -514
  136. data/.github/actions/build-ruby/package-lock.json +0 -581
  137. data/.github/actions/build-ruby/package.json +0 -32
  138. data/.github/actions/build-ruby/pre-commit +0 -5
  139. data/.github/pull_request_template.md +0 -16
  140. data/.github/workflows/ci.yml +0 -212
  141. data/.github/workflows/pr_review_checklist.yml +0 -22
  142. data/.github/workflows/release.yml +0 -78
  143. data/.github/workflows/scripts/rubygems-authenticate.py +0 -13
  144. data/.github/workflows/scripts/rubygems-publish.rb +0 -32
  145. data/.github/workflows/snyk.yml +0 -27
  146. data/.github/workflows/stale.yml +0 -21
  147. data/cert/cacert.pem +0 -1177
  148. data/lib/new_relic/agent/instrumentation/http.rb +0 -49
  149. data/lib/new_relic/agent/instrumentation/net.rb +0 -87
@@ -23,7 +23,7 @@ module NewRelic
23
23
  end
24
24
 
25
25
  def has_key?(key)
26
- @cache.has_key?[key]
26
+ @cache.has_key? key
27
27
  end
28
28
 
29
29
  def keys
@@ -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
@@ -345,7 +345,7 @@ module NewRelic
345
345
  end
346
346
 
347
347
  def reset_cache
348
- @cache = Hash.new {|hash,key| hash[key] = self.fetch(key) }
348
+ @cache = Hash.new { |hash,key| hash[key] = self.fetch(key) }
349
349
  end
350
350
 
351
351
  def log_config(direction, source)
@@ -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
@@ -0,0 +1,33 @@
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
+ class DatabaseAdapter
8
+ VERSIONS = {
9
+ '6.1.0' => proc { ActiveRecord::Base.connection_db_config.configuration_hash[:adapter] },
10
+ '4.0.0' => proc { ActiveRecord::Base.connection_config[:adapter] },
11
+ '3.0.0' => proc { |env| ActiveRecord::Base.configurations[env]['adapter'] }
12
+ }
13
+
14
+ def self.value
15
+ return unless defined? ActiveRecord::Base
16
+ new(::NewRelic::Control.instance.env, ActiveRecord::VERSION::STRING).value
17
+ end
18
+
19
+ attr_reader :env, :version
20
+
21
+ def initialize(env, version)
22
+ @env = env
23
+ @version = Gem::Version.new(version)
24
+ end
25
+
26
+ def value
27
+ match = VERSIONS.keys.find { |key| version >= Gem::Version.new(key) }
28
+ return unless match
29
+ VERSIONS[match].call(env)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -6,11 +6,7 @@ module NewRelic
6
6
  module Agent
7
7
  module Datastores
8
8
  module Redis
9
- MULTI_OPERATION = 'multi'
10
- PIPELINE_OPERATION = 'pipeline'
11
9
  BINARY_DATA_PLACEHOLDER = "<binary data>"
12
- PRODUCT_NAME = 'Redis'
13
- CONNECT = 'connect'
14
10
 
15
11
  MAXIMUM_COMMAND_LENGTH = 1000
16
12
  MAXIMUM_ARGUMENT_LENGTH = 64
@@ -24,72 +24,6 @@ module NewRelic
24
24
  extend NewRelic::SupportabilityHelper
25
25
  extend self
26
26
 
27
- # Create a payload object containing the current transaction's
28
- # tracing properties (e.g., duration, priority). You can use
29
- # this object to generate headers to inject into a network
30
- # request, so that the downstream service can participate in a
31
- # distributed trace.
32
- #
33
- # @return [DistributedTracePayload] Payload for the current
34
- # transaction, or +nil+ if we
35
- # could not create the payload
36
- #
37
- # @api public
38
- #
39
- # @deprecated See {#create_distributed_trace_headers} instead.
40
- #
41
- def create_distributed_trace_payload
42
- Deprecator.deprecate :create_distributed_trace_payload, :create_distributed_trace_headers
43
-
44
- unless Agent.config[:'distributed_tracing.enabled']
45
- NewRelic::Agent.logger.warn "Not configured to create New Relic distributed trace payload"
46
- return nil
47
- end
48
-
49
- return unless transaction = Transaction.tl_current
50
- transaction.distributed_tracer.create_distributed_trace_payload
51
- rescue => e
52
- NewRelic::Agent.logger.error 'error during create_distributed_trace_payload', e
53
- nil
54
- end
55
-
56
- # Decode a JSON string containing distributed trace properties
57
- # (e.g., calling application, priority) and apply them to the
58
- # current transaction. You can use it to receive distributed
59
- # tracing information protocols the agent does not already
60
- # support.
61
- #
62
- # This method will fail if you call it after calling
63
- # {#create_distributed_trace_payload}.
64
- #
65
- # @param payload [String] Incoming distributed trace payload,
66
- # either as a JSON string or as a
67
- # header-friendly string returned from
68
- # {DistributedTracePayload#http_safe}
69
- #
70
- # @return nil
71
- #
72
- # @api public
73
- #
74
- # @deprecated See {#accept_distributed_trace_headers} instead
75
- #
76
- def accept_distributed_trace_payload payload
77
- Deprecator.deprecate :accept_distributed_trace_payload, :accept_distributed_trace_headers
78
-
79
- unless Agent.config[:'distributed_tracing.enabled']
80
- NewRelic::Agent.logger.warn "Not configured to accept New Relic distributed trace payload"
81
- return nil
82
- end
83
-
84
- return unless transaction = Transaction.tl_current
85
- transaction.distributed_tracer.accept_distributed_trace_payload(payload)
86
- nil
87
- rescue => e
88
- NewRelic::Agent.logger.error 'error during accept_distributed_trace_payload', e
89
- nil
90
- end
91
-
92
-
93
27
  # Adds the Distributed Trace headers so that the downstream service can participate in a
94
28
  # distributed trace. This method should be called every time an outbound call is made
95
29
  # since the header payload contains a timestamp.
@@ -52,14 +52,14 @@ module NewRelic
52
52
  result
53
53
  end
54
54
 
55
- def insert_cross_app_header request
55
+ def insert_cross_app_header headers
56
56
  return unless CrossAppTracing.cross_app_enabled?
57
57
  @is_cross_app_caller = true
58
58
  txn_guid = transaction.guid
59
59
  trip_id = cat_trip_id
60
60
  path_hash = cat_path_hash
61
61
 
62
- insert_request_headers request, txn_guid, trip_id, path_hash
62
+ insert_request_headers headers, txn_guid, trip_id, path_hash
63
63
  end
64
64
 
65
65
  def add_message_cat_headers headers
@@ -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