honkster-newrelic_rpm 2.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. data/CHANGELOG +462 -0
  2. data/LICENSE +37 -0
  3. data/README.rdoc +172 -0
  4. data/bin/mongrel_rpm +33 -0
  5. data/bin/newrelic +13 -0
  6. data/bin/newrelic_cmd +5 -0
  7. data/cert/cacert.pem +34 -0
  8. data/install.rb +9 -0
  9. data/lib/new_relic/agent.rb +382 -0
  10. data/lib/new_relic/agent/agent.rb +741 -0
  11. data/lib/new_relic/agent/busy_calculator.rb +91 -0
  12. data/lib/new_relic/agent/chained_call.rb +13 -0
  13. data/lib/new_relic/agent/error_collector.rb +131 -0
  14. data/lib/new_relic/agent/instrumentation/active_merchant.rb +18 -0
  15. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +92 -0
  16. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +45 -0
  17. data/lib/new_relic/agent/instrumentation/authlogic.rb +8 -0
  18. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +409 -0
  19. data/lib/new_relic/agent/instrumentation/data_mapper.rb +58 -0
  20. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +22 -0
  21. data/lib/new_relic/agent/instrumentation/memcache.rb +40 -0
  22. data/lib/new_relic/agent/instrumentation/merb/controller.rb +26 -0
  23. data/lib/new_relic/agent/instrumentation/merb/errors.rb +9 -0
  24. data/lib/new_relic/agent/instrumentation/metric_frame.rb +319 -0
  25. data/lib/new_relic/agent/instrumentation/net.rb +17 -0
  26. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +22 -0
  27. data/lib/new_relic/agent/instrumentation/rack.rb +98 -0
  28. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +59 -0
  29. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +27 -0
  30. data/lib/new_relic/agent/instrumentation/rails/errors.rb +24 -0
  31. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +45 -0
  32. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +21 -0
  33. data/lib/new_relic/agent/instrumentation/sinatra.rb +46 -0
  34. data/lib/new_relic/agent/instrumentation/sunspot.rb +17 -0
  35. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +10 -0
  36. data/lib/new_relic/agent/method_tracer.rb +350 -0
  37. data/lib/new_relic/agent/sampler.rb +50 -0
  38. data/lib/new_relic/agent/samplers/cpu_sampler.rb +54 -0
  39. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +37 -0
  40. data/lib/new_relic/agent/samplers/memory_sampler.rb +142 -0
  41. data/lib/new_relic/agent/samplers/object_sampler.rb +24 -0
  42. data/lib/new_relic/agent/shim_agent.rb +25 -0
  43. data/lib/new_relic/agent/stats_engine.rb +24 -0
  44. data/lib/new_relic/agent/stats_engine/metric_stats.rb +118 -0
  45. data/lib/new_relic/agent/stats_engine/samplers.rb +83 -0
  46. data/lib/new_relic/agent/stats_engine/transactions.rb +149 -0
  47. data/lib/new_relic/agent/transaction_sampler.rb +330 -0
  48. data/lib/new_relic/agent/worker_loop.rb +81 -0
  49. data/lib/new_relic/collection_helper.rb +71 -0
  50. data/lib/new_relic/command.rb +85 -0
  51. data/lib/new_relic/commands/deployments.rb +105 -0
  52. data/lib/new_relic/commands/install.rb +81 -0
  53. data/lib/new_relic/control.rb +203 -0
  54. data/lib/new_relic/control/configuration.rb +149 -0
  55. data/lib/new_relic/control/frameworks/external.rb +13 -0
  56. data/lib/new_relic/control/frameworks/merb.rb +24 -0
  57. data/lib/new_relic/control/frameworks/rails.rb +126 -0
  58. data/lib/new_relic/control/frameworks/rails3.rb +60 -0
  59. data/lib/new_relic/control/frameworks/ruby.rb +36 -0
  60. data/lib/new_relic/control/frameworks/sinatra.rb +18 -0
  61. data/lib/new_relic/control/instrumentation.rb +95 -0
  62. data/lib/new_relic/control/logging_methods.rb +74 -0
  63. data/lib/new_relic/control/profiling.rb +24 -0
  64. data/lib/new_relic/control/server_methods.rb +88 -0
  65. data/lib/new_relic/delayed_job_injection.rb +27 -0
  66. data/lib/new_relic/histogram.rb +91 -0
  67. data/lib/new_relic/local_environment.rb +333 -0
  68. data/lib/new_relic/merbtasks.rb +6 -0
  69. data/lib/new_relic/metric_data.rb +42 -0
  70. data/lib/new_relic/metric_parser.rb +136 -0
  71. data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
  72. data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
  73. data/lib/new_relic/metric_parser/active_record.rb +28 -0
  74. data/lib/new_relic/metric_parser/apdex.rb +88 -0
  75. data/lib/new_relic/metric_parser/controller.rb +62 -0
  76. data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
  77. data/lib/new_relic/metric_parser/errors.rb +6 -0
  78. data/lib/new_relic/metric_parser/external.rb +50 -0
  79. data/lib/new_relic/metric_parser/mem_cache.rb +50 -0
  80. data/lib/new_relic/metric_parser/other_transaction.rb +36 -0
  81. data/lib/new_relic/metric_parser/view.rb +61 -0
  82. data/lib/new_relic/metric_parser/web_frontend.rb +14 -0
  83. data/lib/new_relic/metric_parser/web_service.rb +9 -0
  84. data/lib/new_relic/metric_spec.rb +67 -0
  85. data/lib/new_relic/metrics.rb +9 -0
  86. data/lib/new_relic/noticed_error.rb +24 -0
  87. data/lib/new_relic/rack/developer_mode.rb +257 -0
  88. data/lib/new_relic/rack/metric_app.rb +64 -0
  89. data/lib/new_relic/rack/mongrel_rpm.ru +26 -0
  90. data/lib/new_relic/rack/newrelic.yml +27 -0
  91. data/lib/new_relic/rack_app.rb +6 -0
  92. data/lib/new_relic/recipes.rb +82 -0
  93. data/lib/new_relic/stats.rb +368 -0
  94. data/lib/new_relic/timer_lib.rb +27 -0
  95. data/lib/new_relic/transaction_analysis.rb +119 -0
  96. data/lib/new_relic/transaction_sample.rb +586 -0
  97. data/lib/new_relic/url_rule.rb +14 -0
  98. data/lib/new_relic/version.rb +55 -0
  99. data/lib/new_relic_api.rb +276 -0
  100. data/lib/newrelic_rpm.rb +49 -0
  101. data/lib/tasks/all.rb +4 -0
  102. data/lib/tasks/install.rake +7 -0
  103. data/lib/tasks/tests.rake +15 -0
  104. data/newrelic.yml +246 -0
  105. data/newrelic_rpm.gemspec +254 -0
  106. data/recipes/newrelic.rb +6 -0
  107. data/test/active_record_fixtures.rb +55 -0
  108. data/test/config/newrelic.yml +48 -0
  109. data/test/config/test_control.rb +36 -0
  110. data/test/new_relic/agent/active_record_instrumentation_test.rb +286 -0
  111. data/test/new_relic/agent/agent_controller_test.rb +294 -0
  112. data/test/new_relic/agent/agent_test_controller.rb +77 -0
  113. data/test/new_relic/agent/busy_calculator_test.rb +81 -0
  114. data/test/new_relic/agent/collection_helper_test.rb +125 -0
  115. data/test/new_relic/agent/error_collector_test.rb +163 -0
  116. data/test/new_relic/agent/memcache_instrumentation_test.rb +103 -0
  117. data/test/new_relic/agent/method_tracer_test.rb +340 -0
  118. data/test/new_relic/agent/metric_data_test.rb +53 -0
  119. data/test/new_relic/agent/metric_frame_test.rb +51 -0
  120. data/test/new_relic/agent/mock_scope_listener.rb +23 -0
  121. data/test/new_relic/agent/net_instrumentation_test.rb +77 -0
  122. data/test/new_relic/agent/rpm_agent_test.rb +142 -0
  123. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +79 -0
  124. data/test/new_relic/agent/stats_engine/samplers_test.rb +72 -0
  125. data/test/new_relic/agent/stats_engine/stats_engine_test.rb +184 -0
  126. data/test/new_relic/agent/task_instrumentation_test.rb +188 -0
  127. data/test/new_relic/agent/testable_agent.rb +13 -0
  128. data/test/new_relic/agent/transaction_sample_builder_test.rb +195 -0
  129. data/test/new_relic/agent/transaction_sample_test.rb +192 -0
  130. data/test/new_relic/agent/transaction_sampler_test.rb +385 -0
  131. data/test/new_relic/agent/worker_loop_test.rb +66 -0
  132. data/test/new_relic/control_test.rb +127 -0
  133. data/test/new_relic/deployments_api_test.rb +69 -0
  134. data/test/new_relic/environment_test.rb +75 -0
  135. data/test/new_relic/metric_parser_test.rb +226 -0
  136. data/test/new_relic/metric_spec_test.rb +177 -0
  137. data/test/new_relic/rack/episodes_test.rb +318 -0
  138. data/test/new_relic/shim_agent_test.rb +9 -0
  139. data/test/new_relic/stats_test.rb +312 -0
  140. data/test/new_relic/version_number_test.rb +89 -0
  141. data/test/test_contexts.rb +28 -0
  142. data/test/test_helper.rb +72 -0
  143. data/ui/helpers/developer_mode_helper.rb +359 -0
  144. data/ui/helpers/google_pie_chart.rb +49 -0
  145. data/ui/views/layouts/newrelic_default.rhtml +47 -0
  146. data/ui/views/newrelic/_explain_plans.rhtml +27 -0
  147. data/ui/views/newrelic/_sample.rhtml +20 -0
  148. data/ui/views/newrelic/_segment.rhtml +29 -0
  149. data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
  150. data/ui/views/newrelic/_segment_row.rhtml +14 -0
  151. data/ui/views/newrelic/_show_sample_detail.rhtml +24 -0
  152. data/ui/views/newrelic/_show_sample_sql.rhtml +20 -0
  153. data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
  154. data/ui/views/newrelic/_sql_row.rhtml +16 -0
  155. data/ui/views/newrelic/_stack_trace.rhtml +15 -0
  156. data/ui/views/newrelic/_table.rhtml +12 -0
  157. data/ui/views/newrelic/explain_sql.rhtml +43 -0
  158. data/ui/views/newrelic/file/images/arrow-close.png +0 -0
  159. data/ui/views/newrelic/file/images/arrow-open.png +0 -0
  160. data/ui/views/newrelic/file/images/blue_bar.gif +0 -0
  161. data/ui/views/newrelic/file/images/file_icon.png +0 -0
  162. data/ui/views/newrelic/file/images/gray_bar.gif +0 -0
  163. data/ui/views/newrelic/file/images/new-relic-rpm-desktop.gif +0 -0
  164. data/ui/views/newrelic/file/images/new_relic_rpm_desktop.gif +0 -0
  165. data/ui/views/newrelic/file/images/textmate.png +0 -0
  166. data/ui/views/newrelic/file/javascript/jquery-1.4.2.js +6240 -0
  167. data/ui/views/newrelic/file/javascript/transaction_sample.js +120 -0
  168. data/ui/views/newrelic/file/stylesheets/style.css +484 -0
  169. data/ui/views/newrelic/index.rhtml +59 -0
  170. data/ui/views/newrelic/sample_not_found.rhtml +2 -0
  171. data/ui/views/newrelic/show_sample.rhtml +79 -0
  172. data/ui/views/newrelic/show_source.rhtml +3 -0
  173. data/ui/views/newrelic/threads.rhtml +52 -0
  174. metadata +307 -0
@@ -0,0 +1,50 @@
1
+ class NewRelic::MetricParser::External < NewRelic::MetricParser
2
+
3
+ def all?
4
+ host == 'all' || host == 'allWeb' || host == 'allOther'
5
+ end
6
+ def hosts_all?
7
+ library == 'all'
8
+ end
9
+ def host
10
+ segments[1]
11
+ end
12
+ def library
13
+ segments[2]
14
+ end
15
+ def operation
16
+ segments[3] && segments[3..-1].join("/")
17
+ end
18
+ def legend_name
19
+ case
20
+ when all?
21
+ "External Services"
22
+ when hosts_all?
23
+ "All #{host} calls"
24
+ else
25
+ developer_name
26
+ end
27
+ end
28
+ def tooltip_name
29
+ case
30
+ when all?
31
+ "calls to external systems"
32
+ when hosts_all?
33
+ "calls to #{host}"
34
+ else
35
+ "calls to #{developer_name}"
36
+ end
37
+ end
38
+ def developer_name
39
+ case
40
+ when all?
41
+ 'All External'
42
+ when hosts_all?
43
+ host
44
+ when operation
45
+ "#{library}[#{host}]: #{operation}"
46
+ else
47
+ "#{library}[#{host}]"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,50 @@
1
+ class NewRelic::MetricParser::MemCache < NewRelic::MetricParser
2
+ def is_memcache?; true; end
3
+
4
+ # for MemCache metrics, the short name is actually
5
+ # the full name
6
+ def short_name
7
+ name
8
+ end
9
+ def developer_name
10
+ "MemCache #{segments[1..-1].join '/'}"
11
+ end
12
+
13
+ def all?
14
+ segments[1].index('all') == 0
15
+ end
16
+ def operation
17
+ all? ? 'All Operations' : segments[1]
18
+ end
19
+ def legend_name
20
+ case segments[1]
21
+ when 'allWeb'
22
+ "MemCache"
23
+ when 'allOther'
24
+ "Non-web MemCache"
25
+ else
26
+ "MemCache #{operation} operations"
27
+ end
28
+ end
29
+ def tooltip_name
30
+ case segments[1]
31
+ when 'allWeb'
32
+ "MemCache calls from web transactions"
33
+ when 'allOther'
34
+ "MemCache calls from non-web transactions"
35
+ else
36
+ "MemCache #{operation} operations"
37
+ end
38
+
39
+ end
40
+ def developer_name
41
+ case segments[1]
42
+ when 'allWeb'
43
+ "Web MemCache"
44
+ when 'allOther'
45
+ "Non-web MemCache"
46
+ else
47
+ "MemCache #{operation}"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,36 @@
1
+ # OtherTransaction metrics must have at least three segments: /OtherTransaction/<task>/*
2
+ # Task is "Background", "Resque", "DelayedJob" etc.
3
+
4
+ class NewRelic::MetricParser::OtherTransaction < NewRelic::MetricParser
5
+
6
+ def is_transaction?
7
+ true
8
+ end
9
+ def task
10
+ segments[1]
11
+ end
12
+
13
+ def developer_name
14
+ segments[2..-1].join(NewRelic::MetricParser::SEPARATOR)
15
+ end
16
+
17
+ def short_name
18
+ developer_name
19
+ end
20
+
21
+ def drilldown_url(metric_id)
22
+ {:controller => '/v2/background_tasks', :action => 'index', :task => task, :anchor => "id=#{metric_id}"}
23
+ end
24
+
25
+ def path
26
+ segments[2..-1].join "/"
27
+ end
28
+
29
+ def summary_metrics
30
+ if segments.size > 2
31
+ %W[OtherTransaction/#{task}/all OtherTransaction/all]
32
+ else
33
+ []
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,61 @@
1
+ class NewRelic::MetricParser::View < NewRelic::MetricParser
2
+ def is_view?; true; end
3
+
4
+ def is_render?
5
+ segments.last == "Rendering"
6
+ end
7
+ def is_compiler?
8
+ segments.last == "Compile"
9
+ end
10
+ def pie_chart_label
11
+ case segments.last
12
+ when "Rendering"
13
+ "#{file_name(segments[-2])} Template"
14
+ when "Partial"
15
+ "#{file_name(segments[-2])} Partial"
16
+ when ".rhtml Processing"
17
+ "ERB compilation"
18
+ else
19
+ segments[1..-1]
20
+ end
21
+ end
22
+ def template_label
23
+ case segments.last
24
+ when "Rendering"
25
+ "#{file_name(segments[1..-2].join(NewRelic::MetricParser::SEPARATOR))} Template"
26
+ when "Partial"
27
+ "#{file_name(segments[1..-2].join(NewRelic::MetricParser::SEPARATOR))} Partial"
28
+ when ".rhtml Processing"
29
+ "ERB compilation"
30
+ else
31
+ segments[1..-1].join("/")
32
+ end
33
+ end
34
+
35
+ def short_name
36
+ segments[1..-2].join(NewRelic::MetricParser::SEPARATOR)
37
+ end
38
+
39
+ def controller_name
40
+ template_label
41
+ end
42
+
43
+ def action_name
44
+ # Strip the extension
45
+ segments[-2].gsub(/\..*$/, "")
46
+ end
47
+
48
+ def developer_name
49
+ template_label
50
+ end
51
+
52
+ def url
53
+ '/' + file_name(segments[1..-2].join('/'))
54
+ end
55
+ private
56
+ def file_name(path)
57
+ label = path.gsub /\.html\.rhtml/, '.rhtml'
58
+ label = segments[1] if label.empty?
59
+ label
60
+ end
61
+ end
@@ -0,0 +1,14 @@
1
+ # The metric where the mongrel queue time is stored
2
+
3
+ class NewRelic::MetricParser::WebFrontend < NewRelic::MetricParser
4
+ def short_name
5
+ if segments.last == 'Average Queue Time'
6
+ 'Mongrel Average Queue Time'
7
+ else
8
+ super
9
+ end
10
+ end
11
+ def legend_name
12
+ 'Mongrel Wait'
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ class NewRelic::MetricParser::WebService < NewRelic::MetricParser
2
+ def is_web_service?
3
+ segments[1] != 'Soap' && segments[1] != 'Xml Rpc'
4
+ end
5
+
6
+ def webservice_call_rate_suffix
7
+ 'rpm'
8
+ end
9
+ end
@@ -0,0 +1,67 @@
1
+ # this struct uniquely defines a metric, optionally inside
2
+ # the call scope of another metric
3
+ class NewRelic::MetricSpec
4
+ attr_accessor :name
5
+ attr_accessor :scope
6
+
7
+ MAX_LENGTH = 255
8
+ # Need a "zero-arg" constructor so it can be instantiated from java (using
9
+ # jruby) for sending responses to ruby agents from the java collector.
10
+ #
11
+ def initialize(metric_name = '', metric_scope = '')
12
+ self.name = (metric_name || '') && metric_name[0...MAX_LENGTH]
13
+ self.scope = metric_scope && metric_scope[0...MAX_LENGTH]
14
+ end
15
+
16
+ def truncate!
17
+ self.name = name[0...MAX_LENGTH] if name && name.size > MAX_LENGTH
18
+ self.scope = scope[0...MAX_LENGTH] if scope && scope.size > MAX_LENGTH
19
+ end
20
+
21
+ def ==(o)
22
+ self.eql?(o)
23
+ end
24
+
25
+ def eql? o
26
+ self.class == o.class &&
27
+ name.eql?(o.name) &&
28
+ # coerce scope to a string and compare
29
+ (scope || '') == (o.scope || '')
30
+ end
31
+
32
+ def hash
33
+ h = name.hash
34
+ h ^= scope.hash unless scope.nil?
35
+ h
36
+ end
37
+ # return a new metric spec if the given regex
38
+ # matches the name or scope.
39
+ def sub(pattern, replacement, apply_to_scope = true)
40
+ return nil if name !~ pattern &&
41
+ (!apply_to_scope || scope.nil? || scope !~ pattern)
42
+ new_name = name.sub(pattern, replacement)[0...MAX_LENGTH]
43
+
44
+ if apply_to_scope
45
+ new_scope = (scope && scope.sub(pattern, replacement)[0...MAX_LENGTH])
46
+ else
47
+ new_scope = scope
48
+ end
49
+
50
+ self.class.new new_name, new_scope
51
+ end
52
+
53
+ def to_s
54
+ "#{name}:#{scope}"
55
+ end
56
+
57
+ def to_json(*a)
58
+ {'name' => name,
59
+ 'scope' => scope}.to_json(*a)
60
+ end
61
+
62
+ def <=>(o)
63
+ namecmp = self.name <=> o.name
64
+ return namecmp if namecmp != 0
65
+ return (self.scope || '') <=> (o.scope || '')
66
+ end
67
+ end
@@ -0,0 +1,9 @@
1
+ module NewRelic
2
+ module Metrics
3
+ CONTROLLER = "Controller"
4
+ DISPATCHER = "HttpDispatcher"
5
+ ACTIVE_RECORD = "ActiveRecord"
6
+ USER_TIME = "CPU/User Time"
7
+ MEMORY = "Memory/Physical"
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ # This class encapsulates an error that was noticed by RPM in a managed app.
2
+ class NewRelic::NoticedError
3
+ extend NewRelic::CollectionHelper
4
+ attr_accessor :path, :timestamp, :params, :exception_class, :message
5
+
6
+ def initialize(path, data, exception, timestamp = Time.now)
7
+ self.path = path
8
+ self.params = NewRelic::NoticedError.normalize_params(data)
9
+
10
+ self.exception_class = exception.is_a?(Exception) ? exception.class.name : 'Error'
11
+
12
+ if exception.respond_to?('original_exception')
13
+ self.message = exception.original_exception.message.to_s
14
+ else
15
+ self.message = (exception || '<no message>').to_s
16
+ end
17
+
18
+ # clamp long messages to 4k so that we don't send a lot of
19
+ # overhead across the wire
20
+ self.message = self.message[0..4095] if self.message.length > 4096
21
+
22
+ self.timestamp = timestamp
23
+ end
24
+ end
@@ -0,0 +1,257 @@
1
+ require 'rack'
2
+ require 'rack/request'
3
+ require 'rack/response'
4
+ require 'rack/file'
5
+
6
+ module NewRelic::Rack
7
+ class DeveloperMode
8
+
9
+ VIEW_PATH = File.expand_path('../../../../ui/views/', __FILE__)
10
+ HELPER_PATH = File.expand_path('../../../../ui/helpers/', __FILE__)
11
+ require File.join(HELPER_PATH, 'developer_mode_helper.rb')
12
+
13
+ include NewRelic::DeveloperModeHelper
14
+
15
+ def initialize(app)
16
+ @app = app
17
+ end
18
+
19
+ def call(env)
20
+ return @app.call(env) unless /^\/newrelic/ =~ Rack::Request.new(env).path_info
21
+ dup._call(env)
22
+ end
23
+
24
+ protected
25
+
26
+ def _call(env)
27
+ @req = Rack::Request.new(env)
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
56
+
57
+ private
58
+
59
+ def index
60
+ get_samples
61
+ render(:index)
62
+ end
63
+
64
+ def reset
65
+ NewRelic::Agent.instance.transaction_sampler.reset!
66
+ Rack::Response.new{|r| r.redirect('/newrelic/')}.finish
67
+ end
68
+
69
+ def explain_sql
70
+ get_segment
71
+
72
+ return render(:sample_not_found) unless @sample
73
+
74
+ @sql = @segment[:sql]
75
+ @trace = @segment[:backtrace]
76
+
77
+ if NewRelic::Agent.agent.record_sql == :obfuscated
78
+ @obfuscated_sql = @segment.obfuscated_sql
79
+ end
80
+
81
+ explanations = @segment.explain_sql
82
+ if explanations
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
92
+ 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
+
103
+ def threads
104
+ render(:threads)
105
+ end
106
+
107
+ def render(view, layout=true)
108
+ add_rack_array = true
109
+ if view.is_a? Hash
110
+ layout = false
111
+ if view[:object]
112
+ object = view[:object]
113
+ end
114
+
115
+ if view[:collection]
116
+ return view[:collection].map do |object|
117
+ render({:partial => view[:partial], :object => object})
118
+ end.join(' ')
119
+ end
120
+
121
+ if view[:partial]
122
+ add_rack_array = false
123
+ view = "_#{view[:partial]}"
124
+ end
125
+ end
126
+ binding = Proc.new {}.binding
127
+ if layout
128
+ body = render_with_layout(view) do
129
+ render_without_layout(view, binding)
130
+ 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
+
141
+ # You have to call this with a block - the contents returned from
142
+ # that block are interpolated into the layout
143
+ def render_with_layout(view)
144
+ body = ERB.new(File.read(File.join(VIEW_PATH, 'layouts/newrelic_default.rhtml')))
145
+ body.result(Proc.new {}.binding)
146
+ end
147
+
148
+ # you have to pass a binding to this (a proc) so that ERB can have
149
+ # access to helper functions and local variables
150
+ def render_without_layout(view, binding)
151
+ ERB.new(File.read(File.join(VIEW_PATH, 'newrelic', view.to_s + '.rhtml')), nil, nil, 'frobnitz').result(binding)
152
+ end
153
+
154
+ def content_tag(tag, contents, opts={})
155
+ opt_values = opts.map {|k, v| "#{k}=\"#{v}\"" }.join(' ')
156
+ "<#{tag} #{opt_values}>#{contents}</#{tag}>"
157
+ end
158
+
159
+ def sample
160
+ @sample || @samples[0]
161
+ end
162
+
163
+ def params
164
+ @req.params
165
+ end
166
+
167
+ def segment
168
+ @segment
169
+ end
170
+
171
+
172
+ # show the selected source file with the highlighted selected line
173
+ def show_source
174
+ @filename = params['file']
175
+ line_number = params['line'].to_i
176
+
177
+ if !File.readable?(@filename)
178
+ @source="<p>Unable to read #{@filename}.</p>"
179
+ return
180
+ end
181
+ begin
182
+ file = File.new(@filename, 'r')
183
+ rescue => e
184
+ @source="<p>Unable to access the source file #{@filename} (#{e.message}).</p>"
185
+ return
186
+ end
187
+ @source = ""
188
+
189
+ @source << "<pre>"
190
+ file.each_line do |line|
191
+ # place an anchor 6 lines above the selected line (if the line # < 6)
192
+ if file.lineno == line_number - 6
193
+ @source << "</pre><pre id = 'selected_line'>"
194
+ @source << line.rstrip
195
+ @source << "</pre><pre>"
196
+
197
+ # highlight the selected line
198
+ elsif file.lineno == line_number
199
+ @source << "</pre><pre class = 'selected_source_line'>"
200
+ @source << line.rstrip
201
+ @source << "</pre><pre>"
202
+ else
203
+ @source << line
204
+ end
205
+ end
206
+ render(:show_source)
207
+ end
208
+
209
+ def show_sample_data
210
+ get_sample
211
+
212
+ return render(:sample_not_found) unless @sample
213
+
214
+ @request_params = @sample.params['request_params'] || {}
215
+ @custom_params = @sample.params['custom_params'] || {}
216
+
217
+ controller_metric = @sample.root_segment.called_segments.first.metric_name
218
+
219
+ metric_parser = NewRelic::MetricParser.for_metric_named controller_metric
220
+ @sample_controller_name = metric_parser.controller_name
221
+ @sample_action_name = metric_parser.action_name
222
+
223
+ render(:show_sample)
224
+ end
225
+
226
+ def get_samples
227
+ @samples = NewRelic::Agent.instance.transaction_sampler.samples.select do |sample|
228
+ sample.params[:path] != nil
229
+ end
230
+
231
+ return @samples = @samples.sort{|x,y| y.omit_segments_with('(Rails/Application Code Loading)|(Database/.*/.+ Columns)').duration <=>
232
+ x.omit_segments_with('(Rails/Application Code Loading)|(Database/.*/.+ Columns)').duration} if params['h']
233
+ return @samples = @samples.sort{|x,y| x.params[:uri] <=> y.params[:uri]} if params['u']
234
+ @samples = @samples.reverse
235
+ end
236
+
237
+ def get_sample
238
+ get_samples
239
+ id = params['id']
240
+ sample_id = id.to_i
241
+ @samples.each do |s|
242
+ if s.sample_id == sample_id
243
+ @sample = stripped_sample(s)
244
+ return
245
+ end
246
+ end
247
+ end
248
+
249
+ def get_segment
250
+ get_sample
251
+ return unless @sample
252
+
253
+ segment_id = params['segment'].to_i
254
+ @segment = @sample.find_segment(segment_id)
255
+ end
256
+ end
257
+ end