newrelic_rpm 3.6.1.88 → 3.6.2.90.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data/CHANGELOG +20 -0
  2. data/Gemfile +1 -0
  3. data/lib/new_relic/agent/autostart.rb +4 -3
  4. data/lib/new_relic/agent/database.rb +14 -17
  5. data/lib/new_relic/agent/instrumentation/active_record.rb +15 -2
  6. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -1
  7. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +14 -2
  8. data/lib/new_relic/agent/instrumentation/net.rb +11 -5
  9. data/lib/new_relic/agent/instrumentation/resque.rb +5 -3
  10. data/lib/new_relic/agent/instrumentation/sequel.rb +40 -0
  11. data/lib/new_relic/agent/sql_sampler.rb +14 -4
  12. data/lib/new_relic/agent/transaction_sampler.rb +13 -10
  13. data/lib/new_relic/build.rb +2 -2
  14. data/lib/new_relic/transaction_sample/segment.rb +12 -5
  15. data/lib/new_relic/version.rb +1 -1
  16. data/lib/sequel/extensions/newrelic_instrumentation.rb +103 -0
  17. data/lib/sequel/plugins/newrelic_instrumentation.rb +83 -0
  18. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +1 -1
  19. data/test/multiverse/suites/resque/instrumentation_test.rb +2 -0
  20. data/test/new_relic/agent/database_test.rb +15 -12
  21. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +13 -3
  22. data/test/new_relic/agent/instrumentation/sequel_test.rb +285 -0
  23. data/test/new_relic/agent/sql_sampler_test.rb +27 -4
  24. data/test/new_relic/agent/transaction_sampler_test.rb +14 -14
  25. data/test/new_relic/transaction_sample/segment_test.rb +24 -2
  26. data/test/new_relic/transaction_sample_test.rb +1 -1
  27. data/test/test_helper.rb +19 -5
  28. data.tar.gz.sig +0 -0
  29. metadata +17 -26
  30. metadata.gz.sig +0 -0
data/CHANGELOG CHANGED
@@ -1,6 +1,26 @@
1
1
 
2
2
  # New Relic Ruby Agent Release Notes #
3
3
 
4
+ ## v3.6.2 ##
5
+
6
+ * Sequel support
7
+
8
+ The Ruby agent now supports Sequel, a database toolkit for Ruby. This
9
+ includes capturing SQL calls and model operations in transaction traces, and
10
+ recording slow SQL calls. See https://newrelic.com/docs/ruby/sequel-instrumentation
11
+ for full details.
12
+
13
+ * Fix for over-counted Net::HTTP calls
14
+
15
+ Under some circumstances, calls into Net::HTTP were being counted twice in
16
+ metrics and transaction traces. This has been fixed.
17
+
18
+ * Missing traced errors for Resque applications
19
+
20
+ Traced errors weren't displaying for some Resque workers, although the errors
21
+ were factored into the overall count graphs. This has been fixed, and traced
22
+ errors should be available again after upgrading the agent.
23
+
4
24
  ## v3.6.1 ##
5
25
 
6
26
  * Full URIs for HTTP requests are recorded in transaction traces
data/Gemfile CHANGED
@@ -12,4 +12,5 @@ group :development do
12
12
  gem 'sqlite3', :platform => 'mri'
13
13
  gem 'activerecord-jdbcsqlite3-adapter', :platform => 'jruby'
14
14
  gem 'jruby-openssl', :platform => 'jruby'
15
+ gem 'sequel', '~> 3.46.0'
15
16
  end
@@ -19,9 +19,10 @@ module NewRelic
19
19
  extend self
20
20
 
21
21
 
22
- # The constants and execuatables (i.e. $0) used can be configured with
23
- # via the config keys 'autostart.blacklisted_constants' and
24
- # 'autostart.blacklisted_executables'
22
+ # The constants, execuatables (i.e. $0) and rake tasks used can be
23
+ # configured with the config keys 'autostart.blacklisted_constants',
24
+ # 'autostart.blacklisted_executables' and
25
+ # 'autostart.blacklisted_rake_tasks'
25
26
  def agent_should_start?
26
27
  !::NewRelic::Agent.config['autostart.blacklisted_constants'] \
27
28
  .split(/\s*,\s*/).any?{ |name| constant_is_defined?(name) } &&
@@ -46,8 +46,8 @@ module NewRelic
46
46
  end
47
47
  end
48
48
 
49
- def get_connection(config)
50
- ConnectionManager.instance.get_connection(config)
49
+ def get_connection(config, &connector)
50
+ ConnectionManager.instance.get_connection(config, &connector)
51
51
  end
52
52
 
53
53
  def close_connections
@@ -63,14 +63,14 @@ module NewRelic
63
63
  # exceeds a threshold (e.g. 500ms) and only within the slowest
64
64
  # transaction in a report period, selected for shipment to New
65
65
  # Relic
66
- def explain_sql(sql, connection_config)
66
+ def explain_sql(sql, connection_config, &explainer)
67
67
  return nil unless sql && connection_config
68
68
  statement = sql.split(";\n")[0] # only explain the first
69
- explain_plan = explain_statement(statement, connection_config)
69
+ explain_plan = explain_statement(statement, connection_config, &explainer)
70
70
  return explain_plan || []
71
71
  end
72
72
 
73
- def explain_statement(statement, config)
73
+ def explain_statement(statement, config, &explainer)
74
74
  return unless is_select?(statement)
75
75
 
76
76
  if statement[-3,3] == '...'
@@ -84,14 +84,10 @@ module NewRelic
84
84
  end
85
85
 
86
86
  handle_exception_in_explain do
87
- connection = get_connection(config)
88
- plan = nil
89
- if connection
90
- start = Time.now
91
- plan = process_resultset(connection.execute("EXPLAIN #{statement}"))
92
- ::NewRelic::Agent.record_metric("Supportability/Database/execute_explain_plan", Time.now - start)
93
- end
94
- return plan
87
+ start = Time.now
88
+ plan = explainer.call(config, statement)
89
+ ::NewRelic::Agent.record_metric("Supportability/Database/execute_explain_plan", Time.now - start)
90
+ return process_resultset(plan) if plan
95
91
  end
96
92
  end
97
93
 
@@ -130,8 +126,10 @@ module NewRelic
130
126
  begin
131
127
  # guarantees no throw from explain_sql
132
128
  ::NewRelic::Agent.logger.error("Error getting query plan:", e)
129
+ nil
133
130
  rescue
134
131
  # double exception. throw up your hands
132
+ nil
135
133
  end
136
134
  end
137
135
 
@@ -153,7 +151,7 @@ module NewRelic
153
151
  # configuration - these are stored or reopened as needed, and if
154
152
  # we cannot get one, we ignore it and move on without explaining
155
153
  # the sql
156
- def get_connection(config)
154
+ def get_connection(config, &connector)
157
155
  @connections ||= {}
158
156
 
159
157
  connection = @connections[config]
@@ -161,8 +159,7 @@ module NewRelic
161
159
  return connection if connection
162
160
 
163
161
  begin
164
- connection = ActiveRecord::Base.send("#{config[:adapter]}_connection", config)
165
- @connections[config] = connection
162
+ @connections[config] = connector.call(config)
166
163
  rescue => e
167
164
  ::NewRelic::Agent.logger.error("Caught exception trying to get connection to DB for explain. Control: #{config}", e)
168
165
  nil
@@ -253,7 +250,7 @@ module NewRelic
253
250
  end
254
251
 
255
252
  class Statement < String
256
- attr_accessor :adapter
253
+ attr_accessor :adapter, :config, :explainer
257
254
  end
258
255
  end
259
256
  end
@@ -6,6 +6,16 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module ActiveRecord
9
+ EXPLAINER = lambda do |config, query|
10
+ connection = NewRelic::Agent::Database.get_connection(config) do
11
+ ::ActiveRecord::Base.send("#{config[:adapter]}_connection",
12
+ config)
13
+ end
14
+ if connection && connection.respond_to?(:execute)
15
+ return connection.execute("EXPLAIN #{query}")
16
+ end
17
+ end
18
+
9
19
  def self.included(instrumented_class)
10
20
  instrumented_class.class_eval do
11
21
  unless instrumented_class.method_defined?(:log_without_newrelic_instrumentation)
@@ -36,10 +46,13 @@ module NewRelic
36
46
  log_without_newrelic_instrumentation(*args, &block)
37
47
  ensure
38
48
  elapsed_time = (Time.now - t0).to_f
49
+
39
50
  NewRelic::Agent.instance.transaction_sampler.notice_sql(sql,
40
- @config, elapsed_time)
51
+ @config, elapsed_time,
52
+ &EXPLAINER)
41
53
  NewRelic::Agent.instance.sql_sampler.notice_sql(sql, metric,
42
- @config, elapsed_time)
54
+ @config, elapsed_time,
55
+ &EXPLAINER)
43
56
  end
44
57
  end
45
58
  end
@@ -63,7 +63,7 @@ module NewRelic
63
63
  # Host defaults to "localhost".
64
64
  def remote_service_metric(adapter, host)
65
65
  host ||= 'localhost'
66
- type = adapter.sub(/\d*/, '')
66
+ type = adapter.to_s.sub(/\d*/, '')
67
67
  "RemoteService/sql/#{type}/#{host}"
68
68
  end
69
69
  end
@@ -27,6 +27,16 @@ module NewRelic
27
27
  notice_sql(event)
28
28
  end
29
29
 
30
+ def get_explain_plan( config, query )
31
+ connection = NewRelic::Agent::Database.get_connection(config) do
32
+ ::ActiveRecord::Base.send("#{config[:adapter]}_connection",
33
+ config)
34
+ end
35
+ if connection && connection.respond_to?(:execute)
36
+ return connection.execute("EXPLAIN #{query}")
37
+ end
38
+ end
39
+
30
40
  def notice_sql(event)
31
41
  config = active_record_config_for_event(event)
32
42
  metric = base_metric(event)
@@ -36,11 +46,13 @@ module NewRelic
36
46
 
37
47
  NewRelic::Agent.instance.transaction_sampler \
38
48
  .notice_sql(event.payload[:sql], config,
39
- Helper.milliseconds_to_seconds(event.duration))
49
+ Helper.milliseconds_to_seconds(event.duration),
50
+ &method(:get_explain_plan))
40
51
 
41
52
  NewRelic::Agent.instance.sql_sampler \
42
53
  .notice_sql(event.payload[:sql], metric, config,
43
- Helper.milliseconds_to_seconds(event.duration))
54
+ Helper.milliseconds_to_seconds(event.duration),
55
+ &method(:get_explain_plan))
44
56
 
45
57
  # exit transaction trace segment
46
58
  NewRelic::Agent.instance.stats_engine.pop_scope(scope, metric, event.end)
@@ -2,7 +2,6 @@
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
4
 
5
-
6
5
  DependencyDetection.defer do
7
6
  @name = :net
8
7
 
@@ -17,11 +16,18 @@ DependencyDetection.defer do
17
16
 
18
17
  executes do
19
18
  class Net::HTTP
20
-
21
- # Instrument outgoing HTTP requests and fire associated events back
22
- # into the Agent.
19
+ # Instrument outgoing HTTP requests
20
+ #
21
+ # If request is called when not the connection isn't started, request
22
+ # will call back into itself (via a start block).
23
+ #
24
+ # Don't tracing until the inner call then to avoid double-counting.
23
25
  def request_with_newrelic_trace(request, *args, &block)
24
- NewRelic::Agent::CrossAppTracing.trace_http_request( self, request ) do
26
+ if started?
27
+ NewRelic::Agent::CrossAppTracing.trace_http_request( self, request ) do
28
+ request_without_newrelic_trace( request, *args, &block )
29
+ end
30
+ else
25
31
  request_without_newrelic_trace( request, *args, &block )
26
32
  end
27
33
  end
@@ -66,8 +66,7 @@ DependencyDetection.defer do
66
66
  ::Resque.before_first_fork do
67
67
  NewRelic::Agent.manual_start(:dispatcher => :resque,
68
68
  :sync_startup => true,
69
- :start_channel_listener => true,
70
- :report_instance_busy => false)
69
+ :start_channel_listener => true)
71
70
  end
72
71
 
73
72
  ::Resque.before_fork do |job|
@@ -75,7 +74,10 @@ DependencyDetection.defer do
75
74
  end
76
75
 
77
76
  ::Resque.after_fork do |job|
78
- NewRelic::Agent.after_fork(:report_to_channel => job.object_id)
77
+ # Only suppress reporting Instance/Busy for forked children
78
+ # Traced errors UI relies on having the parent process report that metric
79
+ NewRelic::Agent.after_fork(:report_to_channel => job.object_id,
80
+ :report_instance_busy => false)
79
81
  end
80
82
  end
81
83
  end
@@ -0,0 +1,40 @@
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
+ require 'newrelic_rpm'
7
+
8
+ DependencyDetection.defer do
9
+ @name = :sequel
10
+
11
+ depends_on do
12
+ defined?(::Sequel)
13
+ end
14
+
15
+ depends_on do
16
+ !NewRelic::Agent.config[:disable_activerecord_instrumentation] &&
17
+ !NewRelic::Agent.config[:disable_database_instrumentation]
18
+ end
19
+
20
+ executes do
21
+ ::NewRelic::Agent.logger.info 'Installing Sequel instrumentation'
22
+
23
+ if Sequel::Database.respond_to?( :extension )
24
+ Sequel::Database.extension :newrelic_instrumentation
25
+ else
26
+ NewRelic::Agent.logger.info "Detected Sequel version %s." % [ Sequel::VERSION ]
27
+ NewRelic::Agent.logger.info "Please see additional documentation: " +
28
+ "https://newrelic.com/docs/ruby/sequel-instrumentation"
29
+ end
30
+
31
+ Sequel.synchronize{Sequel::DATABASES.dup}.each do |db|
32
+ db.extension :newrelic_instrumentation
33
+ end
34
+
35
+ Sequel::Model.plugin :newrelic_instrumentation
36
+
37
+ end
38
+
39
+ end
40
+
@@ -99,14 +99,14 @@ module NewRelic
99
99
 
100
100
  end
101
101
 
102
- def notice_sql(sql, metric_name, config, duration)
102
+ def notice_sql(sql, metric_name, config, duration, &explainer)
103
103
  return unless transaction_data
104
104
  if NewRelic::Agent.is_sql_recorded?
105
105
  if duration > Agent.config[:'slow_sql.explain_threshold']
106
106
  backtrace = caller.join("\n")
107
107
  transaction_data.sql_data << SlowSql.new(TransactionSampler.truncate_message(sql),
108
108
  metric_name, config,
109
- duration, backtrace)
109
+ duration, backtrace, &explainer)
110
110
  end
111
111
  end
112
112
  end
@@ -163,12 +163,14 @@ module NewRelic
163
163
  attr_reader :duration
164
164
  attr_reader :backtrace
165
165
 
166
- def initialize(sql, metric_name, config, duration, backtrace = nil)
166
+ def initialize(sql, metric_name, config, duration, backtrace=nil,
167
+ &explainer)
167
168
  @sql = sql
168
169
  @metric_name = metric_name
169
170
  @config = config
170
171
  @duration = duration
171
172
  @backtrace = backtrace
173
+ @explainer = explainer
172
174
  end
173
175
 
174
176
  def obfuscate
@@ -181,7 +183,14 @@ module NewRelic
181
183
  end
182
184
 
183
185
  def explain
184
- NewRelic::Agent::Database.explain_sql(@sql, @config)
186
+ if @config && @explainer
187
+ NewRelic::Agent::Database.explain_sql(@sql, @config, &@explainer)
188
+ end
189
+ end
190
+
191
+ # We can't serialize the explainer, so clear it before we transmit
192
+ def prepare_to_send
193
+ @explainer = nil
185
194
  end
186
195
  end
187
196
 
@@ -222,6 +231,7 @@ module NewRelic
222
231
  def prepare_to_send
223
232
  params[:explain_plan] = @slow_sql.explain if need_to_explain?
224
233
  @sql = @slow_sql.obfuscate if need_to_obfuscate?
234
+ @slow_sql.prepare_to_send
225
235
  end
226
236
 
227
237
  def need_to_obfuscate?
@@ -261,19 +261,16 @@ module NewRelic
261
261
  # active segment like a sql query, memcache key, or Net::HTTP uri
262
262
  #
263
263
  # duration is seconds, float value.
264
- def notice_extra_data(message, duration, key, config=nil, config_key=nil)
264
+ def notice_extra_data(message, duration, key)
265
265
  return unless builder
266
266
  segment = builder.current_segment
267
267
  if segment
268
- new_message = self.class.truncate_message(append_new_message(segment[key],
269
- message))
270
- if key == :sql && config.respond_to?(:has_key?) && config.has_key?(:adapter)
271
- segment[key] = Database::Statement.new(new_message)
272
- segment[key].adapter = config[:adapter]
268
+ if key != :sql
269
+ segment[key] = self.class.truncate_message(append_new_message(segment[key],
270
+ message))
273
271
  else
274
- segment[key] = new_message
272
+ segment[key] = message
275
273
  end
276
- segment[config_key] = config if config_key
277
274
  append_backtrace(segment, duration)
278
275
  end
279
276
  end
@@ -314,9 +311,15 @@ module NewRelic
314
311
  # may be very large; we should trim them to a maximum usable length
315
312
  # config is the driver configuration for the connection
316
313
  # duration is seconds, float value.
317
- def notice_sql(sql, config, duration)
314
+ def notice_sql(sql, config, duration, &explainer)
318
315
  if NewRelic::Agent.is_sql_recorded?
319
- notice_extra_data(sql, duration, :sql, config, :connection_config)
316
+ statement = Database::Statement.new(self.class.truncate_message(sql))
317
+ if config
318
+ statement.adapter = config[:adapter]
319
+ statement.config = config
320
+ end
321
+ statement.explainer = explainer
322
+ notice_extra_data(statement, duration, :sql)
320
323
  end
321
324
  end
322
325
 
@@ -1,2 +1,2 @@
1
- # GITSHA: a83461a282a21153235345f8ea3fbfc579b87126
2
- module NewRelic; module VERSION; BUILD='88'; end; end
1
+ # GITSHA: cf4fddf24f885c2d7a4cbdaf9c3b762e482354e8
2
+ module NewRelic; module VERSION; BUILD='90.beta'; end; end
@@ -182,16 +182,23 @@ module NewRelic
182
182
  end
183
183
  nil
184
184
  end
185
-
185
+
186
186
  def explain_sql
187
- NewRelic::Agent::Database.explain_sql(params[:sql],
188
- params[:connection_config])
187
+ return params[:explain_plan] if params.key?(:explain_plan)
188
+
189
+ statement = params[:sql]
190
+ return nil unless statement.respond_to?(:config) &&
191
+ statement.respond_to?(:explainer)
192
+
193
+ NewRelic::Agent::Database.explain_sql(statement,
194
+ statement.config,
195
+ &statement.explainer)
189
196
  end
190
-
197
+
191
198
  def obfuscated_sql
192
199
  NewRelic::Agent::Database.obfuscate_sql(params[:sql])
193
200
  end
194
-
201
+
195
202
  def params=(p)
196
203
  @params = p
197
204
  end
@@ -12,7 +12,7 @@ module NewRelic
12
12
 
13
13
  MAJOR = 3
14
14
  MINOR = 6
15
- TINY = 1
15
+ TINY = 2
16
16
 
17
17
  begin
18
18
  require File.join(File.dirname(__FILE__), 'build')