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
@@ -7,7 +7,8 @@ require 'newrelic_rpm'
7
7
  require 'new_relic/agent/datastores/mongo'
8
8
  require 'securerandom'
9
9
 
10
- if NewRelic::Agent::Datastores::Mongo.is_supported_version?
10
+ if NewRelic::Agent::Datastores::Mongo.is_supported_version? &&
11
+ !NewRelic::Agent::Datastores::Mongo.is_monitoring_enabled?
11
12
  require File.join(File.dirname(__FILE__), '..', '..', '..', 'helpers', 'mongo_metric_builder')
12
13
  require File.join(File.dirname(__FILE__), 'helpers', 'mongo_server')
13
14
  require File.join(File.dirname(__FILE__), 'helpers', 'mongo_replica_set')
@@ -7,7 +7,8 @@ require 'newrelic_rpm'
7
7
  require 'new_relic/agent/datastores/mongo'
8
8
  require 'securerandom'
9
9
 
10
- if NewRelic::Agent::Datastores::Mongo.is_supported_version?
10
+ if NewRelic::Agent::Datastores::Mongo.is_supported_version? &&
11
+ !NewRelic::Agent::Datastores::Mongo.is_monitoring_enabled?
11
12
  require File.join(File.dirname(__FILE__), '..', '..', '..', 'helpers', 'mongo_metric_builder')
12
13
  require File.join(File.dirname(__FILE__), 'helpers', 'mongo_server')
13
14
  require File.join(File.dirname(__FILE__), 'helpers', 'mongo_replica_set')
@@ -63,7 +63,7 @@ if !NewRelic::Agent::Datastores::Mongo.is_supported_version?
63
63
  end
64
64
  end
65
65
 
66
- if NewRelic::Agent::Datastores::Mongo.is_version2?
66
+ if NewRelic::Agent::Datastores::Mongo.is_unsupported_2x?
67
67
  include Mongo2xUnsupported
68
68
  else
69
69
  include Mongo1xUnsupported
@@ -47,6 +47,13 @@ if !defined?(MyApp)
47
47
  end
48
48
 
49
49
  if defined?(Sinatra)
50
+ module Sinatra
51
+ class Application < Base
52
+ # Override to not accidentally start the app in at_exit handler
53
+ set :run, Proc.new { false }
54
+ end
55
+ end
56
+
50
57
  class SinatraTestApp < Sinatra::Base
51
58
  get '/' do
52
59
  raise "Intentional error" if params["raise"]
@@ -6,7 +6,7 @@ end
6
6
  if RUBY_VERSION >= '2.0.0' || (RUBY_PLATFORM == 'java')
7
7
  gemfile <<-RB
8
8
  gem 'json'
9
- gem 'sidekiq', '~> 3.3.4'
9
+ gem 'sidekiq', '~> 3.4.2'
10
10
  gem 'rack'
11
11
 
12
12
  gem 'newrelic_rpm', :require => false, :path => File.expand_path('../../../../')
@@ -89,7 +89,6 @@ class NewRelic::Agent::Agent::StartTest < Minitest::Test
89
89
  private :at_exit
90
90
 
91
91
  def test_install_exit_handler_positive
92
- NewRelic::LanguageSupport.expects(:using_engine?).with('rbx').returns(false)
93
92
  NewRelic::LanguageSupport.expects(:using_engine?).with('jruby').returns(false)
94
93
  self.expects(:sinatra_classic_app?).returns(false)
95
94
  # we are overriding at_exit above, to immediately return, so we can
@@ -110,15 +109,11 @@ class NewRelic::Agent::Agent::StartTest < Minitest::Test
110
109
 
111
110
  def test_install_exit_handler_weird_ruby
112
111
  with_config(:send_data_one_exit => true) do
113
- NewRelic::LanguageSupport.expects(:using_engine?).with('rbx').returns(false)
114
112
  NewRelic::LanguageSupport.expects(:using_engine?).with('jruby').returns(false)
115
113
  self.expects(:sinatra_classic_app?).returns(true)
116
114
  install_exit_handler
117
- NewRelic::LanguageSupport.expects(:using_engine?).with('rbx').returns(false)
118
115
  NewRelic::LanguageSupport.expects(:using_engine?).with('jruby').returns(true)
119
116
  install_exit_handler
120
- NewRelic::LanguageSupport.expects(:using_engine?).with('rbx').returns(true)
121
- install_exit_handler
122
117
  end
123
118
  end
124
119
 
@@ -370,7 +370,7 @@ class NewRelic::Agent::DatabaseTest < Minitest::Test
370
370
  assert_equal('a' * (NewRelic::Agent::Database::MAX_QUERY_LENGTH - 3) + '...', truncated_query)
371
371
  end
372
372
 
373
- INVALID_UTF8_STRING = (''.respond_to?(:force_encoding) ? "\x80".force_encoding('UTF-8') : "\x80")
373
+ INVALID_UTF8_STRING = (''.respond_to?(:force_encoding) ? "select \x80".force_encoding('UTF-8') : "select \x80")
374
374
 
375
375
  def test_capture_query_mis_encoded
376
376
  query = INVALID_UTF8_STRING
@@ -382,6 +382,13 @@ class NewRelic::Agent::DatabaseTest < Minitest::Test
382
382
  assert_equal(expected_query, captured)
383
383
  end
384
384
 
385
+ def test_parse_operation_from_query_mis_encoded
386
+ query = INVALID_UTF8_STRING
387
+ expected = "select"
388
+ parsed = NewRelic::Agent::Database.parse_operation_from_query(query)
389
+ assert_equal(expected, parsed)
390
+ end
391
+
385
392
  sql_parsing_tests = load_cross_agent_test('sql_parsing')
386
393
  sql_parsing_tests.each_with_index do |test_case, i|
387
394
  define_method("test_sql_parsing_#{i}") do
@@ -0,0 +1,154 @@
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 File.expand_path(File.join(File.dirname(__FILE__),'..','..','..','..','test_helper'))
6
+ require 'new_relic/agent/datastores/mongo/event_formatter'
7
+
8
+ module NewRelic
9
+ module Agent
10
+ module Datastores
11
+ module Mongo
12
+ class EventFormatterTest < Minitest::Test
13
+
14
+ DATABASE = 'multiverse'.freeze
15
+
16
+ FIND_COMMAND = {
17
+ "find" => "tribbles",
18
+ "filter" => { "_id" => { "$gt" => 1 }, "name" => "joe" },
19
+ "sort" => { "_id" => 1 },
20
+ "limit" => 2,
21
+ "skip" => 2,
22
+ "comment" => "test",
23
+ "hint" => { "_id" => 1 },
24
+ "max" => { "_id" => 6 },
25
+ "maxScan" => 5000,
26
+ "maxTimeMS" => 6000,
27
+ "min" => { "_id" => 0 },
28
+ "readPreference" => { "mode" => "secondaryPreferred" },
29
+ "returnKey" => false,
30
+ "showRecordId" => false,
31
+ "snapshot" => false
32
+ }.freeze
33
+
34
+ INSERT_COMMAND = {
35
+ "insert" => "tribbles",
36
+ "ordered" => true,
37
+ "documents" => [{ :name => "test" }]
38
+ }.freeze
39
+
40
+ UPDATE_COMMAND = {
41
+ "update" => "tribbles",
42
+ "ordered" => true,
43
+ "updates" => [
44
+ {
45
+ :q => { :_id => { "$gt" => 1 }},
46
+ :u => { "$inc" => { :x => 1 }},
47
+ :multi => false,
48
+ :upsert => false
49
+ }
50
+ ]
51
+ }.freeze
52
+
53
+ DELETE_COMMAND = {
54
+ "delete" => "tribbles",
55
+ "ordered" => true,
56
+ "deletes" => [{ :q => { :_id => { "$gt" => 1 }}, :limit => 1 }]
57
+ }.freeze
58
+
59
+ if RUBY_VERSION > "1.9.3"
60
+
61
+ def test_doesnt_modify_incoming_statement
62
+ formatted = EventFormatter.format('find', DATABASE, FIND_COMMAND)
63
+ refute_same FIND_COMMAND, formatted
64
+ end
65
+
66
+ def test_can_disable_statement_capturing_queries
67
+ with_config(:'mongo.capture_queries' => false) do
68
+ formatted = EventFormatter.format('find', DATABASE, FIND_COMMAND)
69
+ assert_nil formatted
70
+ end
71
+ end
72
+
73
+ def test_event_formatter_obfuscates_by_default
74
+ expected = {
75
+ :operation => :find,
76
+ :database => DATABASE,
77
+ :collection => "tribbles",
78
+ "find" => "tribbles",
79
+ "filter" => { "_id" => { "$gt" => "?" }, "name" => "?" },
80
+ "sort" => { "_id" => 1 },
81
+ "limit" => 2,
82
+ "skip" => 2,
83
+ "comment" => "test",
84
+ "hint" => { "_id" => 1 },
85
+ "max" => { "_id" => 6 },
86
+ "maxScan" => 5000,
87
+ "maxTimeMS" => 6000,
88
+ "min" => { "_id" => 0 },
89
+ "readPreference" => { "mode" => "secondaryPreferred" },
90
+ "returnKey" => false,
91
+ "showRecordId" => false,
92
+ "snapshot" => false
93
+ }
94
+
95
+ formatted = EventFormatter.format(:find, DATABASE, FIND_COMMAND)
96
+ assert_equal expected, formatted
97
+ end
98
+
99
+ def test_event_formatter_raw_selectors
100
+ with_config(:'mongo.obfuscate_queries' => false) do
101
+ formatted = EventFormatter.format(:find, DATABASE, FIND_COMMAND)
102
+ expected = FIND_COMMAND.merge(
103
+ :operation => :find,
104
+ :database => DATABASE,
105
+ :collection => 'tribbles'
106
+ )
107
+ assert_equal expected, formatted
108
+ end
109
+ end
110
+
111
+ def test_event_formatter_blacklists_inserts
112
+ expected = {
113
+ :operation => :insert,
114
+ :database => DATABASE,
115
+ :collection => "tribbles",
116
+ "insert" => "tribbles",
117
+ "ordered" => true
118
+ }
119
+
120
+ formatted = EventFormatter.format(:insert, DATABASE, INSERT_COMMAND)
121
+ assert_equal expected, formatted
122
+ end
123
+
124
+ def test_event_formatter_blacklists_updates
125
+ expected = {
126
+ :operation => :update,
127
+ :database => DATABASE,
128
+ :collection => "tribbles",
129
+ "update" => "tribbles",
130
+ "ordered" => true
131
+ }
132
+
133
+ formatted = EventFormatter.format(:update, DATABASE, UPDATE_COMMAND)
134
+ assert_equal expected, formatted
135
+ end
136
+
137
+ def test_event_formatter_blacklists_deletes
138
+ expected = {
139
+ :operation => :delete,
140
+ :database => DATABASE,
141
+ :collection => "tribbles",
142
+ "delete" => "tribbles",
143
+ "ordered" => true
144
+ }
145
+
146
+ formatted = EventFormatter.format(:delete, DATABASE, DELETE_COMMAND)
147
+ assert_equal expected, formatted
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -471,19 +471,6 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
471
471
  assert_equal('STACK STACK STACK', @error_collector.extract_stack_trace(exception))
472
472
  end
473
473
 
474
- def test_over_queue_limit_negative
475
- refute @error_collector.over_queue_limit?(nil)
476
- end
477
-
478
- def test_over_queue_limit_positive
479
- expects_logging(:warn, includes('The error reporting queue has reached 20'))
480
- 21.times do
481
- @error_collector.notice_error("", {})
482
- end
483
-
484
- assert @error_collector.over_queue_limit?('hooray')
485
- end
486
-
487
474
  def test_skip_notice_error_is_true_if_the_error_collector_is_disabled
488
475
  error = StandardError.new
489
476
  with_config(:'error_collector.enabled' => false) do
@@ -0,0 +1,30 @@
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 File.expand_path(File.join(File.dirname(__FILE__),'..','..','test_helper'))
6
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','data_container_tests'))
7
+
8
+ module NewRelic
9
+ module Agent
10
+ class ErrorTraceAggregatorTest < Minitest::Test
11
+ def setup
12
+ @aggregator = ErrorTraceAggregator.new(20)
13
+ end
14
+
15
+ def test_over_queue_limit_negative
16
+ refute @aggregator.over_queue_limit?(nil)
17
+ end
18
+
19
+ def test_over_queue_limit_positive
20
+ expects_logging(:warn, includes('The error reporting queue has reached 20'))
21
+ 21.times do
22
+ error = stub(:message => "", :is_internal => false)
23
+ @aggregator.add_to_error_queue(error)
24
+ end
25
+
26
+ assert @aggregator.over_queue_limit?('hooray')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,72 @@
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 File.expand_path(File.join(File.dirname(__FILE__),'..','..','..','test_helper'))
6
+ require 'new_relic/agent/instrumentation/mongodb_command_subscriber'
7
+
8
+ class NewRelic::Agent::Instrumentation::MongodbCommandSubscriberTest < Minitest::Test
9
+
10
+ if RUBY_VERSION > "1.9.3"
11
+ def setup
12
+ @started_event = mock('started event')
13
+ @started_event.stubs(:operation_id).returns(1)
14
+ @started_event.stubs(:command_name).returns('find')
15
+ @started_event.stubs(:database_name).returns('mongodb-test')
16
+ @started_event.stubs(:command).returns({ 'find' => 'users', 'filter' => { 'name' => 'test' }})
17
+
18
+ @succeeded_event = mock('succeeded event')
19
+ @succeeded_event.stubs(:operation_id).returns(1)
20
+ @succeeded_event.stubs(:duration).returns(2)
21
+
22
+ @subscriber = NewRelic::Agent::Instrumentation::MongodbCommandSubscriber.new
23
+
24
+ @stats_engine = NewRelic::Agent.instance.stats_engine
25
+ @stats_engine.clear_stats
26
+ end
27
+
28
+ def test_records_metrics_for_simple_find
29
+ simulate_query
30
+
31
+ metric_name = 'Datastore/statement/MongoDB/users/find'
32
+ assert_metrics_recorded(
33
+ metric_name => { :call_count => 1, :total_call_time => 2.0 }
34
+ )
35
+ end
36
+
37
+ def test_records_scoped_metrics
38
+ in_transaction('test_txn') { simulate_query }
39
+
40
+ metric_name = 'Datastore/statement/MongoDB/users/find'
41
+ assert_metrics_recorded(
42
+ [ metric_name, 'test_txn' ] => { :call_count => 1, :total_call_time => 2 }
43
+ )
44
+ end
45
+
46
+ def test_records_nothing_if_tracing_disabled
47
+ NewRelic::Agent.disable_all_tracing { simulate_query }
48
+ metric_name = 'Datastore/statement/MongoDB/users/find'
49
+ assert_metrics_not_recorded([ metric_name ])
50
+ end
51
+
52
+ def test_records_rollup_metrics
53
+ in_web_transaction { simulate_query }
54
+
55
+ assert_metrics_recorded(
56
+ 'Datastore/operation/MongoDB/find' => { :call_count => 1, :total_call_time => 2 },
57
+ 'Datastore/allWeb' => { :call_count => 1, :total_call_time => 2 },
58
+ 'Datastore/all' => { :call_count => 1, :total_call_time => 2 }
59
+ )
60
+ end
61
+
62
+ def test_should_not_raise_due_to_an_exception_during_instrumentation_callback
63
+ @subscriber.stubs(:metrics).raises(StandardError)
64
+ simulate_query
65
+ end
66
+
67
+ def simulate_query
68
+ @subscriber.started(@started_event)
69
+ @subscriber.succeeded(@succeeded_event)
70
+ end
71
+ end
72
+ end
@@ -161,32 +161,34 @@ class NewRelic::Agent::MetricStatsTest < Minitest::Test
161
161
  ])
162
162
  end
163
163
 
164
- def test_record_scoped_and_unscoped_metrics_is_thread_safe
165
- threads = []
166
- nthreads = 25
167
- iterations = 100
168
-
169
- nthreads.times do |tid|
170
- threads << Thread.new do
171
- iterations.times do
172
- in_transaction('txn') do
173
- @engine.tl_record_scoped_and_unscoped_metrics('m1', ['m3'], 1)
174
- @engine.tl_record_scoped_and_unscoped_metrics('m2', ['m4'], 1)
164
+ unless NewRelic::LanguageSupport.rubinius? # Routine segfaults with rbx, see RUBY-1507
165
+ def test_record_scoped_and_unscoped_metrics_is_thread_safe
166
+ threads = []
167
+ nthreads = 25
168
+ iterations = 100
169
+
170
+ nthreads.times do |tid|
171
+ threads << Thread.new do
172
+ iterations.times do
173
+ in_transaction('txn') do
174
+ @engine.tl_record_scoped_and_unscoped_metrics('m1', ['m3'], 1)
175
+ @engine.tl_record_scoped_and_unscoped_metrics('m2', ['m4'], 1)
176
+ end
175
177
  end
176
178
  end
177
179
  end
180
+ threads.each { |t| t.join }
181
+
182
+ expected = { :call_count => nthreads * iterations }
183
+ assert_metrics_recorded(
184
+ 'm1' => expected,
185
+ 'm2' => expected,
186
+ ['m1', 'txn'] => expected,
187
+ ['m2', 'txn'] => expected,
188
+ 'm3' => expected,
189
+ 'm4' => expected
190
+ )
178
191
  end
179
- threads.each { |t| t.join }
180
-
181
- expected = { :call_count => nthreads * iterations }
182
- assert_metrics_recorded(
183
- 'm1' => expected,
184
- 'm2' => expected,
185
- ['m1', 'txn'] => expected,
186
- ['m2', 'txn'] => expected,
187
- 'm3' => expected,
188
- 'm4' => expected
189
- )
190
192
  end
191
193
 
192
194
  def test_record_scoped_and_unscoped_metrics_records_unscoped_if_not_in_txn
@@ -31,8 +31,16 @@ class NewRelic::Agent::SystemInfoTest < Minitest::Test
31
31
  assert_equal(num_physical_cores , info[:num_physical_cores ])
32
32
  assert_equal(num_logical_processors, info[:num_logical_processors])
33
33
  end
34
+ elsif File.basename(file) =~ /malformed/
35
+ define_method("test_#{File.basename(file)}") do
36
+ cpuinfo = File.read(file)
37
+ info = @sysinfo.parse_cpuinfo(cpuinfo)
38
+ assert_equal(nil, info[:num_physical_package])
39
+ assert_equal(nil, info[:num_physical_cores])
40
+ assert_equal(nil, info[:num_logical_processors])
41
+ end
34
42
  else
35
- fail "Bad filename: cross_agent_tests/proc_cpuinfo/#{file}"
43
+ fail "Bad filename: #{file}"
36
44
  end
37
45
  end
38
46