newrelic_rpm 3.7.2.190.beta → 3.7.2.192

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +48 -0
  3. data/LICENSE +1 -1
  4. data/lib/new_relic/agent/configuration/default_source.rb +0 -6
  5. data/lib/new_relic/agent/cross_app_monitor.rb +7 -6
  6. data/lib/new_relic/agent/datastores/mongo/metric_generator.rb +8 -0
  7. data/lib/new_relic/agent/datastores/mongo/metric_translator.rb +27 -36
  8. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +21 -11
  9. data/lib/new_relic/agent/instrumentation/mongo.rb +56 -40
  10. data/lib/new_relic/agent/method_tracer.rb +10 -3
  11. data/lib/new_relic/agent/transaction_sampler.rb +0 -3
  12. data/lib/sequel/extensions/newrelic_instrumentation.rb +12 -5
  13. data/test/helpers/mongo_metric_builder.rb +1 -1
  14. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +2 -2
  15. data/test/multiverse/suites/mongo/Envfile +19 -28
  16. data/test/multiverse/suites/mongo/helpers/mongo_operation_tests.rb +437 -0
  17. data/test/multiverse/suites/mongo/helpers/mongo_replica_set.rb +97 -0
  18. data/test/multiverse/suites/mongo/helpers/mongo_replica_set_test.rb +82 -0
  19. data/test/multiverse/suites/mongo/helpers/mongo_server.rb +239 -0
  20. data/test/multiverse/suites/mongo/helpers/mongo_server_test.rb +176 -0
  21. data/test/multiverse/suites/mongo/mongo_connection_test.rb +40 -0
  22. data/test/multiverse/suites/mongo/mongo_instrumentation_test.rb +8 -393
  23. data/test/multiverse/suites/mongo/mongo_unsupported_version_test.rb +6 -4
  24. data/test/multiverse/suites/rails/ignore_test.rb +7 -2
  25. data/test/multiverse/suites/sequel/database.rb +24 -20
  26. data/test/multiverse/suites/sequel/sequel_instrumentation_test.rb +16 -0
  27. data/test/new_relic/agent/cross_app_monitor_test.rb +4 -2
  28. data/test/new_relic/agent/datastores/mongo/metric_generator_test.rb +27 -1
  29. data/test/new_relic/agent/datastores/mongo/metric_translator_test.rb +19 -9
  30. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +497 -493
  31. data/test/new_relic/agent/method_tracer_test.rb +23 -0
  32. data/test/new_relic/agent/transaction_sampler_test.rb +5 -16
  33. data/test/new_relic/json_wrapper_test.rb +5 -6
  34. data/test/performance/suites/trace_execution_scoped.rb +32 -0
  35. metadata +34 -26
  36. metadata.gz.sig +0 -0
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,5 +1,53 @@
1
1
  # New Relic Ruby Agent Release Notes #
2
2
 
3
+ ## v3.7.2 ##
4
+
5
+ * Mongo instrumentation improvements
6
+
7
+ Users of the 'mongo' MongoDB client gem will get more detailed instrumentation
8
+ including support for some operations that were not previously captured, and
9
+ separation of aggregate metrics for web transactions from background jobs.
10
+
11
+ An issue with ensure_index when passed a symbol or string was also fixed.
12
+ Thanks Maxime RETY for the report!
13
+
14
+ * More accurate error tracing in Rails 4
15
+
16
+ Traced errors in Rails 4 applications will now be correctly associated with
17
+ the transaction they occurred in, and custom attributes attached to the
18
+ transaction will be correctly attached to the traced error as well.
19
+
20
+ * More accurate partial-rendering metrics for Rails 4
21
+
22
+ View partials are now correctly treated as sub-components of the containing
23
+ template render in Rails 4 applications, meaning that the app server breakdown
24
+ graphs for Rails 4 transactions should be more accurate and useful.
25
+
26
+ * Improved Unicorn 4.8.0 compatibility
27
+
28
+ A rare issue that could lead to spurrious traced errors on app startup for
29
+ applications using Unicorn 4.8.0 has been fixed.
30
+
31
+ * meta_request gem compatibility
32
+
33
+ An incompatibility with the meta_request gem has been fixed.
34
+
35
+ * Typhoeus 0.6.4+ compatibility
36
+
37
+ A potential crash with Typhoeus 0.6.4+ when passing a URI object instead of a
38
+ String instance to one of Typhoeus's HTTP request methods has been fixed.
39
+
40
+ * Sequel single threaded mode fix
41
+
42
+ The agent will no longer attempt to run EXPLAIN queries for slow SQL
43
+ statements issued using the Sequel gem in single-threaded mode, since
44
+ doing so could potentially cause crashes.
45
+
46
+ * Additional functionality for add_custom_parameters
47
+
48
+ Calling add_custom_parameters adds parameters to the system codenamed
49
+ Rubicon. For more information, see http://newrelic.com/software-analytics
50
+
3
51
  ## v3.7.1 ##
4
52
 
5
53
  * MongoDB support
data/LICENSE CHANGED
@@ -112,7 +112,7 @@ the software.
112
112
 
113
113
 
114
114
  All other components of this product are
115
- Copyright (c) 2008-2013 New Relic, Inc. All rights reserved.
115
+ Copyright (c) 2008-2014 New Relic, Inc. All rights reserved.
116
116
 
117
117
  Certain inventions disclosed in this file may be claimed within
118
118
  patents owned or patent applications filed by New Relic, Inc. or third
@@ -624,12 +624,6 @@ module NewRelic
624
624
  :type => Boolean,
625
625
  :description => 'Enable or disable sequel instrumentation.'
626
626
  },
627
- :override_sql_obfuscation_adapter => {
628
- :default => '',
629
- :public => true,
630
- :type => String,
631
- :description => "Override for the sql obfuscation adapter."
632
- },
633
627
  :disable_database_instrumentation => {
634
628
  :default => false,
635
629
  :public => true,
@@ -106,13 +106,14 @@ module NewRelic
106
106
 
107
107
  def insert_response_header(request_headers, response_headers)
108
108
  unless client_cross_app_id.nil?
109
- NewRelic::Agent::Transaction.freeze_name
110
- timings = NewRelic::Agent::TransactionState.get.timings
111
- content_length = content_length_from_request(request_headers)
112
-
113
- set_response_headers(response_headers, timings, content_length)
114
- set_metrics(client_cross_app_id, timings)
109
+ unless NewRelic::Agent::TransactionState.get.transaction.nil?
110
+ NewRelic::Agent::Transaction.freeze_name
111
+ timings = NewRelic::Agent::TransactionState.get.timings
112
+ content_length = content_length_from_request(request_headers)
115
113
 
114
+ set_response_headers(response_headers, timings, content_length)
115
+ set_metrics(client_cross_app_id, timings)
116
+ end
116
117
  clear_client_cross_app_id
117
118
  end
118
119
  end
@@ -17,6 +17,14 @@ module NewRelic
17
17
  end
18
18
 
19
19
  NewRelic::Agent::Datastores::Mongo::MetricTranslator.metrics_for(name, payload, request_type)
20
+ rescue => e
21
+ NewRelic::Agent.logger.debug("Failure during Mongo metric generation", e)
22
+ []
23
+ end
24
+
25
+ def self.generate_instance_metric_for(host, port, database_name)
26
+ return unless host && port && database_name
27
+ NewRelic::Agent::Datastores::Mongo::MetricTranslator.instance_metric(host, port, database_name)
20
28
  end
21
29
  end
22
30
  end
@@ -10,16 +10,15 @@ module NewRelic
10
10
  module Mongo
11
11
  module MetricTranslator
12
12
  def self.metrics_for(name, payload, request_type = :web)
13
- payload = {} if payload.nil?
13
+ payload ||= {}
14
14
 
15
+ database = payload[:database]
15
16
  collection = payload[:collection]
16
17
 
17
18
  if collection_in_selector?(collection, payload)
18
19
  command_key = command_key_from_selector(payload)
19
-
20
- name = get_name_from_selector(command_key)
21
- collection = get_collection_from_selector(command_key, payload)
22
- log_if_unknown_command(command_key, payload)
20
+ name = get_name_from_selector(command_key, payload)
21
+ collection = get_collection_from_selector(command_key, payload)
23
22
  end
24
23
 
25
24
  if self.find_one?(name, payload)
@@ -43,22 +42,21 @@ module NewRelic
43
42
  elsif self.rename_collection?(name, payload)
44
43
  name = 'renameCollection'
45
44
  collection = collection_name_from_rename_selector(payload)
46
- elsif self.ismaster?(name, payload)
47
- name = 'ismaster'
48
- collection = collection_name_from_ismaster_selector(payload)
49
45
  end
50
46
 
51
- build_metrics(name, collection, request_type)
47
+ metrics = build_metrics(name, collection, request_type)
48
+
49
+ metrics
52
50
  end
53
51
 
54
52
  def self.build_metrics(name, collection, request_type = :web)
55
53
  default_metrics = [
56
54
  "Datastore/statement/MongoDB/#{collection}/#{name}",
57
- "Datastore/operation/MongoDB/#{name}",
58
- 'ActiveRecord/all'
55
+ "Datastore/operation/MongoDB/#{name}"
59
56
  ]
60
57
 
61
58
  if request_type == :web
59
+ default_metrics << 'ActiveRecord/all'
62
60
  default_metrics << 'Datastore/allWeb'
63
61
  else
64
62
  default_metrics << 'Datastore/allOther'
@@ -67,6 +65,10 @@ module NewRelic
67
65
  default_metrics
68
66
  end
69
67
 
68
+ def self.instance_metric(host, port, database)
69
+ "Datastore/instance/MongoDB/#{host}:#{port}/#{database}"
70
+ end
71
+
70
72
  def self.collection_in_selector?(collection, payload)
71
73
  collection == '$cmd' && payload[:selector]
72
74
  end
@@ -84,15 +86,11 @@ module NewRelic
84
86
  :deleteIndexes,
85
87
  :reIndex,
86
88
 
87
- :ismaster,
88
89
  :collstats,
89
90
  :renameCollection,
90
91
  :drop,
91
92
  ]
92
93
 
93
- UNKNOWN_COMMAND = "UnknownCommand"
94
- UNKNOWN_COLLECTION = "UnknownCollection"
95
-
96
94
  def self.command_key_from_selector(payload)
97
95
  selector = payload[:selector]
98
96
  NAMES_IN_SELECTOR.find do |check_name|
@@ -100,22 +98,23 @@ module NewRelic
100
98
  end
101
99
  end
102
100
 
103
- def self.get_name_from_selector(command_key)
104
- return UNKNOWN_COMMAND unless command_key
105
-
106
- command_key.to_sym
101
+ def self.get_name_from_selector(command_key, payload)
102
+ if command_key
103
+ command_key.to_sym
104
+ else
105
+ NewRelic::Agent.increment_metric("Supportability/Mongo/UnknownCollection")
106
+ payload[:selector].first.first unless command_key
107
+ end
107
108
  end
108
109
 
109
- def self.get_collection_from_selector(command_key, payload)
110
- return UNKNOWN_COLLECTION unless command_key
110
+ CMD_COLLECTION = "$cmd".freeze
111
111
 
112
- payload[:selector][command_key]
113
- end
114
-
115
- def self.log_if_unknown_command(command_key, payload)
116
- unless command_key
117
- NewRelic::Agent.logger.debug("Unknown Mongo command: #{Obfuscator.obfuscate_statement(payload).inspect}")
118
- NewRelic::Agent.increment_metric("Supportability/Mongo/UnknownCommand")
112
+ def self.get_collection_from_selector(command_key, payload)
113
+ if command_key
114
+ payload[:selector][command_key]
115
+ else
116
+ NewRelic::Agent.increment_metric("Supportability/Mongo/UnknownCollection")
117
+ CMD_COLLECTION
119
118
  end
120
119
  end
121
120
 
@@ -155,10 +154,6 @@ module NewRelic
155
154
  name == :renameCollection
156
155
  end
157
156
 
158
- def self.ismaster?(name, payload)
159
- name == :ismaster
160
- end
161
-
162
157
  def self.collection_name_from_index(payload)
163
158
  if payload[:documents] && payload[:documents].first[:ns]
164
159
  payload[:documents].first[:ns].split('.').last
@@ -177,10 +172,6 @@ module NewRelic
177
172
  parts.join('.')
178
173
  end
179
174
 
180
- def self.collection_name_from_ismaster_selector(payload)
181
- payload[:selector][:ismaster]
182
- end
183
-
184
175
  end
185
176
 
186
177
  end
@@ -418,22 +418,32 @@ module NewRelic
418
418
  def perform_action_with_newrelic_profile(args)
419
419
  txn = _start_transaction(block_given? ? args : [])
420
420
  val = nil
421
- NewRelic::Agent.trace_execution_scoped txn.metric_name do
422
- NewRelic::Agent.disable_all_tracing do
423
- # turn on profiling
424
- profile = RubyProf.profile do
425
- if block_given?
426
- val = yield
427
- else
428
- val = perform_action_without_newrelic_trace(*args)
429
- end
421
+ options = { :metric => true }
422
+
423
+ _, expected_scope = NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_header(options, txn.start_time.to_f)
424
+
425
+ NewRelic::Agent.disable_all_tracing do
426
+ # turn on profiling
427
+ profile = RubyProf.profile do
428
+ if block_given?
429
+ val = yield
430
+ else
431
+ val = perform_action_without_newrelic_trace(*args)
430
432
  end
431
- NewRelic::Agent.instance.transaction_sampler.notice_profile profile
432
433
  end
434
+ NewRelic::Agent.instance.transaction_sampler.notice_profile profile
433
435
  end
436
+
434
437
  return val
435
438
  ensure
436
- txn.pop
439
+ end_time = Time.now
440
+ txn.freeze_name
441
+ metric_names = Array(recorded_metrics(txn))
442
+ txn_name = metric_names.shift
443
+
444
+ NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_footer(txn.start_time.to_f, txn_name, metric_names, expected_scope, options, end_time.to_f)
445
+
446
+ txn = Transaction.stop(txn_name, end_time)
437
447
  end
438
448
 
439
449
  # Write a transaction onto a thread local if there isn't already one there.
@@ -28,37 +28,66 @@ DependencyDetection.defer do
28
28
  end
29
29
 
30
30
  def install_mongo_instrumentation
31
+ setup_logging_for_instrumentation
31
32
  instrument_mongo_logging
32
33
  instrument_save
33
34
  instrument_ensure_index
34
35
  end
35
36
 
36
- def instrument_mongo_logging
37
+ def setup_logging_for_instrumentation
37
38
  ::Mongo::Logging.class_eval do
38
39
  include NewRelic::Agent::MethodTracer
39
40
  require 'new_relic/agent/datastores/mongo/metric_generator'
40
41
  require 'new_relic/agent/datastores/mongo/statement_formatter'
41
42
 
42
- def instrument_with_new_relic_trace(name, payload = {}, &block)
43
- metrics = NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_metrics_for(name, payload)
44
-
45
- trace_execution_scoped(metrics) do
46
- t0 = Time.now
47
- result = instrument_without_new_relic_trace(name, payload, &block)
48
-
49
- payload[:operation] = name
50
- statement = NewRelic::Agent::Datastores::Mongo::StatementFormatter.format(payload)
51
- if statement
52
- NewRelic::Agent.instance.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
43
+ def new_relic_instance_metric_builder
44
+ Proc.new do
45
+ if @pool
46
+ host, port = @pool.host, @pool.port
47
+ elsif @connection && (primary = @connection.primary)
48
+ host, port = primary[0], primary[1]
53
49
  end
54
50
 
55
- result
51
+ database_name = @db.name if @db
52
+ NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_instance_metric_for(host, port, database_name)
53
+ end
54
+ end
55
+
56
+ # It's key that this method eats all exceptions, as it rests between the
57
+ # Mongo operation the user called and us returning them the data. Be safe!
58
+ def new_relic_notice_statement(t0, payload, operation)
59
+ payload[:operation] = operation
60
+ statement = NewRelic::Agent::Datastores::Mongo::StatementFormatter.format(payload)
61
+ if statement
62
+ NewRelic::Agent.instance.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
56
63
  end
64
+ rescue => e
65
+ NewRelic::Agent.logger.debug("Exception during Mongo statement gathering", e)
66
+ end
67
+
68
+ def new_relic_generate_metrics(operation, payload = nil)
69
+ payload ||= { :collection => self.name, :database => self.db.name }
70
+ metrics = NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_metrics_for(operation, payload)
57
71
  end
58
72
 
59
73
  ::Mongo::Collection.class_eval { include Mongo::Logging; }
60
74
  ::Mongo::Connection.class_eval { include Mongo::Logging; }
61
75
  ::Mongo::Cursor.class_eval { include Mongo::Logging; }
76
+ end
77
+ end
78
+
79
+ def instrument_mongo_logging
80
+ ::Mongo::Logging.class_eval do
81
+ def instrument_with_new_relic_trace(name, payload = {}, &block)
82
+ metrics = new_relic_generate_metrics(name, payload)
83
+
84
+ trace_execution_scoped(metrics, :additional_metrics_callback => new_relic_instance_metric_builder) do
85
+ t0 = Time.now
86
+ result = instrument_without_new_relic_trace(name, payload, &block)
87
+ new_relic_notice_statement(t0, payload, name)
88
+ result
89
+ end
90
+ end
62
91
 
63
92
  alias_method :instrument_without_new_relic_trace, :instrument
64
93
  alias_method :instrument, :instrument_with_new_relic_trace
@@ -67,14 +96,9 @@ DependencyDetection.defer do
67
96
 
68
97
  def instrument_save
69
98
  ::Mongo::Collection.class_eval do
70
- include NewRelic::Agent::MethodTracer
71
- require 'new_relic/agent/datastores/mongo/metric_generator'
72
- require 'new_relic/agent/datastores/mongo/statement_formatter'
73
-
74
99
  def save_with_new_relic_trace(doc, opts = {}, &block)
75
- metrics = NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_metrics_for(:save, { :collection => self.name })
76
-
77
- trace_execution_scoped(metrics) do
100
+ metrics = new_relic_generate_metrics(:save)
101
+ trace_execution_scoped(metrics, :additional_metrics_callback => new_relic_instance_metric_builder) do
78
102
  t0 = Time.now
79
103
 
80
104
  transaction_state = NewRelic::Agent::TransactionState.get
@@ -86,12 +110,7 @@ DependencyDetection.defer do
86
110
  transaction_state.pop_traced
87
111
  end
88
112
 
89
- doc[:operation] = :save
90
- statement = NewRelic::Agent::Datastores::Mongo::StatementFormatter.format(doc)
91
- if statement
92
- NewRelic::Agent.instance.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
93
- end
94
-
113
+ new_relic_notice_statement(t0, doc, :save)
95
114
  result
96
115
  end
97
116
  end
@@ -103,14 +122,9 @@ DependencyDetection.defer do
103
122
 
104
123
  def instrument_ensure_index
105
124
  ::Mongo::Collection.class_eval do
106
- include NewRelic::Agent::MethodTracer
107
- require 'new_relic/agent/datastores/mongo/metric_generator'
108
- require 'new_relic/agent/datastores/mongo/statement_formatter'
109
-
110
125
  def ensure_index_with_new_relic_trace(spec, opts = {}, &block)
111
- metrics = NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_metrics_for(:ensureIndex, { :collection => self.name })
112
-
113
- trace_execution_scoped(metrics) do
126
+ metrics = new_relic_generate_metrics(:ensureIndex)
127
+ trace_execution_scoped(metrics, :additional_metrics_callback => new_relic_instance_metric_builder) do
114
128
  t0 = Time.now
115
129
 
116
130
  transaction_state = NewRelic::Agent::TransactionState.get
@@ -122,14 +136,16 @@ DependencyDetection.defer do
122
136
  transaction_state.pop_traced
123
137
  end
124
138
 
125
- spec = spec.is_a?(Array) ? Hash[spec] : spec.dup
126
- spec[:operation] = :ensureIndex
127
-
128
- statement = NewRelic::Agent::Datastores::Mongo::StatementFormatter.format(spec)
129
- if statement
130
- NewRelic::Agent.instance.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
131
- end
139
+ spec = case spec
140
+ when Array
141
+ Hash[spec]
142
+ when String, Symbol
143
+ { spec => 1 }
144
+ else
145
+ spec.dup
146
+ end
132
147
 
148
+ new_relic_notice_statement(t0, spec, :ensureIndex)
133
149
  result
134
150
  end
135
151
  end
@@ -267,13 +267,20 @@ module NewRelic
267
267
  #
268
268
  def trace_execution_scoped(metric_names, options={})
269
269
  return yield if trace_disabled?(options)
270
- set_if_nil(options, :metric)
271
- set_if_nil(options, :deduct_call_time_from_parent)
270
+
272
271
  metric_names = Array(metric_names)
273
272
  first_name = metric_names.shift
273
+ return yield if first_name.nil?
274
+
275
+ set_if_nil(options, :metric)
276
+ set_if_nil(options, :deduct_call_time_from_parent)
277
+ additional_metrics_callback = options[:additional_metrics_callback]
274
278
  start_time, expected_scope = trace_execution_scoped_header(options)
279
+
275
280
  begin
276
- yield
281
+ result = yield
282
+ metric_names += Array(additional_metrics_callback.call) if additional_metrics_callback
283
+ result
277
284
  ensure
278
285
  trace_execution_scoped_footer(start_time, first_name, metric_names, expected_scope, options)
279
286
  end