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.
- data/CHANGELOG +20 -0
- data/Gemfile +1 -0
- data/lib/new_relic/agent/autostart.rb +4 -3
- data/lib/new_relic/agent/database.rb +14 -17
- data/lib/new_relic/agent/instrumentation/active_record.rb +15 -2
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +14 -2
- data/lib/new_relic/agent/instrumentation/net.rb +11 -5
- data/lib/new_relic/agent/instrumentation/resque.rb +5 -3
- data/lib/new_relic/agent/instrumentation/sequel.rb +40 -0
- data/lib/new_relic/agent/sql_sampler.rb +14 -4
- data/lib/new_relic/agent/transaction_sampler.rb +13 -10
- data/lib/new_relic/build.rb +2 -2
- data/lib/new_relic/transaction_sample/segment.rb +12 -5
- data/lib/new_relic/version.rb +1 -1
- data/lib/sequel/extensions/newrelic_instrumentation.rb +103 -0
- data/lib/sequel/plugins/newrelic_instrumentation.rb +83 -0
- data/test/multiverse/suites/agent_only/thread_profiling_test.rb +1 -1
- data/test/multiverse/suites/resque/instrumentation_test.rb +2 -0
- data/test/new_relic/agent/database_test.rb +15 -12
- data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +13 -3
- data/test/new_relic/agent/instrumentation/sequel_test.rb +285 -0
- data/test/new_relic/agent/sql_sampler_test.rb +27 -4
- data/test/new_relic/agent/transaction_sampler_test.rb +14 -14
- data/test/new_relic/transaction_sample/segment_test.rb +24 -2
- data/test/new_relic/transaction_sample_test.rb +1 -1
- data/test/test_helper.rb +19 -5
- data.tar.gz.sig +0 -0
- metadata +17 -26
- 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
@@ -19,9 +19,10 @@ module NewRelic
|
|
19
19
|
extend self
|
20
20
|
|
21
21
|
|
22
|
-
# The constants
|
23
|
-
#
|
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
|
-
|
88
|
-
plan =
|
89
|
-
|
90
|
-
|
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
|
-
|
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
|
-
|
51
|
+
@config, elapsed_time,
|
52
|
+
&EXPLAINER)
|
41
53
|
NewRelic::Agent.instance.sql_sampler.notice_sql(sql, metric,
|
42
|
-
|
54
|
+
@config, elapsed_time,
|
55
|
+
&EXPLAINER)
|
43
56
|
end
|
44
57
|
end
|
45
58
|
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
|
-
#
|
22
|
-
#
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
264
|
+
def notice_extra_data(message, duration, key)
|
265
265
|
return unless builder
|
266
266
|
segment = builder.current_segment
|
267
267
|
if segment
|
268
|
-
|
269
|
-
|
270
|
-
|
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] =
|
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
|
-
|
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
|
|
data/lib/new_relic/build.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
# GITSHA:
|
2
|
-
module NewRelic; module VERSION; BUILD='
|
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
|
-
|
188
|
-
|
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
|