newrelic_rpm 2.12.3 → 2.13.0.beta3

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 (118) hide show
  1. data/CHANGELOG +24 -2
  2. data/README.rdoc +172 -0
  3. data/bin/newrelic +13 -0
  4. data/bin/newrelic_cmd +2 -1
  5. data/install.rb +8 -45
  6. data/lib/new_relic/agent.rb +43 -30
  7. data/lib/new_relic/agent/agent.rb +699 -631
  8. data/lib/new_relic/agent/busy_calculator.rb +81 -81
  9. data/lib/new_relic/agent/error_collector.rb +9 -6
  10. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +2 -2
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +10 -5
  12. data/lib/new_relic/agent/instrumentation/data_mapper.rb +13 -45
  13. data/lib/new_relic/agent/instrumentation/memcache.rb +15 -4
  14. data/lib/new_relic/agent/instrumentation/metric_frame.rb +37 -29
  15. data/lib/new_relic/agent/instrumentation/rack.rb +20 -34
  16. data/lib/new_relic/agent/method_tracer.rb +9 -9
  17. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +31 -31
  18. data/lib/new_relic/agent/stats_engine/metric_stats.rb +5 -5
  19. data/lib/new_relic/agent/stats_engine/samplers.rb +3 -0
  20. data/lib/new_relic/agent/transaction_sampler.rb +31 -15
  21. data/lib/new_relic/agent/worker_loop.rb +29 -28
  22. data/lib/new_relic/collection_helper.rb +4 -2
  23. data/lib/new_relic/command.rb +85 -0
  24. data/lib/new_relic/commands/deployments.rb +74 -114
  25. data/lib/new_relic/commands/install.rb +81 -0
  26. data/lib/new_relic/control.rb +54 -379
  27. data/lib/new_relic/control/configuration.rb +149 -0
  28. data/lib/new_relic/control/{external.rb → frameworks/external.rb} +2 -2
  29. data/lib/new_relic/control/{merb.rb → frameworks/merb.rb} +1 -1
  30. data/lib/new_relic/control/frameworks/rails.rb +126 -0
  31. data/lib/new_relic/control/{rails3.rb → frameworks/rails3.rb} +11 -10
  32. data/lib/new_relic/control/{ruby.rb → frameworks/ruby.rb} +1 -1
  33. data/lib/new_relic/control/{sinatra.rb → frameworks/sinatra.rb} +2 -2
  34. data/lib/new_relic/control/instrumentation.rb +84 -0
  35. data/lib/new_relic/control/logging_methods.rb +74 -0
  36. data/lib/new_relic/control/profiling.rb +24 -0
  37. data/lib/new_relic/control/server_methods.rb +88 -0
  38. data/lib/new_relic/local_environment.rb +3 -3
  39. data/lib/new_relic/metric_parser.rb +13 -2
  40. data/lib/new_relic/metric_parser/active_record.rb +4 -1
  41. data/lib/new_relic/metric_parser/apdex.rb +53 -0
  42. data/lib/new_relic/metric_parser/controller.rb +13 -5
  43. data/lib/new_relic/metric_parser/mem_cache.rb +1 -1
  44. data/lib/new_relic/metric_parser/other_transaction.rb +21 -0
  45. data/lib/new_relic/metric_spec.rb +3 -3
  46. data/lib/new_relic/noticed_error.rb +1 -1
  47. data/lib/new_relic/rack/developer_mode.rb +257 -0
  48. data/lib/new_relic/rack/metric_app.rb +56 -50
  49. data/lib/new_relic/rack/mongrel_rpm.ru +7 -6
  50. data/lib/new_relic/rack/newrelic.yml +4 -3
  51. data/lib/new_relic/rack_app.rb +2 -1
  52. data/lib/new_relic/recipes.rb +7 -7
  53. data/lib/new_relic/stats.rb +6 -14
  54. data/lib/new_relic/timer_lib.rb +27 -0
  55. data/lib/new_relic/transaction_analysis.rb +2 -7
  56. data/lib/new_relic/transaction_sample.rb +17 -85
  57. data/lib/new_relic/url_rule.rb +14 -0
  58. data/lib/new_relic/version.rb +3 -3
  59. data/lib/newrelic_rpm.rb +5 -9
  60. data/newrelic.yml +26 -9
  61. data/newrelic_rpm.gemspec +67 -32
  62. data/test/config/newrelic.yml +5 -0
  63. data/test/config/test_control.rb +6 -8
  64. data/test/new_relic/agent/active_record_instrumentation_test.rb +5 -6
  65. data/test/new_relic/agent/agent_controller_test.rb +18 -4
  66. data/test/new_relic/agent/agent_test_controller.rb +1 -6
  67. data/test/new_relic/agent/busy_calculator_test.rb +2 -0
  68. data/test/new_relic/agent/collection_helper_test.rb +6 -6
  69. data/test/new_relic/agent/error_collector_test.rb +13 -21
  70. data/test/new_relic/agent/metric_data_test.rb +3 -6
  71. data/test/new_relic/agent/rpm_agent_test.rb +121 -117
  72. data/test/new_relic/agent/task_instrumentation_test.rb +128 -133
  73. data/test/new_relic/agent/transaction_sample_test.rb +176 -170
  74. data/test/new_relic/agent/worker_loop_test.rb +24 -18
  75. data/test/new_relic/control_test.rb +13 -3
  76. data/test/new_relic/deployments_api_test.rb +7 -7
  77. data/test/new_relic/environment_test.rb +1 -1
  78. data/test/new_relic/metric_parser_test.rb +58 -4
  79. data/test/new_relic/rack/episodes_test.rb +317 -0
  80. data/test/new_relic/stats_test.rb +3 -2
  81. data/test/test_contexts.rb +28 -0
  82. data/test/test_helper.rb +24 -5
  83. data/ui/helpers/{newrelic_helper.rb → developer_mode_helper.rb} +63 -23
  84. data/ui/views/layouts/newrelic_default.rhtml +6 -6
  85. data/ui/views/newrelic/_explain_plans.rhtml +4 -4
  86. data/ui/views/newrelic/_sample.rhtml +18 -17
  87. data/ui/views/newrelic/_segment.rhtml +1 -0
  88. data/ui/views/newrelic/_segment_row.rhtml +8 -8
  89. data/ui/views/newrelic/_show_sample_detail.rhtml +1 -1
  90. data/ui/views/newrelic/_show_sample_sql.rhtml +2 -2
  91. data/ui/views/newrelic/_sql_row.rhtml +8 -3
  92. data/ui/views/newrelic/_stack_trace.rhtml +9 -24
  93. data/ui/views/newrelic/_table.rhtml +1 -1
  94. data/ui/views/newrelic/explain_sql.rhtml +3 -2
  95. data/ui/views/newrelic/{images → file/images}/arrow-close.png +0 -0
  96. data/ui/views/newrelic/{images → file/images}/arrow-open.png +0 -0
  97. data/ui/views/newrelic/{images → file/images}/blue_bar.gif +0 -0
  98. data/ui/views/newrelic/{images → file/images}/file_icon.png +0 -0
  99. data/ui/views/newrelic/{images → file/images}/gray_bar.gif +0 -0
  100. data/ui/views/newrelic/{images → file/images}/new-relic-rpm-desktop.gif +0 -0
  101. data/ui/views/newrelic/{images → file/images}/new_relic_rpm_desktop.gif +0 -0
  102. data/ui/views/newrelic/{images → file/images}/textmate.png +0 -0
  103. data/ui/views/newrelic/file/javascript/jquery-1.4.2.js +6240 -0
  104. data/ui/views/newrelic/file/javascript/transaction_sample.js +120 -0
  105. data/ui/views/newrelic/{stylesheets → file/stylesheets}/style.css +0 -0
  106. data/ui/views/newrelic/index.rhtml +7 -5
  107. data/ui/views/newrelic/sample_not_found.rhtml +1 -1
  108. data/ui/views/newrelic/show_sample.rhtml +5 -6
  109. metadata +92 -34
  110. data/README.md +0 -138
  111. data/lib/new_relic/commands/new_relic_commands.rb +0 -30
  112. data/lib/new_relic/control/rails.rb +0 -151
  113. data/test/new_relic/agent/mock_ar_connection.rb +0 -40
  114. data/test/ui/newrelic_controller_test.rb +0 -14
  115. data/test/ui/newrelic_helper_test.rb +0 -53
  116. data/ui/controllers/newrelic_controller.rb +0 -220
  117. data/ui/views/newrelic/javascript/prototype-scriptaculous.js +0 -7288
  118. data/ui/views/newrelic/javascript/transaction_sample.js +0 -107
@@ -0,0 +1,24 @@
1
+ module NewRelic
2
+ class Control
3
+ module Profiling
4
+
5
+ # A flag used in dev mode to indicate if profiling is available
6
+ def profiling?
7
+ @profiling
8
+ end
9
+
10
+ def profiling_available?
11
+ @profiling_available ||=
12
+ begin
13
+ require 'ruby-prof'
14
+ true
15
+ rescue LoadError; end
16
+ end
17
+ # Set the flag for capturing profiles in dev mode. If RubyProf is not
18
+ # loaded a true value is ignored.
19
+ def profiling=(val)
20
+ @profiling = profiling_available? && val && defined?(RubyProf)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,88 @@
1
+ module NewRelic
2
+ class Control
3
+
4
+ # Structs holding info for the remote server and proxy server
5
+ class Server < Struct.new :name, :port, :ip #:nodoc:
6
+ def to_s; "#{name}:#{port}"; end
7
+ end
8
+
9
+ ProxyServer = Struct.new :name, :port, :user, :password #:nodoc:
10
+
11
+ module ServerMethods
12
+
13
+ def server
14
+ @remote_server ||= server_from_host(nil)
15
+ end
16
+
17
+ def api_server
18
+ api_host = self['api_host'] || 'rpm.newrelic.com'
19
+ @api_server ||=
20
+ NewRelic::Control::Server.new \
21
+ api_host,
22
+ (self['api_port'] || self['port'] || (use_ssl? ? 443 : 80)).to_i,
23
+ nil
24
+ end
25
+
26
+ def proxy_server
27
+ @proxy_server ||=
28
+ NewRelic::Control::ProxyServer.new self['proxy_host'], self['proxy_port'], self['proxy_user'], self['proxy_pass']
29
+ end
30
+
31
+ def server_from_host(hostname=nil)
32
+ host = hostname || self['host'] || 'collector.newrelic.com'
33
+
34
+ # if the host is not an IP address, turn it into one
35
+ NewRelic::Control::Server.new host, (self['port'] || (use_ssl? ? 443 : 80)).to_i, convert_to_ip_address(host)
36
+ end
37
+
38
+ # Look up the ip address of the host using the pure ruby lookup
39
+ # to prevent blocking. If that fails, fall back to the regular
40
+ # IPSocket library. Return nil if we can't find the host ip
41
+ # address and don't have a good default.
42
+ def convert_to_ip_address(host)
43
+ # here we leave it as a host name since the cert verification
44
+ # needs it in host form
45
+ return host if verify_certificate?
46
+ return nil if host.nil? || host.downcase == "localhost"
47
+ # Fall back to known ip address in the common case
48
+ ip_address = '65.74.177.195' if host.downcase == 'collector.newrelic.com'
49
+ begin
50
+ ip_address = Resolv.getaddress(host)
51
+ log.info "Resolved #{host} to #{ip_address}"
52
+ rescue => e
53
+ log.warn "DNS Error caching IP address: #{e}"
54
+ log.debug e.backtrace.join("\n ")
55
+ ip_address = IPSocket::getaddress host rescue ip_address
56
+ end
57
+ ip_address
58
+ end
59
+
60
+ # Return the Net::HTTP with proxy configuration given the NewRelic::Control::Server object.
61
+ # Default is the collector but for api calls you need to pass api_server
62
+ #
63
+ # Experimental support for SSL verification:
64
+ # swap 'VERIFY_NONE' for 'VERIFY_PEER' line to try it out
65
+ # If verification fails, uncomment the 'http.ca_file' line
66
+ # and it will use the included certificate.
67
+ def http_connection(host = nil)
68
+ host ||= server
69
+ # Proxy returns regular HTTP if @proxy_host is nil (the default)
70
+ http_class = Net::HTTP::Proxy(proxy_server.name, proxy_server.port,
71
+ proxy_server.user, proxy_server.password)
72
+ http = http_class.new(host.ip || host.name, host.port)
73
+ log.debug("Http Connection opened to #{host.ip||host.name}:#{host.port}")
74
+ if use_ssl?
75
+ http.use_ssl = true
76
+ if verify_certificate?
77
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
78
+ http.ca_file = File.join(File.dirname(__FILE__), '..', '..', 'cert', 'cacert.pem')
79
+ else
80
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
81
+ end
82
+ end
83
+ http
84
+ end
85
+ end
86
+ end
87
+ end
88
+
@@ -152,10 +152,10 @@ module NewRelic
152
152
  @unicorn
153
153
  end
154
154
 
155
- # Obsolete method for DelayedJob instrumentation support, which is
156
- # now in the rpm_contrib gem.
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
157
  def delayed_worker=(worker)
158
- $stderr.puts "WARNING: DelayedJob instrumentation has been moved to the rpm_contrib gem."
158
+ $stderr.puts "WARNING: obsolete call to delayed_worker=(worker). Please remove custom DJ instrumentation."
159
159
  end
160
160
 
161
161
  private
@@ -67,7 +67,7 @@ module NewRelic
67
67
  end
68
68
 
69
69
  def apdex_metric_path
70
- "Apdex/#{segments[1..-1].join('/')}"
70
+ %Q[Apdex/#{segments[1..-1].join('/')}]
71
71
  end
72
72
 
73
73
  # A short name for legends in the graphs
@@ -81,6 +81,8 @@ module NewRelic
81
81
  nil
82
82
  end
83
83
 
84
+ # Category is a UI description of the general
85
+ # category of metrics for this metric.
84
86
  def category
85
87
  segments[0]
86
88
  end
@@ -112,7 +114,11 @@ module NewRelic
112
114
  def url
113
115
  ''
114
116
  end
115
-
117
+ # Return the list of dispatcher metrics that correspond to this metric. That is,
118
+ # the summary metrics which should also be recorded when this metric is recorded.
119
+ def summary_metrics
120
+ []
121
+ end
116
122
  # returns a hash of params for url_for(), giving you a drilldown URL to an RPM page for this metric
117
123
  # define in subclasses - TB 2009-12-18
118
124
  # def drilldown_url(metric_id); end
@@ -121,5 +127,10 @@ module NewRelic
121
127
  @name = name
122
128
  end
123
129
 
130
+ # These would be reflected properly by method missing; consider
131
+ # this an optimization
132
+ def is_controller?; false; end
133
+ def is_transaction?; false; end
134
+
124
135
  end
125
136
  end
@@ -4,7 +4,10 @@ class NewRelic::MetricParser::ActiveRecord < NewRelic::MetricParser
4
4
  def model_class
5
5
  return segments[1]
6
6
  end
7
-
7
+
8
+ def is_database?
9
+ true
10
+ end
8
11
  def legend_name
9
12
  if name == 'ActiveRecord/all'
10
13
  'Database'
@@ -0,0 +1,53 @@
1
+ class NewRelic::MetricParser::Apdex < NewRelic::MetricParser
2
+
3
+ CLIENT = 'Client'
4
+
5
+ # Convenience method for creating the appropriate client
6
+ # metric name.
7
+ def self.client_metric(apdex_t)
8
+ "Apdex/#{CLIENT}/#{apdex_t}"
9
+ end
10
+
11
+ def is_client?
12
+ segments[1] == CLIENT
13
+ end
14
+ def is_summary?
15
+ segments.size == 1
16
+ end
17
+
18
+ # Apdex/Client/N
19
+ def apdex_t
20
+ is_client? && segments[2].to_f
21
+ end
22
+
23
+ def developer_name
24
+ case
25
+ when is_client? then "Apdex Client (#{apdex_t})"
26
+ when is_summary? then "Apdex"
27
+ else "Apdex #{segments[1..-1].join("/")}"
28
+ end
29
+ end
30
+
31
+ def short_name
32
+ # standard controller actions
33
+ if segments.length > 1
34
+ url
35
+ else
36
+ 'All Frontend Urls'
37
+ end
38
+ end
39
+
40
+ def url
41
+ '/' + segments[1..-1].join('/')
42
+ end
43
+
44
+ # this is used to match transaction traces to controller actions.
45
+ # TT's don't have a preceding slash :P
46
+ def tt_path
47
+ segments[1..-1].join('/')
48
+ end
49
+
50
+ def call_rate_suffix
51
+ 'rpm'
52
+ end
53
+ end
@@ -1,11 +1,12 @@
1
1
  class NewRelic::MetricParser::Controller < NewRelic::MetricParser
2
2
 
3
- def is_controller?
4
- true
5
- end
6
-
3
+ def is_controller?; true; end
4
+ def is_transaction?; true; end
5
+
7
6
  # If the controller name segments look like a file path, convert it to the controller
8
- # class name. If it begins with a capital letter, assume it's already a class name
7
+ # class name. If it begins with a capital letter, assume it's already a class name.
8
+ # We only expect a lower case letter with Rails, so we'll be able to use camelize for
9
+ # that.
9
10
  def controller_name
10
11
  path = segments[1..-2].join('/')
11
12
  path < 'a' ? path : path.camelize+"Controller"
@@ -23,6 +24,9 @@ class NewRelic::MetricParser::Controller < NewRelic::MetricParser
23
24
  "#{controller_name}##{action_name}"
24
25
  end
25
26
 
27
+ def is_web_transaction?
28
+ true
29
+ end
26
30
  # return the cpu measuring equivalent. It may be nil since this metric was not
27
31
  # present in earlier versions of the agent.
28
32
  def cpu_metric
@@ -51,4 +55,8 @@ class NewRelic::MetricParser::Controller < NewRelic::MetricParser
51
55
  def call_rate_suffix
52
56
  'rpm'
53
57
  end
58
+
59
+ def summary_metrics
60
+ %w[HttpDispatcher]
61
+ end
54
62
  end
@@ -11,7 +11,7 @@ class NewRelic::MetricParser::MemCache < NewRelic::MetricParser
11
11
  end
12
12
 
13
13
  def all?
14
- segments[1]['all'] == 0
14
+ segments[1].index('all') == 0
15
15
  end
16
16
  def operation
17
17
  all? ? 'All Operations' : segments[1]
@@ -1,6 +1,11 @@
1
1
  # OtherTransaction metrics must have at least three segments: /OtherTransaction/<task>/*
2
+ # Task is "Background", "Resque", "DelayedJob" etc.
2
3
 
3
4
  class NewRelic::MetricParser::OtherTransaction < NewRelic::MetricParser
5
+
6
+ def is_transaction?
7
+ true
8
+ end
4
9
  def task
5
10
  segments[1]
6
11
  end
@@ -9,7 +14,23 @@ class NewRelic::MetricParser::OtherTransaction < NewRelic::MetricParser
9
14
  segments[2..-1].join(NewRelic::MetricParser::SEPARATOR)
10
15
  end
11
16
 
17
+ def short_name
18
+ developer_name
19
+ end
20
+
12
21
  def drilldown_url(metric_id)
13
22
  {:controller => '/v2/background_tasks', :action => 'index', :task => task, :anchor => "id=#{metric_id}"}
14
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
15
36
  end
@@ -4,7 +4,7 @@ class NewRelic::MetricSpec
4
4
  attr_accessor :name
5
5
  attr_accessor :scope
6
6
 
7
- MAX_LENGTH = 100
7
+ MAX_LENGTH = 255
8
8
  # Need a "zero-arg" constructor so it can be instantiated from java (using
9
9
  # jruby) for sending responses to ruby agents from the java collector.
10
10
  #
@@ -14,8 +14,8 @@ class NewRelic::MetricSpec
14
14
  end
15
15
 
16
16
  def truncate!
17
- self.name = name[0...100] if name && name.size > 100
18
- self.scope = scope[0...100] if scope && scope.size > 100
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
19
  end
20
20
 
21
21
  def ==(o)
@@ -7,7 +7,7 @@ class NewRelic::NoticedError
7
7
  self.path = path
8
8
  self.params = NewRelic::NoticedError.normalize_params(data)
9
9
 
10
- self.exception_class = exception ? exception.class.name : '<no exception>'
10
+ self.exception_class = exception.is_a?(Exception) ? exception.class.name : 'Error'
11
11
 
12
12
  if exception.respond_to?('original_exception')
13
13
  self.message = exception.original_exception.message.to_s
@@ -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 {}
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 {})
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