newrelic_rpm 3.1.2 → 3.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (69) hide show
  1. data/CHANGELOG +9 -0
  2. data/lib/new_relic/agent.rb +12 -3
  3. data/lib/new_relic/agent/agent.rb +99 -97
  4. data/lib/new_relic/agent/database.rb +203 -0
  5. data/lib/new_relic/agent/instrumentation/active_merchant.rb +2 -0
  6. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +2 -0
  7. data/lib/new_relic/agent/instrumentation/authlogic.rb +2 -0
  8. data/lib/new_relic/agent/instrumentation/data_mapper.rb +2 -0
  9. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +2 -0
  10. data/lib/new_relic/agent/instrumentation/memcache.rb +23 -13
  11. data/lib/new_relic/agent/instrumentation/merb/controller.rb +2 -1
  12. data/lib/new_relic/agent/instrumentation/merb/errors.rb +2 -0
  13. data/lib/new_relic/agent/instrumentation/metric_frame.rb +7 -1
  14. data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +1 -0
  15. data/lib/new_relic/agent/instrumentation/net.rb +2 -0
  16. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +2 -0
  17. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +63 -36
  18. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +2 -0
  19. data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +5 -2
  20. data/lib/new_relic/agent/instrumentation/rails/errors.rb +4 -2
  21. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +56 -2
  22. data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +5 -2
  23. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +3 -1
  24. data/lib/new_relic/agent/instrumentation/sunspot.rb +2 -0
  25. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +2 -1
  26. data/lib/new_relic/agent/shim_agent.rb +1 -0
  27. data/lib/new_relic/agent/sql_sampler.rb +230 -0
  28. data/lib/new_relic/agent/stats_engine/transactions.rb +10 -0
  29. data/lib/new_relic/agent/transaction_sampler.rb +11 -6
  30. data/lib/new_relic/collection_helper.rb +7 -4
  31. data/lib/new_relic/commands/deployments.rb +1 -1
  32. data/lib/new_relic/commands/install.rb +2 -13
  33. data/lib/new_relic/control/class_methods.rb +4 -3
  34. data/lib/new_relic/control/configuration.rb +21 -0
  35. data/lib/new_relic/control/frameworks/rails.rb +1 -1
  36. data/lib/new_relic/control/logging_methods.rb +17 -6
  37. data/lib/new_relic/delayed_job_injection.rb +1 -1
  38. data/lib/new_relic/local_environment.rb +8 -14
  39. data/lib/new_relic/rack/developer_mode.rb +1 -0
  40. data/lib/new_relic/stats.rb +1 -0
  41. data/lib/new_relic/transaction_sample.rb +5 -60
  42. data/lib/new_relic/transaction_sample/segment.rb +7 -82
  43. data/lib/new_relic/version.rb +3 -3
  44. data/newrelic_rpm.gemspec +8 -3
  45. data/test/new_relic/agent/agent/connect_test.rb +95 -0
  46. data/test/new_relic/agent/agent/start_test.rb +0 -85
  47. data/test/new_relic/agent/agent/start_worker_thread_test.rb +1 -0
  48. data/test/new_relic/agent/agent_test.rb +0 -73
  49. data/test/new_relic/agent/browser_monitoring_test.rb +1 -1
  50. data/test/new_relic/agent/database_test.rb +160 -0
  51. data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +3 -0
  52. data/test/new_relic/agent/memcache_instrumentation_test.rb +14 -15
  53. data/test/new_relic/agent/sql_sampler_test.rb +135 -0
  54. data/test/new_relic/agent/transaction_sampler_test.rb +12 -3
  55. data/test/new_relic/collection_helper_test.rb +4 -4
  56. data/test/new_relic/control/configuration_test.rb +31 -0
  57. data/test/new_relic/control/logging_methods_test.rb +20 -4
  58. data/test/new_relic/delayed_job_injection_test.rb +1 -1
  59. data/test/new_relic/rack/developer_mode_helper_test.rb +141 -0
  60. data/test/new_relic/stats_test.rb +3 -3
  61. data/test/new_relic/transaction_sample/segment_test.rb +4 -92
  62. data/test/new_relic/transaction_sample_test.rb +1 -1
  63. data/test/test_helper.rb +1 -1
  64. data/ui/helpers/developer_mode_helper.rb +14 -8
  65. data/ui/helpers/google_pie_chart.rb +0 -1
  66. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection.rb +5 -0
  67. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/mem_cache.rb +11 -11
  68. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/view.rb +4 -0
  69. metadata +15 -10
@@ -67,6 +67,7 @@ module NewRelic
67
67
  log_without_newrelic_instrumentation(*args, &block)
68
68
  ensure
69
69
  NewRelic::Agent.instance.transaction_sampler.notice_sql(sql, supported_config, (Time.now - t0).to_f)
70
+ NewRelic::Agent.instance.sql_sampler.notice_sql(sql, metric, supported_config, (Time.now - t0).to_f)
70
71
  end
71
72
  end
72
73
  end
@@ -78,12 +79,14 @@ module NewRelic
78
79
  end
79
80
 
80
81
  DependencyDetection.defer do
82
+ @name = :rails3_active_record
83
+
81
84
  depends_on do
82
85
  defined?(ActiveRecord) && defined?(ActiveRecord::Base)
83
86
  end
84
87
 
85
88
  depends_on do
86
- defined?(Rails) && Rails::VERSION::MAJOR.to_i == 3
89
+ defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 3
87
90
  end
88
91
 
89
92
  depends_on do
@@ -95,7 +98,7 @@ DependencyDetection.defer do
95
98
  end
96
99
 
97
100
  executes do
98
- NewRelic::Agent.logger.debug 'Installing Rails3 ActiveRecord instrumentation'
101
+ NewRelic::Agent.logger.debug 'Installing Rails 3 ActiveRecord instrumentation'
99
102
  end
100
103
 
101
104
  executes do
@@ -15,8 +15,10 @@ module NewRelic
15
15
  end
16
16
 
17
17
  DependencyDetection.defer do
18
+ @name = :rails3_error
19
+
18
20
  depends_on do
19
- defined?(Rails) && Rails.respond_to?(:version) && Rails.version.to_i == 3
21
+ defined?(::Rails) && ::Rails.respond_to?(:version) && ::Rails.version.to_i == 3
20
22
  end
21
23
 
22
24
  depends_on do
@@ -1,4 +1,6 @@
1
1
  DependencyDetection.defer do
2
+ @name = :sunspot
3
+
2
4
  depends_on do
3
5
  defined?(::Sunspot)
4
6
  end
@@ -1,4 +1,6 @@
1
1
  DependencyDetection.defer do
2
+ @name = :unicorn
3
+
2
4
  depends_on do
3
5
  defined?(::Unicorn) && defined?(::Unicorn::HttpServer)
4
6
  end
@@ -9,7 +11,6 @@ DependencyDetection.defer do
9
11
 
10
12
  executes do
11
13
  Unicorn::HttpServer.class_eval do
12
- NewRelic::Agent.logger.debug "Installing Unicorn worker hook."
13
14
  old_worker_loop = instance_method(:worker_loop)
14
15
  define_method(:worker_loop) do | worker |
15
16
  NewRelic::Agent.after_fork(:force_reconnect => true)
@@ -12,6 +12,7 @@ module NewRelic
12
12
  @stats_engine.extend NewRelic::Agent::StatsEngine::Shim
13
13
  @stats_engine.extend NewRelic::Agent::StatsEngine::Transactions::Shim
14
14
  @transaction_sampler.extend NewRelic::Agent::TransactionSampler::Shim
15
+ @sql_sampler.extend NewRelic::Agent::SqlSampler::Shim
15
16
  @error_collector.extend NewRelic::Agent::ErrorCollector::Shim
16
17
  end
17
18
  def after_fork *args; end
@@ -0,0 +1,230 @@
1
+ require 'new_relic/agent'
2
+ require 'new_relic/control'
3
+ module NewRelic
4
+ module Agent
5
+
6
+ class SqlSampler
7
+
8
+ # Module defining methods stubbed out when the agent is disabled
9
+ module Shim #:nodoc:
10
+ def notice_scope_empty(*args); end
11
+ def notice_first_scope_push(*args); end
12
+ def notice_transaction(*args); end
13
+ end
14
+
15
+ attr_reader :disabled
16
+
17
+ # this is for unit tests only
18
+ attr_reader :sql_traces
19
+
20
+ def initialize
21
+ config = NewRelic::Control.instance
22
+ sampler_config = config.fetch('transaction_tracer', {})
23
+ @explain_threshold = sampler_config.fetch('explain_threshold', 0.5).to_f
24
+ # @stack_trace_threshold = sampler_config.fetch('stack_trace_threshold', 0.500).to_f
25
+ @sql_traces = {}
26
+ clear_transaction_data
27
+
28
+ # This lock is used to synchronize access to the @last_sample
29
+ # and related variables. It can become necessary on JRuby or
30
+ # any 'honest-to-god'-multithreaded system
31
+ @samples_lock = Mutex.new
32
+ end
33
+
34
+ # Enable the sql sampler - this also registers it with
35
+ # the statistics engine.
36
+ def enable
37
+ @disabled = false
38
+ NewRelic::Agent.instance.stats_engine.sql_sampler = self
39
+ end
40
+
41
+ # Disable the sql sampler - this also deregisters it
42
+ # with the statistics engine.
43
+ def disable
44
+ @disabled = true
45
+ NewRelic::Agent.instance.stats_engine.remove_sql_sampler(self)
46
+ end
47
+
48
+ def notice_transaction(path, uri=nil, params={})
49
+ transaction_data.set_transaction_info(path, uri, params) if !disabled && transaction_data
50
+ end
51
+
52
+ def notice_first_scope_push(time)
53
+ create_transaction_data
54
+ end
55
+
56
+ def create_transaction_data
57
+ Thread.current[:new_relic_sql_data] = TransactionSqlData.new
58
+ end
59
+
60
+ def transaction_data
61
+ Thread.current[:new_relic_sql_data]
62
+ end
63
+
64
+ def clear_transaction_data
65
+ Thread.current[:new_relic_sql_data] = nil
66
+ end
67
+
68
+ # This is called when we are done with the transaction.
69
+ def notice_scope_empty(time=Time.now)
70
+ data = transaction_data
71
+ clear_transaction_data
72
+
73
+ if data.sql_data.count > 0
74
+ @samples_lock.synchronize do
75
+ NewRelic::Agent.instance.log.debug "Harvesting #{data.sql_data.count} slow transaction sql statement(s)"
76
+ #FIXME get tx name and uri
77
+ harvest_slow_sql data
78
+ end
79
+ end
80
+ end
81
+
82
+ # this should always be called under the @samples_lock
83
+ def harvest_slow_sql(transaction_sql_data)
84
+ transaction_sql_data.sql_data.each do |sql_item|
85
+ obfuscated_sql = sql_item.normalize
86
+ sql_trace = @sql_traces[obfuscated_sql]
87
+ if sql_trace
88
+ sql_trace.aggregate(sql_item, transaction_sql_data.path,
89
+ transaction_sql_data.uri)
90
+ else
91
+ @sql_traces[obfuscated_sql] = SqlTrace.new(obfuscated_sql,
92
+ sql_item, transaction_sql_data.path, transaction_sql_data.uri)
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ def notice_sql(sql, metric_name, config, duration)
99
+ return unless transaction_data
100
+ if NewRelic::Agent.is_sql_recorded?
101
+ if duration > @explain_threshold
102
+ backtrace = caller.join("\n")
103
+ transaction_data.sql_data << SlowSql.new(sql, metric_name, config,
104
+ duration, backtrace)
105
+ end
106
+ end
107
+ end
108
+
109
+ def merge(sql_traces)
110
+ @samples_lock.synchronize do
111
+ #FIXME we need to merge the sql_traces array back into the @sql_traces hash
112
+ # @sql_traces.merge! sql_traces
113
+ end
114
+ end
115
+
116
+ def harvest
117
+ return [] if disabled
118
+ result = []
119
+ @samples_lock.synchronize do
120
+ result = @sql_traces.values
121
+ @sql_traces = {}
122
+ end
123
+ slowest = result.sort{|a,b| b.max_call_time <=> a.max_call_time}[0,10]
124
+ slowest.each {|trace| trace.prepare_to_send }
125
+ slowest
126
+ end
127
+
128
+ # reset samples without rebooting the web server
129
+ def reset!
130
+ end
131
+ end
132
+
133
+ class TransactionSqlData
134
+ attr_reader :path
135
+ attr_reader :uri
136
+ attr_reader :params
137
+ attr_reader :sql_data
138
+
139
+ def initialize
140
+ @sql_data = []
141
+ end
142
+
143
+ def set_transaction_info(path, uri, params)
144
+ @path = path
145
+ @uri = uri
146
+ @params = params
147
+ end
148
+ end
149
+
150
+ class SlowSql
151
+ attr_reader :sql
152
+ attr_reader :metric_name
153
+ attr_reader :duration
154
+ attr_reader :backtrace
155
+
156
+ def initialize(sql, metric_name, config, duration, backtrace = nil)
157
+ @sql = sql
158
+ @metric_name = metric_name
159
+ @config = config
160
+ @duration = duration
161
+ @backtrace = backtrace
162
+ end
163
+
164
+ def obfuscate
165
+ NewRelic::Agent::Database.obfuscate_sql(@sql)
166
+ end
167
+
168
+ def normalize
169
+ NewRelic::Agent::Database::Obfuscator.instance \
170
+ .default_sql_obfuscator(@sql).gsub(/\?\s*\,\s*/, '')
171
+ end
172
+
173
+ def explain
174
+ NewRelic::Agent::Database.explain_sql(@sql, @config)
175
+ end
176
+ end
177
+
178
+ class SqlTrace < MethodTraceStats
179
+ attr_reader :path
180
+ attr_reader :url
181
+ attr_reader :sql_id
182
+ attr_reader :sql
183
+ attr_reader :database_metric_name
184
+ attr_reader :params
185
+
186
+ def initialize(obfuscated_sql, slow_sql, path, uri)
187
+ super()
188
+ @params = {} #FIXME
189
+ @sql_id = obfuscated_sql.hash
190
+ set_primary slow_sql, path, uri
191
+ record_data_point slow_sql.duration
192
+ end
193
+
194
+ def set_primary(slow_sql, path, uri)
195
+ @slow_sql = slow_sql
196
+ @sql = slow_sql.sql
197
+ @database_metric_name = slow_sql.metric_name
198
+ @path = path
199
+ @url = uri
200
+ # FIXME
201
+ @params[:backtrace] = slow_sql.backtrace if slow_sql.backtrace
202
+ end
203
+
204
+ def aggregate(slow_sql, path, uri)
205
+ if slow_sql.duration > max_call_time
206
+ set_primary slow_sql, path, uri
207
+ end
208
+
209
+ record_data_point slow_sql.duration
210
+ end
211
+
212
+ def prepare_to_send
213
+ begin
214
+ params[:explain_plan] = @slow_sql.explain
215
+ ensure
216
+ NewRelic::Agent::Database.close_connections
217
+ end
218
+ @sql = @slow_sql.obfuscate if need_to_obfuscate?
219
+ end
220
+
221
+ def need_to_obfuscate?
222
+ NewRelic::Control.instance['transaction_tracer']['record_sql'] == 'obfuscated'
223
+ end
224
+
225
+ def to_json(*a)
226
+ [@path, @url, @sql_id, @sql, @database_metric_name, @call_count, @total_call_time, @min_call_time, @max_call_time, @params].to_json(*a)
227
+ end
228
+ end
229
+ end
230
+ end
@@ -25,6 +25,7 @@ module Agent
25
25
  def end_transaction; end
26
26
  def push_scope(*args); end
27
27
  def transaction_sampler=(*args); end
28
+ def sql_sampler=(*args); end
28
29
  def scope_name=(*args); end
29
30
  def scope_name; end
30
31
  def pop_scope(*args); end
@@ -42,6 +43,15 @@ module Agent
42
43
  @transaction_sampler = nil
43
44
  end
44
45
 
46
+ def sql_sampler= sampler
47
+ fail "Can't add a scope listener midflight in a transaction" if scope_stack.any?
48
+ @sql_sampler = sampler
49
+ end
50
+
51
+ def remove_sql_sampler(l)
52
+ @sql_sampler = nil
53
+ end
54
+
45
55
  # Pushes a scope onto the transaction stack - this generates a
46
56
  # TransactionSample::Segment at the end of transaction execution
47
57
  def push_scope(metric, time = Time.now.to_f, deduct_call_time_from_parent = true)
@@ -32,7 +32,15 @@ module NewRelic
32
32
  @harvest_count = 0
33
33
  @random_sample = nil
34
34
  @sampling_rate = 10
35
+ configure!
35
36
 
37
+ # This lock is used to synchronize access to the @last_sample
38
+ # and related variables. It can become necessary on JRuby or
39
+ # any 'honest-to-god'-multithreaded system
40
+ @samples_lock = Mutex.new
41
+ end
42
+
43
+ def configure!
36
44
  # @segment_limit and @stack_trace_threshold come from the
37
45
  # configuration file, with built-in defaults that should
38
46
  # suffice for most customers
@@ -40,11 +48,7 @@ module NewRelic
40
48
  sampler_config = config.fetch('transaction_tracer', {})
41
49
  @segment_limit = sampler_config.fetch('limit_segments', 4000)
42
50
  @stack_trace_threshold = sampler_config.fetch('stack_trace_threshold', 0.500).to_f
43
-
44
- # This lock is used to synchronize access to the @last_sample
45
- # and related variables. It can become necessary on JRuby or
46
- # any 'honest-to-god'-multithreaded system
47
- @samples_lock = Mutex.new
51
+ @explain_threshold = sampler_config.fetch('explain_threshold', 0.5).to_f
48
52
  end
49
53
 
50
54
  # Returns the current sample id, delegated from `builder`
@@ -236,7 +240,8 @@ module NewRelic
236
240
  return unless builder
237
241
  segment = builder.current_segment
238
242
  if segment
239
- segment[key] = truncate_message(append_new_message(segment[key], message))
243
+ segment[key] = truncate_message(append_new_message(segment[key],
244
+ message))
240
245
  segment[config_key] = config if config_key
241
246
  append_backtrace(segment, duration)
242
247
  end
@@ -2,8 +2,8 @@ require 'new_relic/control'
2
2
 
3
3
  module NewRelic
4
4
  module CollectionHelper
5
- DEFAULT_TRUNCATION_SIZE=256
6
- DEFAULT_ARRAY_TRUNCATION_SIZE=1024
5
+ DEFAULT_TRUNCATION_SIZE=16 * 1024
6
+ DEFAULT_ARRAY_TRUNCATION_SIZE=128
7
7
  # Transform parameter hash into a hash whose values are strictly
8
8
  # strings
9
9
  def normalize_params(params)
@@ -17,7 +17,7 @@ module NewRelic
17
17
  when Hash
18
18
  new_params = {}
19
19
  params.each do | key, value |
20
- new_params[truncate(normalize_params(key),32)] = normalize_params(value)
20
+ new_params[truncate(normalize_params(key),64)] = normalize_params(value)
21
21
  end
22
22
  new_params
23
23
  when Array
@@ -35,7 +35,10 @@ module NewRelic
35
35
  # this is for 1.9.1, where strings no longer have Enumerable
36
36
  backtrace = backtrace.split("\n") if String === backtrace
37
37
  backtrace = backtrace.map &:to_s
38
- backtrace = backtrace.reject {|line| line.include?(NewRelic::Control.newrelic_root) }
38
+ backtrace = backtrace.reject do |line|
39
+ line.include?(NewRelic::Control.newrelic_root) or
40
+ line =~ /^newrelic_rpm\s/
41
+ end
39
42
  # rename methods back to their original state
40
43
  backtrace = backtrace.collect {|line| line.gsub(/_without_(newrelic|trace)/, "")}
41
44
  end
@@ -17,7 +17,7 @@ class NewRelic::Command::Deployments < NewRelic::Command
17
17
  # Initialize the deployment uploader with command line args.
18
18
  # Use -h to see options.
19
19
  # When command_line_args is a hash, we are invoking directly and
20
- # it's treated as an options with optional sttring values for
20
+ # it's treated as an options with optional string values for
21
21
  # :user, :description, :appname, :revision, :environment,
22
22
  # and :changes.
23
23
  #
@@ -52,21 +52,10 @@ at www.newrelic.com, and replace the newrelic.yml file with the one
52
52
  you receive upon registration.
53
53
  EOF
54
54
  puts <<-EOF unless quiet
55
-
55
+
56
56
  E-mail support@newrelic.com with any problems or questions.
57
-
58
- EOF
59
- puts <<-EOF
60
-
61
- Installing the plugin from Rubyforge is deprecated. This repository will not be updated after January 2011
62
-
63
- Please use one of the following options:
64
-
65
- Gems: gem install newrelic_rpm
66
-
67
- Github! git clone git://github.com/newrelic/rpm.git vendor/plugins/newrelic_rpm
68
-
69
57
  EOF
58
+
70
59
  end
71
60
 
72
61
  def content
@@ -3,9 +3,10 @@ module NewRelic
3
3
  # class-level methods for lazy creation of NewRelic::Control and
4
4
  # NewRelic::LocalEnvironment instances.
5
5
  module ClassMethods
6
- # Access the Control singleton, lazy initialized
7
- def instance
8
- @instance ||= new_instance
6
+ # Access the Control singleton, lazy initialized. Default will instantiate a new
7
+ # instance or pass false to defer
8
+ def instance(create=true)
9
+ @instance ||= create && new_instance
9
10
  end
10
11
 
11
12
  # Access the LocalEnvironment singleton, lazy initialized