newrelic_rpm 3.13.0.299 → 3.13.1.300

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG +36 -1
  4. data/lib/new_relic/agent/agent.rb +0 -1
  5. data/lib/new_relic/agent/database.rb +1 -1
  6. data/lib/new_relic/agent/datastores/mongo.rb +8 -7
  7. data/lib/new_relic/agent/datastores/mongo/event_formatter.rb +49 -0
  8. data/lib/new_relic/agent/datastores/mongo/obfuscator.rb +2 -2
  9. data/lib/new_relic/agent/error_collector.rb +12 -51
  10. data/lib/new_relic/agent/error_trace_aggregator.rb +89 -0
  11. data/lib/new_relic/agent/instrumentation/active_record.rb +1 -1
  12. data/lib/new_relic/agent/instrumentation/active_record_4.rb +1 -1
  13. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +26 -1
  14. data/lib/new_relic/agent/instrumentation/mongo.rb +16 -1
  15. data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +74 -0
  16. data/lib/new_relic/agent/supported_versions.rb +15 -1
  17. data/lib/new_relic/agent/system_info.rb +5 -0
  18. data/lib/new_relic/recipes/capistrano3.rb +1 -1
  19. data/lib/new_relic/recipes/capistrano_legacy.rb +1 -1
  20. data/lib/new_relic/version.rb +1 -1
  21. data/newrelic_rpm.gemspec +1 -1
  22. data/test/environments/lib/environments/runner.rb +1 -0
  23. data/test/fixtures/cross_agent_tests/proc_cpuinfo/README.md +4 -0
  24. data/test/fixtures/cross_agent_tests/proc_cpuinfo/malformed_file.txt +3 -0
  25. data/test/multiverse/lib/multiverse/suite.rb +4 -4
  26. data/test/multiverse/suites/active_record/active_record_test.rb +67 -26
  27. data/test/multiverse/suites/agent_only/start_up_test.rb +17 -3
  28. data/test/multiverse/suites/config_file_loading/Envfile +1 -1
  29. data/test/multiverse/suites/mongo/Envfile +3 -1
  30. data/test/multiverse/suites/mongo/mongo2_instrumentation_test.rb +344 -0
  31. data/test/multiverse/suites/mongo/mongo_connection_test.rb +2 -1
  32. data/test/multiverse/suites/mongo/mongo_instrumentation_test.rb +2 -1
  33. data/test/multiverse/suites/mongo/mongo_unsupported_version_test.rb +1 -1
  34. data/test/multiverse/suites/rails/rails3_app/app_rails3_plus.rb +7 -0
  35. data/test/multiverse/suites/sidekiq/Envfile +1 -1
  36. data/test/new_relic/agent/agent/start_test.rb +0 -5
  37. data/test/new_relic/agent/database_test.rb +8 -1
  38. data/test/new_relic/agent/datastores/mongo/event_formatter_test.rb +154 -0
  39. data/test/new_relic/agent/error_collector_test.rb +0 -13
  40. data/test/new_relic/agent/error_trace_aggregator_test.rb +30 -0
  41. data/test/new_relic/agent/instrumentation/mongodb_command_subscriber_test.rb +72 -0
  42. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +24 -22
  43. data/test/new_relic/agent/system_info_test.rb +9 -1
  44. data/test/new_relic/rack/error_collector_test.rb +1 -2
  45. metadata +10 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a66f2339d582713549cd887c6c249a1e167060a
4
- data.tar.gz: 340be0c604f5a041c013c02118bc1f83bfae2fd1
3
+ metadata.gz: 0b291cf3e5902d8fa8dbf9c03754ba1358d6a4ba
4
+ data.tar.gz: 12fd3b54abcbe8f42dfa4a15f3f5415633e0cfa5
5
5
  SHA512:
6
- metadata.gz: f09c1d8499f23aa1659c2a7622f7655b94644493cca9401f9ba663c69c8ff6394c06ef9238b7bb0fc8a62f10be9dd099d27a45e80403810d801ab55ade84d5f0
7
- data.tar.gz: 6c325a570205de1ce13fe012203255fbd413684f9f387149f3a967d959064965f1284e4c50820f5499d37f29ab8cae39018f3ff60aab0580256283569923f037
6
+ metadata.gz: a23de732cf89b9ca75ba7ada9a154eff9416aed114550c89c8422d5f1189fd0c5fd8f0851da9cebe2f120daa3d3354e13bc8afc1a47b5088c275b4bfd1b56c2e
7
+ data.tar.gz: 034cd20ec28754fd2df81ac73a84458dd1bb96a08c9a2b8253583dc9ff6e910e6cd89db1efe41dbe6d1f0f4148be550d8691183096c5896e4433b709d96d4544
@@ -9,4 +9,9 @@ rvm:
9
9
  - 2.2
10
10
  - jruby-19mode
11
11
  - jruby-18mode
12
+ - rbx-2.5.8
12
13
 
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: rbx-2
17
+
data/CHANGELOG CHANGED
@@ -1,10 +1,45 @@
1
1
  # New Relic Ruby Agent Release Notes #
2
2
 
3
+ ## v3.13.1 ##
4
+
5
+ * Don't use a pager when running `git log` command
6
+
7
+ This would cause Capistrano deploys to hang when a large number of commits were being deployed.
8
+ Thanks to John Naegle for reporting and fixing this issue!
9
+
10
+ * Official support for JRuby 9.0.0.0
11
+
12
+ The Ruby agent is now officially fully tested and supported on JRuby 9.0.0.0.
13
+
14
+ * Instrumentation for MongoDB 2.1.x
15
+
16
+ Visibility in your MongoDB queries returns when using version 2.1.0 of
17
+ the Mongo driver or newer. Thanks to Durran Jordan of MongoDB for contributing
18
+ the Mongo Monitoring instrumentation!
19
+
20
+ * Fix for ArgumentError "invalid byte sequence in UTF-8"
21
+
22
+ This would come up when trying to parse out the operation from a database query
23
+ containing characters that would trigger a invalid byte sequence in UTF-8 error.
24
+ Thanks to Mario Izquierdo for reporting this issue!
25
+
26
+ * Improved database metric names for ActiveRecord::Calculations queries
27
+
28
+ Aggregate metrics recorded for queries made via the ActiveRecord::Calculations
29
+ module (#count, #sum, #max, etc.) will now be associated with the correct
30
+ model name, rather than being counted as generic 'select' operations.
31
+
32
+ * Allow at_exit handlers to be installed for Rubinius
33
+
34
+ Rubinius can support the at_exit block used by install_exit_handler.
35
+ Thanks to Aidan Coyle for reporting and fixing this issue!
36
+
3
37
  ## v3.13.0 ##
4
38
 
5
39
  * Bugfix for uninitialized constant NewRelic::Agent::ParameterFiltering
6
40
 
7
- Users in some environments encountered a NameError: uninitialized constant NewRelic::Agent::ParameterFiltering from the Rails instrumentation while
41
+ Users in some environments encountered a NameError: uninitialized constant
42
+ NewRelic::Agent::ParameterFiltering from the Rails instrumentation while
8
43
  running v3.12.x of the Ruby agent. This issue has been fixed.
9
44
 
10
45
  * Rake task instrumentation
@@ -370,7 +370,6 @@ module NewRelic
370
370
  def should_install_exit_handler?
371
371
  (
372
372
  Agent.config[:send_data_on_exit] &&
373
- !NewRelic::LanguageSupport.using_engine?('rbx') &&
374
373
  !NewRelic::LanguageSupport.using_engine?('jruby') &&
375
374
  !sinatra_classic_app?
376
375
  )
@@ -266,7 +266,7 @@ module NewRelic
266
266
  EMPTY_STRING = ''.freeze
267
267
 
268
268
  def parse_operation_from_query(sql)
269
- sql = sql.gsub(SQL_COMMENT_REGEX, EMPTY_STRING)
269
+ sql = Helper.correctly_encoded(sql).gsub(SQL_COMMENT_REGEX, EMPTY_STRING)
270
270
  if sql =~ /(\w+)/
271
271
  op = $1.downcase
272
272
  return op if KNOWN_OPERATIONS.include?(op)
@@ -8,15 +8,16 @@ module NewRelic
8
8
  module Mongo
9
9
  def self.is_supported_version?
10
10
  # No version constant in < 2.0 versions of Mongo :(
11
- defined?(::Mongo) &&
12
- defined?(::Mongo::MongoClient) &&
13
- !is_version2?
11
+ defined?(::Mongo) && (defined?(::Mongo::MongoClient) || is_monitoring_enabled?)
14
12
  end
15
13
 
16
- # At present we explicitly don't support version 2.x of the driver yet
17
- def self.is_version2?
18
- defined?(::Mongo::VERSION) &&
19
- NewRelic::VersionNumber.new(::Mongo::VERSION) > NewRelic::VersionNumber.new("2.0.0")
14
+ def self.is_monitoring_enabled?
15
+ defined?(::Mongo::Monitoring)
16
+ end
17
+
18
+ def self.is_unsupported_2x?
19
+ defined?(::Mongo::VERSION) && VersionNumber.new(::Mongo::VERSION).major_version == 2 &&
20
+ !self.is_monitoring_enabled?
20
21
  end
21
22
 
22
23
  def self.is_version_1_10_or_later?
@@ -0,0 +1,49 @@
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
+ require 'new_relic/agent/datastores/mongo/obfuscator'
5
+
6
+ module NewRelic
7
+ module Agent
8
+ module Datastores
9
+ module Mongo
10
+ module EventFormatter
11
+
12
+ # Keys that will get their values replaced with '?'.
13
+ OBFUSCATE_KEYS = [ 'filter', 'query' ].freeze
14
+
15
+ # Keys that will get completely removed from the statement.
16
+ BLACKLISTED_KEYS = [ 'deletes', 'documents', 'updates' ].freeze
17
+
18
+ def self.format(command_name, database_name, command)
19
+ return nil unless NewRelic::Agent.config[:'mongo.capture_queries']
20
+
21
+ result = {
22
+ :operation => command_name,
23
+ :database => database_name,
24
+ :collection => command.values.first
25
+ }
26
+
27
+ command.each do |key, value|
28
+ next if BLACKLISTED_KEYS.include?(key)
29
+ if OBFUSCATE_KEYS.include?(key)
30
+ obfuscated = obfuscate(value)
31
+ result[key] = obfuscated if obfuscated
32
+ else
33
+ result[key] = value
34
+ end
35
+ end
36
+ result
37
+ end
38
+
39
+ def self.obfuscate(statement)
40
+ if NewRelic::Agent.config[:'mongo.obfuscate_queries']
41
+ statement = Obfuscator.obfuscate_statement(statement)
42
+ end
43
+ statement
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -10,7 +10,7 @@ module NewRelic
10
10
 
11
11
  WHITELIST = [:operation].freeze
12
12
 
13
- def self.obfuscate_statement(source, whitelist=WHITELIST)
13
+ def self.obfuscate_statement(source, whitelist = WHITELIST)
14
14
  obfuscated = {}
15
15
  source.each do |key, value|
16
16
  if whitelist.include?(key)
@@ -23,7 +23,7 @@ module NewRelic
23
23
  obfuscated
24
24
  end
25
25
 
26
- def self.obfuscate_value(value, whitelist)
26
+ def self.obfuscate_value(value, whitelist = WHITELIST)
27
27
  if value.is_a?(Hash)
28
28
  obfuscate_statement(value, whitelist)
29
29
  elsif value.is_a?(Array)
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+ require 'new_relic/agent/error_trace_aggregator'
4
5
 
5
6
  module NewRelic
6
7
  module Agent
@@ -15,17 +16,15 @@ module NewRelic
15
16
  MAX_ERROR_QUEUE_LENGTH = 20 unless defined? MAX_ERROR_QUEUE_LENGTH
16
17
  EXCEPTION_TAG_IVAR = :'@__nr_seen_exception' unless defined? EXCEPTION_TAG_IVAR
17
18
 
18
- attr_accessor :errors
19
-
20
19
  # Returns a new error collector
21
20
  def initialize
22
- @errors = []
21
+ @error_trace_aggregator = ErrorTraceAggregator.new(MAX_ERROR_QUEUE_LENGTH)
22
+
23
23
 
24
24
  # lookup of exception class names to ignore. Hash for fast access
25
25
  @ignore = {}
26
26
 
27
27
  initialize_ignored_errors(Agent.config[:'error_collector.ignore_errors'])
28
- @lock = Mutex.new
29
28
 
30
29
  Agent.config.register_callback(:'error_collector.enabled') do |config_enabled|
31
30
  ::NewRelic::Agent.logger.debug "Errors will #{config_enabled ? '' : 'not '}be sent to the New Relic service."
@@ -35,6 +34,10 @@ module NewRelic
35
34
  end
36
35
  end
37
36
 
37
+ def errors
38
+ @error_trace_aggregator.errors
39
+ end
40
+
38
41
  def initialize_ignored_errors(ignore_errors)
39
42
  @ignore.clear
40
43
  ignore_errors = ignore_errors.split(",") if ignore_errors.is_a? String
@@ -189,25 +192,6 @@ module NewRelic
189
192
  sense_method(actual_exception, 'backtrace') || '<no stack trace>'
190
193
  end
191
194
 
192
- # checks the size of the error queue to make sure we are under
193
- # the maximum limit, and logs a warning if we are over the limit.
194
- def over_queue_limit?(message)
195
- over_limit = (@errors.reject{|err| err.is_internal}.length >= MAX_ERROR_QUEUE_LENGTH)
196
- ::NewRelic::Agent.logger.warn("The error reporting queue has reached #{MAX_ERROR_QUEUE_LENGTH}. The error detail for this and subsequent errors will not be transmitted to New Relic until the queued errors have been sent: #{message}") if over_limit
197
- over_limit
198
- end
199
-
200
- # Synchronizes adding an error to the error queue, and checks if
201
- # the error queue is too long - if so, we drop the error on the
202
- # floor after logging a warning.
203
- def add_to_error_queue(noticed_error)
204
- @lock.synchronize do
205
- if !over_queue_limit?(noticed_error.message) && !@errors.include?(noticed_error)
206
- @errors << noticed_error
207
- end
208
- end
209
- end
210
-
211
195
  # See NewRelic::Agent.notice_error for options and commentary
212
196
  def notice_error(exception, options={}) #THREAD_LOCAL_ACCESS
213
197
  return if skip_notice_error?(exception)
@@ -216,7 +200,7 @@ module NewRelic
216
200
 
217
201
  state = ::NewRelic::Agent::TransactionState.tl_get
218
202
  increment_error_count!(state, exception, options)
219
- add_to_error_queue(create_noticed_error(exception, options))
203
+ @error_trace_aggregator.add_to_error_queue(create_noticed_error(exception, options))
220
204
 
221
205
  exception
222
206
  rescue => e
@@ -272,44 +256,21 @@ module NewRelic
272
256
  # class per harvest, disregarding (and not impacting) the app error queue
273
257
  # limit.
274
258
  def notice_agent_error(exception)
275
- return unless exception.class < NewRelic::Agent::InternalAgentError
276
-
277
- # Log 'em all!
278
- NewRelic::Agent.logger.info(exception)
279
-
280
- @lock.synchronize do
281
- # Already seen this class once? Bail!
282
- return if @errors.any? { |err| err.exception_class_name == exception.class.name }
283
-
284
- trace = exception.backtrace || caller.dup
285
- noticed_error = NewRelic::NoticedError.new("NewRelic/AgentError", exception)
286
- noticed_error.stack_trace = trace
287
- @errors << noticed_error
288
- end
289
- rescue => e
290
- NewRelic::Agent.logger.info("Unable to capture internal agent error due to an exception:", e)
259
+ @error_trace_aggregator.notice_agent_error(exception)
291
260
  end
292
261
 
293
262
  def merge!(errors)
294
- errors.each do |error|
295
- add_to_error_queue(error)
296
- end
263
+ @error_trace_aggregator.merge!(errors)
297
264
  end
298
265
 
299
266
  # Get the errors currently queued up. Unsent errors are left
300
267
  # over from a previous unsuccessful attempt to send them to the server.
301
268
  def harvest!
302
- @lock.synchronize do
303
- errors = @errors
304
- @errors = []
305
- errors
306
- end
269
+ @error_trace_aggregator.harvest!
307
270
  end
308
271
 
309
272
  def reset!
310
- @lock.synchronize do
311
- @errors = []
312
- end
273
+ @error_trace_aggregator.reset!
313
274
  end
314
275
  end
315
276
  end
@@ -0,0 +1,89 @@
1
+ # -*- ruby -*-
2
+ # encoding: utf-8
3
+ # This file is distributed under New Relic's license terms.
4
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
5
+
6
+ module NewRelic
7
+ module Agent
8
+ class ErrorTraceAggregator
9
+ def initialize(capacity)
10
+ @capacity = capacity
11
+ @lock = Mutex.new
12
+ @errors = []
13
+ end
14
+
15
+ def merge!(errors)
16
+ errors.each do |error|
17
+ add_to_error_queue(error)
18
+ end
19
+ end
20
+
21
+ # Get the errors currently queued up. Unsent errors are left
22
+ # over from a previous unsuccessful attempt to send them to the server.
23
+ def harvest!
24
+ @lock.synchronize do
25
+ errors = @errors
26
+ @errors = []
27
+ errors
28
+ end
29
+ end
30
+
31
+ def reset!
32
+ @lock.synchronize do
33
+ @errors = []
34
+ end
35
+ end
36
+
37
+ # Synchronizes adding an error to the error queue, and checks if
38
+ # the error queue is too long - if so, we drop the error on the
39
+ # floor after logging a warning.
40
+ def add_to_error_queue(noticed_error)
41
+ @lock.synchronize do
42
+ if !over_queue_limit?(noticed_error.message) && !@errors.include?(noticed_error)
43
+ @errors << noticed_error
44
+ end
45
+ end
46
+ end
47
+
48
+ # checks the size of the error queue to make sure we are under
49
+ # the maximum limit, and logs a warning if we are over the limit.
50
+ def over_queue_limit?(message)
51
+ over_limit = (@errors.reject{|err| err.is_internal}.length >= @capacity)
52
+ ::NewRelic::Agent.logger.warn("The error reporting queue has reached #{@capacity}. The error detail for this and subsequent errors will not be transmitted to New Relic until the queued errors have been sent: #{message}") if over_limit
53
+ over_limit
54
+ end
55
+
56
+ def errors
57
+ @lock.synchronize{ @errors }
58
+ end
59
+
60
+ # *Use sparingly for difficult to track bugs.*
61
+ #
62
+ # Track internal agent errors for communication back to New Relic.
63
+ # To use, make a specific subclass of NewRelic::Agent::InternalAgentError,
64
+ # then pass an instance of it to this method when your problem occurs.
65
+ #
66
+ # Limits are treated differently for these errors. We only gather one per
67
+ # class per harvest, disregarding (and not impacting) the app error queue
68
+ # limit.
69
+ def notice_agent_error(exception)
70
+ return unless exception.class < NewRelic::Agent::InternalAgentError
71
+
72
+ # Log 'em all!
73
+ NewRelic::Agent.logger.info(exception)
74
+
75
+ @lock.synchronize do
76
+ # Already seen this class once? Bail!
77
+ return if @errors.any? { |err| err.exception_class_name == exception.class.name }
78
+
79
+ trace = exception.backtrace || caller.dup
80
+ noticed_error = NewRelic::NoticedError.new("NewRelic/AgentError", exception)
81
+ noticed_error.stack_trace = trace
82
+ @errors << noticed_error
83
+ end
84
+ rescue => e
85
+ NewRelic::Agent.logger.info("Unable to capture internal agent error due to an exception:", e)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -18,7 +18,7 @@ module NewRelic
18
18
 
19
19
  def self.insert_instrumentation
20
20
  if defined?(::ActiveRecord::VERSION::MAJOR) && ::ActiveRecord::VERSION::MAJOR.to_i >= 3
21
- ::NewRelic::Agent::Instrumentation::ActiveRecordHelper.instrument_writer_methods
21
+ ::NewRelic::Agent::Instrumentation::ActiveRecordHelper.instrument_additional_methods
22
22
  end
23
23
 
24
24
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
@@ -24,6 +24,6 @@ DependencyDetection.defer do
24
24
  executes do
25
25
  ActiveSupport::Notifications.subscribe('sql.active_record',
26
26
  NewRelic::Agent::Instrumentation::ActiveRecordSubscriber.new)
27
- ::NewRelic::Agent::Instrumentation::ActiveRecordHelper.instrument_writer_methods
27
+ ::NewRelic::Agent::Instrumentation::ActiveRecordHelper.instrument_additional_methods
28
28
  end
29
29
  end
@@ -12,7 +12,12 @@ module NewRelic
12
12
  module_function
13
13
 
14
14
  # Used by both the AR 3.x and 4.x instrumentation
15
- def instrument_writer_methods
15
+ def instrument_additional_methods
16
+ instrument_save_methods
17
+ instrument_relation_methods
18
+ end
19
+
20
+ def instrument_save_methods
16
21
  ::ActiveRecord::Base.class_eval do
17
22
  alias_method :save_without_newrelic, :save
18
23
 
@@ -30,7 +35,9 @@ module NewRelic
30
35
  end
31
36
  end
32
37
  end
38
+ end
33
39
 
40
+ def instrument_relation_methods
34
41
  ::ActiveRecord::Relation.class_eval do
35
42
  alias_method :update_all_without_newrelic, :update_all
36
43
 
@@ -55,6 +62,24 @@ module NewRelic
55
62
  destroy_all_without_newrelic(*args, &blk)
56
63
  end
57
64
  end
65
+
66
+ alias_method :calculate_without_newrelic, :calculate
67
+
68
+ def calculate(*args, &blk)
69
+ ::NewRelic::Agent.with_database_metric_name(self.name, nil, ACTIVE_RECORD) do
70
+ calculate_without_newrelic(*args, &blk)
71
+ end
72
+ end
73
+
74
+ if method_defined?(:pluck)
75
+ alias_method :pluck_without_newrelic, :pluck
76
+
77
+ def pluck(*args, &blk)
78
+ ::NewRelic::Agent.with_database_metric_name(self.name, nil, ACTIVE_RECORD) do
79
+ pluck_without_newrelic(*args, &blk)
80
+ end
81
+ end
82
+ end
58
83
  end
59
84
  end
60
85