newrelic_rpm 3.13.0.299 → 3.13.1.300

Sign up to get free protection for your applications and to get access to all the features.
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