newrelic_rpm 2.13.4 → 2.13.5.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.
- data/CHANGELOG +9 -0
- data/lib/new_relic/agent.rb +2 -1
- data/lib/new_relic/agent/agent.rb +393 -204
- data/lib/new_relic/agent/error_collector.rb +113 -43
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +14 -16
- data/lib/new_relic/agent/instrumentation/queue_time.rb +201 -0
- data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +1 -1
- data/lib/new_relic/agent/instrumentation/sequel.rb +95 -0
- data/lib/new_relic/agent/method_tracer.rb +391 -313
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +43 -41
- data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +2 -0
- data/lib/new_relic/agent/samplers/memory_sampler.rb +122 -120
- data/lib/new_relic/agent/samplers/object_sampler.rb +2 -0
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +0 -1
- data/lib/new_relic/agent/stats_engine/samplers.rb +20 -14
- data/lib/new_relic/agent/stats_engine/transactions.rb +35 -7
- data/lib/new_relic/control.rb +12 -17
- data/lib/new_relic/control/configuration.rb +1 -0
- data/lib/new_relic/control/frameworks/rails.rb +7 -4
- data/lib/new_relic/control/frameworks/rails3.rb +1 -1
- data/lib/new_relic/control/instrumentation.rb +2 -18
- data/lib/new_relic/local_environment.rb +117 -59
- data/lib/new_relic/rack/developer_mode.rb +212 -207
- data/lib/new_relic/recipes.rb +0 -9
- data/lib/new_relic/stats.rb +87 -81
- data/lib/new_relic/transaction_analysis.rb +1 -1
- data/lib/new_relic/version.rb +2 -2
- data/lib/newrelic_rpm.rb +2 -3
- data/lib/tasks/tests.rake +5 -1
- data/newrelic_rpm.gemspec +14 -5
- data/test/config/test_control.rb +14 -2
- data/test/new_relic/agent/active_record_instrumentation_test.rb +342 -119
- data/test/new_relic/agent/add_method_tracer_test.rb +158 -0
- data/test/new_relic/agent/agent_connect_test.rb +295 -0
- data/test/new_relic/agent/agent_controller_test.rb +86 -18
- data/test/new_relic/agent/agent_start_test.rb +326 -0
- data/test/new_relic/agent/agent_start_worker_thread_test.rb +157 -0
- data/test/new_relic/agent/apdex_from_server_test.rb +9 -0
- data/test/new_relic/agent/collection_helper_test.rb +3 -1
- data/test/new_relic/agent/error_collector_notice_error_test.rb +255 -0
- data/test/new_relic/agent/error_collector_test.rb +6 -0
- data/test/new_relic/agent/method_tracer_test.rb +2 -2
- data/test/new_relic/agent/method_tracer_trace_execution_scoped_test.rb +233 -0
- data/test/new_relic/agent/net_instrumentation_test.rb +17 -12
- data/test/new_relic/agent/queue_time_test.rb +333 -0
- data/test/new_relic/agent/rpm_agent_test.rb +4 -2
- data/test/new_relic/agent/stats_engine/samplers_test.rb +27 -1
- data/test/new_relic/agent/transaction_sample_subtest_test.rb +56 -0
- data/test/new_relic/agent/transaction_sample_test.rb +103 -174
- data/test/new_relic/agent/transaction_sampler_test.rb +9 -2
- data/test/new_relic/control_test.rb +7 -2
- data/test/new_relic/metric_spec_test.rb +1 -1
- data/test/new_relic/stats_test.rb +112 -15
- data/test/test_helper.rb +79 -16
- data/ui/helpers/developer_mode_helper.rb +2 -0
- metadata +19 -7
- data/lib/new_relic_api.rb +0 -276
data/lib/new_relic/control.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'yaml'
|
2
|
+
require 'conditional_vendored_metric_parser'
|
2
3
|
require 'new_relic/local_environment'
|
4
|
+
|
3
5
|
require 'singleton'
|
4
6
|
require 'erb'
|
5
7
|
require 'socket'
|
@@ -11,6 +13,9 @@ require 'new_relic/control/configuration'
|
|
11
13
|
require 'new_relic/control/server_methods'
|
12
14
|
require 'new_relic/control/instrumentation'
|
13
15
|
|
16
|
+
require 'new_relic/agent'
|
17
|
+
require 'new_relic/delayed_job_injection'
|
18
|
+
|
14
19
|
module NewRelic
|
15
20
|
|
16
21
|
# The Control is a singleton responsible for the startup and
|
@@ -42,21 +47,19 @@ module NewRelic
|
|
42
47
|
def instance
|
43
48
|
@instance ||= new_instance
|
44
49
|
end
|
50
|
+
|
51
|
+
def mark_browser_request
|
52
|
+
Thread::current[:browser_request] = true
|
53
|
+
end
|
54
|
+
|
45
55
|
|
46
56
|
# Create the concrete class for environment specific behavior:
|
47
57
|
def new_instance
|
48
58
|
@local_env = NewRelic::LocalEnvironment.new
|
49
59
|
if @local_env.framework == :test
|
50
|
-
# You can set this env var if you want to run the tests
|
51
|
-
# without Rails.
|
52
60
|
config = File.expand_path("../../../test/config/newrelic.yml", __FILE__)
|
53
|
-
|
54
|
-
|
55
|
-
NewRelic::Control::Frameworks::Ruby.new @local_env, config
|
56
|
-
else
|
57
|
-
require "config/test_control"
|
58
|
-
NewRelic::Control::Frameworks::Test.new @local_env, config
|
59
|
-
end
|
61
|
+
require "config/test_control"
|
62
|
+
NewRelic::Control::Frameworks::Test.new @local_env, config
|
60
63
|
else
|
61
64
|
begin
|
62
65
|
require "new_relic/control/frameworks/#{@local_env.framework}.rb"
|
@@ -93,16 +96,8 @@ module NewRelic
|
|
93
96
|
# is called one or more times.
|
94
97
|
#
|
95
98
|
def init_plugin(options={})
|
96
|
-
require 'conditional_vendored_metric_parser'
|
97
|
-
|
98
99
|
options['app_name'] = ENV['NEWRELIC_APP_NAME'] if ENV['NEWRELIC_APP_NAME']
|
99
100
|
|
100
|
-
require 'new_relic/agent'
|
101
|
-
|
102
|
-
# Load the DJ injection now. If you do it sooner, DJ might not be loaded and
|
103
|
-
# you'll miss it.
|
104
|
-
require 'new_relic/delayed_job_injection'
|
105
|
-
|
106
101
|
# Merge the stringified options into the config as overrides:
|
107
102
|
logger_override = options.delete(:log)
|
108
103
|
environment_name = options.delete(:env) and self.env = environment_name
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# Control subclass instantiated when Rails is detected. Contains
|
2
2
|
# Rails specific configuration, instrumentation, environment values,
|
3
3
|
# etc.
|
4
|
-
|
4
|
+
require 'new_relic/control/frameworks/ruby'
|
5
|
+
class NewRelic::Control::Frameworks::Rails < NewRelic::Control::Frameworks::Ruby
|
5
6
|
|
6
7
|
def env
|
7
8
|
@env ||= RAILS_ENV.dup
|
@@ -120,11 +121,13 @@ class NewRelic::Control::Frameworks::Rails < NewRelic::Control
|
|
120
121
|
end
|
121
122
|
end
|
122
123
|
end
|
123
|
-
|
124
|
+
|
124
125
|
def install_shim
|
125
126
|
super
|
126
127
|
require 'new_relic/agent/instrumentation/controller_instrumentation'
|
127
|
-
ActionController::Base.
|
128
|
+
::ActionController::Base.class_eval {
|
129
|
+
include NewRelic::Agent::Instrumentation::ControllerInstrumentation::Shim
|
130
|
+
}
|
128
131
|
end
|
129
|
-
|
130
132
|
end
|
133
|
+
|
@@ -32,25 +32,9 @@ module NewRelic
|
|
32
32
|
@instrumentation_files << pattern
|
33
33
|
end
|
34
34
|
end
|
35
|
-
|
36
|
-
def _delayed_instrumentation
|
37
|
-
Rails.configuration.after_initialize do
|
38
|
-
_install_instrumentation
|
39
|
-
end
|
40
|
-
rescue
|
41
|
-
_install_instrumentation
|
42
|
-
end
|
43
|
-
|
35
|
+
|
44
36
|
def install_instrumentation
|
45
|
-
|
46
|
-
_delayed_instrumentation
|
47
|
-
else
|
48
|
-
_install_instrumentation
|
49
|
-
end
|
50
|
-
rescue NameError
|
51
|
-
# needed in the rails 3 case, where Rails.initialized? raises
|
52
|
-
# an error if rails has not been initialised. which is totally sane.
|
53
|
-
_delayed_instrumentation
|
37
|
+
_install_instrumentation
|
54
38
|
end
|
55
39
|
|
56
40
|
def load_samplers
|
@@ -42,7 +42,7 @@ module NewRelic
|
|
42
42
|
# settings. Must pass either a value or a block. Block
|
43
43
|
# is called to get the value and any raised errors are
|
44
44
|
# silently ignored.
|
45
|
-
def append_environment_value
|
45
|
+
def append_environment_value(name, value = nil)
|
46
46
|
value = yield if block_given?
|
47
47
|
@config[name] = value if value
|
48
48
|
rescue Exception
|
@@ -73,35 +73,49 @@ module NewRelic
|
|
73
73
|
@dispatcher_instance_id
|
74
74
|
end
|
75
75
|
|
76
|
-
|
77
|
-
# comparison and change detection.
|
78
|
-
def gather_environment_info
|
79
|
-
append_environment_value 'Framework', @framework.to_s
|
80
|
-
append_environment_value 'Dispatcher', @dispatcher.to_s if @dispatcher
|
81
|
-
append_environment_value 'Dispatcher instance id', @dispatcher_instance_id if @dispatcher_instance_id
|
82
|
-
# This just creates a lot of keys
|
83
|
-
# append_environment_value('Application root') { File.expand_path(NewRelic::Control.instance.root) }
|
76
|
+
def gather_ruby_info
|
84
77
|
append_environment_value('Ruby version'){ RUBY_VERSION }
|
85
78
|
append_environment_value('Ruby description'){ RUBY_DESCRIPTION } if defined? ::RUBY_DESCRIPTION
|
86
79
|
append_environment_value('Ruby platform') { RUBY_PLATFORM }
|
87
80
|
append_environment_value('Ruby patchlevel') { RUBY_PATCHLEVEL }
|
81
|
+
# room here for other ruby implementations, when.
|
88
82
|
if defined? ::JRUBY_VERSION
|
89
|
-
|
90
|
-
append_environment_value('Java VM version') { ENV_JAVA['java.vm.version']}
|
83
|
+
gather_jruby_info
|
91
84
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
append_environment_value('
|
96
|
-
append_environment_value('
|
97
|
-
|
85
|
+
end
|
86
|
+
|
87
|
+
def gather_jruby_info
|
88
|
+
append_environment_value('JRuby version') { JRUBY_VERSION }
|
89
|
+
append_environment_value('Java VM version') { ENV_JAVA['java.vm.version']}
|
90
|
+
end
|
91
|
+
|
92
|
+
# See what the number of cpus is, works only on linux.
|
93
|
+
def gather_cpu_info
|
94
|
+
return unless File.readable? '/proc/cpuinfo'
|
98
95
|
@processors = append_environment_value('Processors') do
|
99
96
|
processors = File.readlines('/proc/cpuinfo').select { |line| line =~ /^processor\s*:/ }.size
|
100
97
|
raise "Cannot determine the number of processors in /proc/cpuinfo" unless processors > 0
|
101
98
|
processors
|
102
|
-
end
|
103
|
-
|
104
|
-
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def gather_architecture_info
|
103
|
+
append_environment_value('Arch') { `uname -p` } ||
|
104
|
+
append_environment_value('Arch') { ENV['PROCESSOR_ARCHITECTURE'] }
|
105
|
+
end
|
106
|
+
|
107
|
+
def gather_os_info
|
108
|
+
append_environment_value('OS version') { `uname -v` }
|
109
|
+
append_environment_value('OS') { `uname -s` } ||
|
110
|
+
append_environment_value('OS') { ENV['OS'] }
|
111
|
+
end
|
112
|
+
|
113
|
+
def gather_system_info
|
114
|
+
gather_architecture_info
|
115
|
+
gather_cpu_info
|
116
|
+
end
|
117
|
+
|
118
|
+
def gather_revision_info
|
105
119
|
# Look for a capistrano file indicating the current revision:
|
106
120
|
rev_file = File.join(NewRelic::Control.instance.root, "REVISION")
|
107
121
|
if File.readable?(rev_file) && File.size(rev_file) < 64
|
@@ -109,22 +123,56 @@ module NewRelic
|
|
109
123
|
File.open(rev_file) { | file | file.readline.strip }
|
110
124
|
end
|
111
125
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
126
|
+
end
|
127
|
+
|
128
|
+
def gather_ar_adapter_info
|
129
|
+
# The name of the database adapter for the current environment.
|
130
|
+
append_environment_value 'Database adapter' do
|
131
|
+
if defined?(ActiveRecord) && defined?(ActiveRecord::Base) &&
|
132
|
+
ActiveRecord::Base.respond_to?(:configurations)
|
133
|
+
config = ActiveRecord::Base.configurations[NewRelic::Control.instance.env]
|
134
|
+
if config
|
135
|
+
config['adapter']
|
136
|
+
end
|
119
137
|
end
|
120
138
|
end
|
139
|
+
append_environment_value 'Database schema version' do
|
140
|
+
ActiveRecord::Migrator.current_version
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def gather_dm_adapter_info
|
145
|
+
append_environment_value 'DataMapper version' do
|
146
|
+
require 'dm-core/version'
|
147
|
+
DataMapper::VERSION
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def gather_db_info
|
152
|
+
# room here for more database adapters, when.
|
153
|
+
if defined? ::ActiveRecord
|
154
|
+
gather_ar_adapter_info
|
155
|
+
end
|
121
156
|
if defined? ::DataMapper
|
122
|
-
|
123
|
-
require 'dm-core/version'
|
124
|
-
DataMapper::VERSION
|
125
|
-
end
|
157
|
+
gather_dm_adapter_info
|
126
158
|
end
|
127
159
|
end
|
160
|
+
|
161
|
+
# Collect base statistics about the environment and record them for
|
162
|
+
# comparison and change detection.
|
163
|
+
def gather_environment_info
|
164
|
+
append_environment_value 'Framework', @framework.to_s
|
165
|
+
append_environment_value 'Dispatcher', @dispatcher.to_s if @dispatcher
|
166
|
+
append_environment_value 'Dispatcher instance id', @dispatcher_instance_id if @dispatcher_instance_id
|
167
|
+
append_environment_value('Environment') { NewRelic::Control.instance.env }
|
168
|
+
|
169
|
+
# miscellaneous other helpful debugging information
|
170
|
+
gather_ruby_info
|
171
|
+
gather_system_info
|
172
|
+
gather_revision_info
|
173
|
+
gather_db_info
|
174
|
+
end
|
175
|
+
|
128
176
|
# Take a snapshot of the environment information for this application
|
129
177
|
# Returns an associative array
|
130
178
|
def snapshot
|
@@ -134,30 +182,32 @@ module NewRelic
|
|
134
182
|
i
|
135
183
|
end
|
136
184
|
|
185
|
+
def working_juby?
|
186
|
+
!(defined?(::JRuby) && Jruby.respond_to?(:runtime) && !JRuby.runtime.is_object_space_enabled)
|
187
|
+
end
|
188
|
+
|
189
|
+
def find_class_in_object_space(klass)
|
190
|
+
ObjectSpace.each_object(klass) do |x|
|
191
|
+
return x
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
137
195
|
def mongrel
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@mongrel = mongrel
|
143
|
-
end unless defined?(::JRuby) && !JRuby.runtime.is_object_space_enabled
|
196
|
+
return @mongrel if @mongrel
|
197
|
+
if defined?(::Mongrel) && defined?(::Mongrel::HttpServer) && working_jruby?
|
198
|
+
@mongrel = find_class_in_object_space(::Mongrel::HttpServer)
|
199
|
+
end
|
144
200
|
@mongrel
|
145
201
|
end
|
146
202
|
|
147
203
|
def unicorn
|
148
|
-
return @unicorn if @unicorn
|
149
|
-
|
150
|
-
@unicorn =
|
151
|
-
end
|
204
|
+
return @unicorn if @unicorn
|
205
|
+
if (defined?(::Unicorn) && defined?(::Unicorn::HttpServer)) && working_jruby?
|
206
|
+
@unicorn = find_class_in_object_space(::Unicorn::HttpServer)
|
207
|
+
end
|
152
208
|
@unicorn
|
153
209
|
end
|
154
210
|
|
155
|
-
# Obsolete method for DelayedJob instrumentation support. Now all DJ instrumentation
|
156
|
-
# is bundled in the newrelic_rpm gem and nobody should be invoking this method.
|
157
|
-
def delayed_worker=(worker)
|
158
|
-
$stderr.puts "WARNING: obsolete call to delayed_worker=(worker). Please remove custom DJ instrumentation."
|
159
|
-
end
|
160
|
-
|
161
211
|
private
|
162
212
|
|
163
213
|
# Although you can override the framework with NEWRELIC_DISPATCHER this
|
@@ -177,14 +227,14 @@ module NewRelic
|
|
177
227
|
# Note that the odd defined? sequence is necessary to work around a bug in an older version
|
178
228
|
# of JRuby.
|
179
229
|
@framework ||= case
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
230
|
+
when ENV['NEWRELIC_FRAMEWORK'] then ENV['NEWRELIC_FRAMEWORK'].to_sym
|
231
|
+
when defined?(::NewRelic::TEST) then :test
|
232
|
+
when defined?(::Merb) && defined?(::Merb::Plugins) then :merb
|
233
|
+
when defined?(::Rails) then check_rails_version
|
234
|
+
when defined?(::Sinatra) && defined?(::Sinatra::Base) then :sinatra
|
235
|
+
when defined?(::NewRelic::IA) then :external
|
236
|
+
else :ruby
|
237
|
+
end
|
188
238
|
end
|
189
239
|
|
190
240
|
def check_rails_version
|
@@ -197,7 +247,7 @@ module NewRelic
|
|
197
247
|
|
198
248
|
def check_for_torquebox
|
199
249
|
return unless defined?(::JRuby) &&
|
200
|
-
|
250
|
+
( Java::OrgTorqueboxRailsWebDeployers::RailsRackDeployer rescue nil)
|
201
251
|
@dispatcher = :torquebox
|
202
252
|
end
|
203
253
|
|
@@ -205,12 +255,20 @@ module NewRelic
|
|
205
255
|
return unless defined?(::JRuby) &&
|
206
256
|
(((com.sun.grizzly.jruby.rack.DefaultRackApplicationFactory rescue nil) &&
|
207
257
|
defined?(com::sun::grizzly::jruby::rack::DefaultRackApplicationFactory)) ||
|
208
|
-
(
|
209
|
-
defined?(org::jruby::rack::DefaultRackApplicationFactory)) ||
|
210
|
-
defined?(::GlassFish::Server))
|
258
|
+
(jruby_rack? && defined?(::GlassFish::Server)))
|
211
259
|
@dispatcher = :glassfish
|
212
260
|
end
|
213
261
|
|
262
|
+
def check_for_trinidad
|
263
|
+
return unless defined?(::JRuby) && jruby_rack? && defined?(::Trinidad::Server)
|
264
|
+
@dispatcher = :trinidad
|
265
|
+
end
|
266
|
+
|
267
|
+
def jruby_rack?
|
268
|
+
((org.jruby.rack.DefaultRackApplicationFactory rescue nil) &&
|
269
|
+
defined?(org::jruby::rack::DefaultRackApplicationFactory))
|
270
|
+
end
|
271
|
+
|
214
272
|
def check_for_webrick
|
215
273
|
return unless defined?(::WEBrick) && defined?(::WEBrick::VERSION)
|
216
274
|
@dispatcher = :webrick
|
@@ -2,256 +2,261 @@ require 'rack'
|
|
2
2
|
require 'rack/request'
|
3
3
|
require 'rack/response'
|
4
4
|
require 'rack/file'
|
5
|
+
require 'new_relic/metric_parser/metric_parser'
|
6
|
+
require 'new_relic/collection_helper'
|
5
7
|
|
6
|
-
module NewRelic
|
7
|
-
|
8
|
+
module NewRelic
|
9
|
+
module Rack
|
10
|
+
class DeveloperMode
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
VIEW_PATH = File.expand_path('../../../../ui/views/', __FILE__)
|
13
|
+
HELPER_PATH = File.expand_path('../../../../ui/helpers/', __FILE__)
|
14
|
+
require File.join(HELPER_PATH, 'developer_mode_helper.rb')
|
12
15
|
|
13
|
-
include NewRelic::DeveloperModeHelper
|
14
16
|
|
15
|
-
|
16
|
-
@app = app
|
17
|
-
end
|
17
|
+
include NewRelic::DeveloperModeHelper
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
19
|
+
def initialize(app)
|
20
|
+
@app = app
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@rendered = false
|
29
|
-
case @req.path_info
|
30
|
-
when /profile/
|
31
|
-
profile
|
32
|
-
when /file/
|
33
|
-
Rack::File.new(VIEW_PATH).call(env)
|
34
|
-
when /index/
|
35
|
-
index
|
36
|
-
when /threads/
|
37
|
-
threads
|
38
|
-
when /reset/
|
39
|
-
reset
|
40
|
-
when /show_sample_detail/
|
41
|
-
show_sample_data
|
42
|
-
when /show_sample_summary/
|
43
|
-
show_sample_data
|
44
|
-
when /show_sample_sql/
|
45
|
-
show_sample_data
|
46
|
-
when /explain_sql/
|
47
|
-
explain_sql
|
48
|
-
when /show_source/
|
49
|
-
show_source
|
50
|
-
when /^\/newrelic\/?$/
|
51
|
-
index
|
52
|
-
else
|
53
|
-
@app.call(env)
|
54
|
-
end
|
55
|
-
end
|
23
|
+
def call(env)
|
24
|
+
return @app.call(env) unless /^\/newrelic/ =~ Rack::Request.new(env).path_info
|
25
|
+
dup._call(env)
|
26
|
+
end
|
56
27
|
|
57
|
-
|
28
|
+
protected
|
29
|
+
|
30
|
+
def _call(env)
|
31
|
+
@req = Rack::Request.new(env)
|
32
|
+
@rendered = false
|
33
|
+
case @req.path_info
|
34
|
+
when /profile/
|
35
|
+
profile
|
36
|
+
when /file/
|
37
|
+
Rack::File.new(VIEW_PATH).call(env)
|
38
|
+
when /index/
|
39
|
+
index
|
40
|
+
when /threads/
|
41
|
+
threads
|
42
|
+
when /reset/
|
43
|
+
reset
|
44
|
+
when /show_sample_detail/
|
45
|
+
show_sample_data
|
46
|
+
when /show_sample_summary/
|
47
|
+
show_sample_data
|
48
|
+
when /show_sample_sql/
|
49
|
+
show_sample_data
|
50
|
+
when /explain_sql/
|
51
|
+
explain_sql
|
52
|
+
when /show_source/
|
53
|
+
show_source
|
54
|
+
when /^\/newrelic\/?$/
|
55
|
+
index
|
56
|
+
else
|
57
|
+
@app.call(env)
|
58
|
+
end
|
59
|
+
end
|
58
60
|
|
59
|
-
|
60
|
-
get_samples
|
61
|
-
render(:index)
|
62
|
-
end
|
61
|
+
private
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
def index
|
64
|
+
get_samples
|
65
|
+
render(:index)
|
66
|
+
end
|
68
67
|
|
69
|
-
|
70
|
-
|
68
|
+
def reset
|
69
|
+
NewRelic::Agent.instance.transaction_sampler.reset!
|
70
|
+
Rack::Response.new{|r| r.redirect('/newrelic/')}.finish
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
+
def explain_sql
|
74
|
+
get_segment
|
73
75
|
|
74
|
-
|
75
|
-
@trace = @segment[:backtrace]
|
76
|
+
return render(:sample_not_found) unless @sample
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
end
|
78
|
+
@sql = @segment[:sql]
|
79
|
+
@trace = @segment[:backtrace]
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
@explanation = explanations.first
|
84
|
-
if !@explanation.blank?
|
85
|
-
first_row = @explanation.first
|
86
|
-
# Show the standard headers if it looks like a mysql explain plan
|
87
|
-
# Otherwise show blank headers
|
88
|
-
if first_row.length < NewRelic::MYSQL_EXPLAIN_COLUMNS.length
|
89
|
-
@row_headers = nil
|
90
|
-
else
|
91
|
-
@row_headers = NewRelic::MYSQL_EXPLAIN_COLUMNS
|
81
|
+
if NewRelic::Agent.agent.record_sql == :obfuscated
|
82
|
+
@obfuscated_sql = @segment.obfuscated_sql
|
92
83
|
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
render(:explain_sql)
|
96
|
-
end
|
97
|
-
|
98
|
-
def profile
|
99
|
-
NewRelic::Control.instance.profiling = params['start'] == 'true'
|
100
|
-
index
|
101
|
-
end
|
102
84
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
85
|
+
explanations = @segment.explain_sql
|
86
|
+
if explanations
|
87
|
+
@explanation = explanations.first
|
88
|
+
if !@explanation.blank?
|
89
|
+
first_row = @explanation.first
|
90
|
+
# Show the standard headers if it looks like a mysql explain plan
|
91
|
+
# Otherwise show blank headers
|
92
|
+
if first_row.length < NewRelic::MYSQL_EXPLAIN_COLUMNS.length
|
93
|
+
@row_headers = nil
|
94
|
+
else
|
95
|
+
@row_headers = NewRelic::MYSQL_EXPLAIN_COLUMNS
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
render(:explain_sql)
|
113
100
|
end
|
114
101
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end.join(' ')
|
102
|
+
def profile
|
103
|
+
NewRelic::Control.instance.profiling = params['start'] == 'true'
|
104
|
+
index
|
119
105
|
end
|
120
106
|
|
121
|
-
|
122
|
-
|
123
|
-
view = "_#{view[:partial]}"
|
107
|
+
def threads
|
108
|
+
render(:threads)
|
124
109
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
110
|
+
|
111
|
+
def render(view, layout=true)
|
112
|
+
add_rack_array = true
|
113
|
+
if view.is_a? Hash
|
114
|
+
layout = false
|
115
|
+
if view[:object]
|
116
|
+
object = view[:object]
|
117
|
+
end
|
118
|
+
|
119
|
+
if view[:collection]
|
120
|
+
return view[:collection].map do |object|
|
121
|
+
render({:partial => view[:partial], :object => object})
|
122
|
+
end.join(' ')
|
123
|
+
end
|
124
|
+
|
125
|
+
if view[:partial]
|
126
|
+
add_rack_array = false
|
127
|
+
view = "_#{view[:partial]}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
binding = Proc.new {}.binding
|
131
|
+
if layout
|
132
|
+
body = render_with_layout(view) do
|
133
|
+
render_without_layout(view, binding)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
body = render_without_layout(view, binding)
|
137
|
+
end
|
138
|
+
if add_rack_array
|
139
|
+
Rack::Response.new(body).finish
|
140
|
+
else
|
141
|
+
body
|
142
|
+
end
|
130
143
|
end
|
131
|
-
else
|
132
|
-
body = render_without_layout(view, binding)
|
133
|
-
end
|
134
|
-
if add_rack_array
|
135
|
-
Rack::Response.new(body).finish
|
136
|
-
else
|
137
|
-
body
|
138
|
-
end
|
139
|
-
end
|
140
144
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
145
|
+
# You have to call this with a block - the contents returned from
|
146
|
+
# that block are interpolated into the layout
|
147
|
+
def render_with_layout(view)
|
148
|
+
body = ERB.new(File.read(File.join(VIEW_PATH, 'layouts/newrelic_default.rhtml')))
|
149
|
+
body.result(Proc.new {}.binding)
|
150
|
+
end
|
147
151
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
152
|
+
# you have to pass a binding to this (a proc) so that ERB can have
|
153
|
+
# access to helper functions and local variables
|
154
|
+
def render_without_layout(view, binding)
|
155
|
+
ERB.new(File.read(File.join(VIEW_PATH, 'newrelic', view.to_s + '.rhtml')), nil, nil, 'frobnitz').result(binding)
|
156
|
+
end
|
153
157
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
+
def content_tag(tag, contents, opts={})
|
159
|
+
opt_values = opts.map {|k, v| "#{k}=\"#{v}\"" }.join(' ')
|
160
|
+
"<#{tag} #{opt_values}>#{contents}</#{tag}>"
|
161
|
+
end
|
158
162
|
|
159
|
-
|
160
|
-
|
161
|
-
|
163
|
+
def sample
|
164
|
+
@sample || @samples[0]
|
165
|
+
end
|
162
166
|
|
163
|
-
|
164
|
-
|
165
|
-
|
167
|
+
def params
|
168
|
+
@req.params
|
169
|
+
end
|
166
170
|
|
167
|
-
|
168
|
-
|
169
|
-
|
171
|
+
def segment
|
172
|
+
@segment
|
173
|
+
end
|
170
174
|
|
171
175
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
+
# show the selected source file with the highlighted selected line
|
177
|
+
def show_source
|
178
|
+
@filename = params['file']
|
179
|
+
line_number = params['line'].to_i
|
176
180
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
181
|
+
if !File.readable?(@filename)
|
182
|
+
@source="<p>Unable to read #{@filename}.</p>"
|
183
|
+
return
|
184
|
+
end
|
185
|
+
begin
|
186
|
+
file = File.new(@filename, 'r')
|
187
|
+
rescue => e
|
188
|
+
@source="<p>Unable to access the source file #{@filename} (#{e.message}).</p>"
|
189
|
+
return
|
190
|
+
end
|
191
|
+
@source = ""
|
192
|
+
|
193
|
+
@source << "<pre>"
|
194
|
+
file.each_line do |line|
|
195
|
+
# place an anchor 6 lines above the selected line (if the line # < 6)
|
196
|
+
if file.lineno == line_number - 6
|
197
|
+
@source << "</pre><pre id = 'selected_line'>"
|
198
|
+
@source << line.rstrip
|
199
|
+
@source << "</pre><pre>"
|
200
|
+
|
201
|
+
# highlight the selected line
|
202
|
+
elsif file.lineno == line_number
|
203
|
+
@source << "</pre><pre class = 'selected_source_line'>"
|
204
|
+
@source << line.rstrip
|
205
|
+
@source << "</pre><pre>"
|
206
|
+
else
|
207
|
+
@source << line
|
208
|
+
end
|
209
|
+
end
|
210
|
+
render(:show_source)
|
204
211
|
end
|
205
|
-
end
|
206
|
-
render(:show_source)
|
207
|
-
end
|
208
212
|
|
209
|
-
|
210
|
-
|
213
|
+
def show_sample_data
|
214
|
+
get_sample
|
211
215
|
|
212
|
-
|
216
|
+
return render(:sample_not_found) unless @sample
|
213
217
|
|
214
|
-
|
215
|
-
|
218
|
+
@request_params = @sample.params['request_params'] || {}
|
219
|
+
@custom_params = @sample.params['custom_params'] || {}
|
216
220
|
|
217
|
-
|
221
|
+
controller_metric = @sample.root_segment.called_segments.first.metric_name
|
218
222
|
|
219
|
-
|
220
|
-
|
221
|
-
|
223
|
+
metric_parser = NewRelic::MetricParser::MetricParser.for_metric_named controller_metric
|
224
|
+
@sample_controller_name = metric_parser.controller_name
|
225
|
+
@sample_action_name = metric_parser.action_name
|
222
226
|
|
223
|
-
|
224
|
-
|
227
|
+
render(:show_sample)
|
228
|
+
end
|
225
229
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
+
def get_samples
|
231
|
+
@samples = NewRelic::Agent.instance.transaction_sampler.samples.select do |sample|
|
232
|
+
sample.params[:path] != nil
|
233
|
+
end
|
230
234
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
235
|
+
return @samples = @samples.sort{|x,y| y.omit_segments_with('(Rails/Application Code Loading)|(Database/.*/.+ Columns)').duration <=>
|
236
|
+
x.omit_segments_with('(Rails/Application Code Loading)|(Database/.*/.+ Columns)').duration} if params['h']
|
237
|
+
return @samples = @samples.sort{|x,y| x.params[:uri] <=> y.params[:uri]} if params['u']
|
238
|
+
@samples = @samples.reverse
|
239
|
+
end
|
236
240
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
241
|
+
def get_sample
|
242
|
+
get_samples
|
243
|
+
id = params['id']
|
244
|
+
sample_id = id.to_i
|
245
|
+
@samples.each do |s|
|
246
|
+
if s.sample_id == sample_id
|
247
|
+
@sample = stripped_sample(s)
|
248
|
+
return
|
249
|
+
end
|
250
|
+
end
|
245
251
|
end
|
246
|
-
end
|
247
|
-
end
|
248
252
|
|
249
|
-
|
250
|
-
|
251
|
-
|
253
|
+
def get_segment
|
254
|
+
get_sample
|
255
|
+
return unless @sample
|
252
256
|
|
253
|
-
|
254
|
-
|
257
|
+
segment_id = params['segment'].to_i
|
258
|
+
@segment = @sample.find_segment(segment_id)
|
259
|
+
end
|
260
|
+
end
|
255
261
|
end
|
256
262
|
end
|
257
|
-
end
|