rack-insight 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +1 -0
  3. data/.simplecov +4 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG +58 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +82 -0
  8. data/LICENSE +24 -0
  9. data/README.md +189 -0
  10. data/Rakefile +27 -0
  11. data/TODO +7 -0
  12. data/lib/rack-insight.rb +1 -0
  13. data/lib/rack/insight.rb +19 -0
  14. data/lib/rack/insight/app.rb +198 -0
  15. data/lib/rack/insight/config.rb +30 -0
  16. data/lib/rack/insight/database.rb +193 -0
  17. data/lib/rack/insight/enable-button.rb +43 -0
  18. data/lib/rack/insight/filtered_backtrace.rb +45 -0
  19. data/lib/rack/insight/instrumentation.rb +9 -0
  20. data/lib/rack/insight/instrumentation/backstage.rb +10 -0
  21. data/lib/rack/insight/instrumentation/client.rb +20 -0
  22. data/lib/rack/insight/instrumentation/instrument.rb +109 -0
  23. data/lib/rack/insight/instrumentation/package-definition.rb +58 -0
  24. data/lib/rack/insight/instrumentation/probe-definition.rb +20 -0
  25. data/lib/rack/insight/instrumentation/probe.rb +196 -0
  26. data/lib/rack/insight/instrumentation/setup.rb +32 -0
  27. data/lib/rack/insight/logger.rb +53 -0
  28. data/lib/rack/insight/options.rb +116 -0
  29. data/lib/rack/insight/panel.rb +135 -0
  30. data/lib/rack/insight/panel_app.rb +31 -0
  31. data/lib/rack/insight/panels-content.rb +22 -0
  32. data/lib/rack/insight/panels-header.rb +18 -0
  33. data/lib/rack/insight/panels/active_record_panel.rb +46 -0
  34. data/lib/rack/insight/panels/active_resource_panel.rb +48 -0
  35. data/lib/rack/insight/panels/active_resource_panel/query.rb +27 -0
  36. data/lib/rack/insight/panels/cache_panel.rb +68 -0
  37. data/lib/rack/insight/panels/cache_panel/panel_app.rb +46 -0
  38. data/lib/rack/insight/panels/cache_panel/stats.rb +90 -0
  39. data/lib/rack/insight/panels/log_panel.rb +53 -0
  40. data/lib/rack/insight/panels/memory_panel.rb +36 -0
  41. data/lib/rack/insight/panels/mongo_panel.rb +41 -0
  42. data/lib/rack/insight/panels/mongo_panel/mongo_extension.rb +24 -0
  43. data/lib/rack/insight/panels/mongo_panel/stats.rb +46 -0
  44. data/lib/rack/insight/panels/rails_info_panel.rb +19 -0
  45. data/lib/rack/insight/panels/redis_panel.rb +42 -0
  46. data/lib/rack/insight/panels/redis_panel/redis_extension.rb +23 -0
  47. data/lib/rack/insight/panels/redis_panel/stats.rb +50 -0
  48. data/lib/rack/insight/panels/request_variables_panel.rb +70 -0
  49. data/lib/rack/insight/panels/speedtracer_panel.rb +89 -0
  50. data/lib/rack/insight/panels/speedtracer_panel/profiling.rb +29 -0
  51. data/lib/rack/insight/panels/speedtracer_panel/trace-app.rb +52 -0
  52. data/lib/rack/insight/panels/speedtracer_panel/tracer.rb +213 -0
  53. data/lib/rack/insight/panels/sphinx_panel.rb +41 -0
  54. data/lib/rack/insight/panels/sphinx_panel/stats.rb +94 -0
  55. data/lib/rack/insight/panels/sql_panel.rb +53 -0
  56. data/lib/rack/insight/panels/sql_panel/panel_app.rb +37 -0
  57. data/lib/rack/insight/panels/sql_panel/query.rb +94 -0
  58. data/lib/rack/insight/panels/templates_panel.rb +58 -0
  59. data/lib/rack/insight/panels/templates_panel/rendering.rb +81 -0
  60. data/lib/rack/insight/panels/timer_panel.rb +40 -0
  61. data/lib/rack/insight/params_signature.rb +61 -0
  62. data/lib/rack/insight/path-filter.rb +23 -0
  63. data/lib/rack/insight/public/__insight__/bookmarklet.html +10 -0
  64. data/lib/rack/insight/public/__insight__/bookmarklet.js +223 -0
  65. data/lib/rack/insight/public/__insight__/insight.css +235 -0
  66. data/lib/rack/insight/public/__insight__/insight.js +127 -0
  67. data/lib/rack/insight/public/__insight__/jquery-1.3.2.js +4376 -0
  68. data/lib/rack/insight/public/__insight__/jquery.tablesorter.min.js +1 -0
  69. data/lib/rack/insight/public/__insight__/spinner.gif +0 -0
  70. data/lib/rack/insight/rack_static_bug_avoider.rb +16 -0
  71. data/lib/rack/insight/redirect_interceptor.rb +25 -0
  72. data/lib/rack/insight/render.rb +72 -0
  73. data/lib/rack/insight/request-recorder.rb +22 -0
  74. data/lib/rack/insight/rspec_matchers.rb +33 -0
  75. data/lib/rack/insight/toolbar.rb +69 -0
  76. data/lib/rack/insight/version.rb +7 -0
  77. data/lib/rack/insight/views/enable-button.html.erb +21 -0
  78. data/lib/rack/insight/views/error.html.erb +17 -0
  79. data/lib/rack/insight/views/headers_fragment.html.erb +20 -0
  80. data/lib/rack/insight/views/panels/active_record.html.erb +17 -0
  81. data/lib/rack/insight/views/panels/active_resource.html.erb +47 -0
  82. data/lib/rack/insight/views/panels/cache.html.erb +93 -0
  83. data/lib/rack/insight/views/panels/execute_sql.html.erb +32 -0
  84. data/lib/rack/insight/views/panels/explain_sql.html.erb +32 -0
  85. data/lib/rack/insight/views/panels/log.html.erb +21 -0
  86. data/lib/rack/insight/views/panels/mongo.html.erb +32 -0
  87. data/lib/rack/insight/views/panels/profile_sql.html.erb +32 -0
  88. data/lib/rack/insight/views/panels/rails_info.html.erb +19 -0
  89. data/lib/rack/insight/views/panels/redis.html.erb +46 -0
  90. data/lib/rack/insight/views/panels/request_variables.html.erb +25 -0
  91. data/lib/rack/insight/views/panels/speedtracer/serverevent.html.erb +10 -0
  92. data/lib/rack/insight/views/panels/speedtracer/servertrace.html.erb +12 -0
  93. data/lib/rack/insight/views/panels/speedtracer/traces.html.erb +18 -0
  94. data/lib/rack/insight/views/panels/sphinx.html.erb +32 -0
  95. data/lib/rack/insight/views/panels/sql.html.erb +43 -0
  96. data/lib/rack/insight/views/panels/templates.html.erb +6 -0
  97. data/lib/rack/insight/views/panels/timer.html.erb +19 -0
  98. data/lib/rack/insight/views/panels/view_cache.html.erb +19 -0
  99. data/lib/rack/insight/views/redirect.html.erb +16 -0
  100. data/lib/rack/insight/views/request_fragment.html.erb +25 -0
  101. data/lib/rack/insight/views/toolbar.html.erb +29 -0
  102. data/rack-insight.gemspec +40 -0
  103. data/spec/custom_matchers.rb +0 -0
  104. data/spec/fixtures/config.ru +8 -0
  105. data/spec/fixtures/dummy_panel.rb +2 -0
  106. data/spec/fixtures/sample_app.rb +72 -0
  107. data/spec/fixtures/star_trek_panel.rb +1 -0
  108. data/spec/insight_spec.rb +163 -0
  109. data/spec/instrumentation_spec.rb +188 -0
  110. data/spec/rack/insight/config_spec.rb +20 -0
  111. data/spec/rack/insight/panels/active_record_panel_spec.rb +43 -0
  112. data/spec/rack/insight/panels/active_resource_panel_spec.rb +40 -0
  113. data/spec/rack/insight/panels/cache_panel_spec.rb +178 -0
  114. data/spec/rack/insight/panels/log_panel_spec.rb +44 -0
  115. data/spec/rack/insight/panels/memory_panel_spec.rb +21 -0
  116. data/spec/rack/insight/panels/mongo_panel_spec_pending.rb +52 -0
  117. data/spec/rack/insight/panels/rails_info_panel_spec.rb +29 -0
  118. data/spec/rack/insight/panels/redis_panel_spec.rb +67 -0
  119. data/spec/rack/insight/panels/speedtracer_panel_spec.rb +86 -0
  120. data/spec/rack/insight/panels/sql_panel_spec.rb +146 -0
  121. data/spec/rack/insight/panels/templates_panel_spec.rb +86 -0
  122. data/spec/rack/insight/panels/timer_panel_spec.rb +38 -0
  123. data/spec/rcov.opts +1 -0
  124. data/spec/spec.opts +1 -0
  125. data/spec/spec_helper.rb +111 -0
  126. metadata +380 -0
@@ -0,0 +1,41 @@
1
+ module Rack::Insight
2
+
3
+ class SphinxPanel < Panel
4
+ require "rack/insight/panels/sphinx_panel/sphinx_extension"
5
+ require "rack/insight/panels/sphinx_panel/stats"
6
+
7
+ def self.record(*sphinx_command_args, &block)
8
+ return block.call unless Rack::Insight.enabled?
9
+
10
+ start_time = Time.now
11
+ result = block.call
12
+ total_time = Time.now - start_time
13
+ stats.record_call(total_time * 1_000, sphinx_command_args)
14
+ return result
15
+ end
16
+
17
+ def self.reset
18
+ Thread.current["rack-insight.sphinx"] = Stats.new
19
+ end
20
+
21
+ def self.stats
22
+ Thread.current["rack-insight.sphinx"] ||= Stats.new
23
+ end
24
+
25
+ def name
26
+ "sphinx"
27
+ end
28
+
29
+ def heading
30
+ "Sphinx: %.2fms (#{self.class.stats.queries.size} calls)" % self.class.stats.time
31
+ end
32
+
33
+ def content
34
+ result = render_template "panels/sphinx", :stats => self.class.stats
35
+ self.class.reset
36
+ return result
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,94 @@
1
+ module Rack::Insight
2
+ class SphinxPanel
3
+
4
+ class Stats
5
+ class Query
6
+ attr_reader :time
7
+ attr_reader :command
8
+
9
+ def initialize(time, *command_args)
10
+ @time = time
11
+ if command_args.flatten.first == :search
12
+ @command = "search: " + decode_message(command_args.first.flatten.last).collect{|k,v| "#{k} => #{v}"}.join(", ")
13
+ else
14
+ @command = command_args.flatten.first.to_s + ": No more info is available for this Sphinx request type"
15
+ end
16
+ end
17
+
18
+ def display_time
19
+ "%.2fms" % time
20
+ end
21
+
22
+ def decode_message(m)
23
+ @m = m.clone
24
+ params = ActiveSupport::OrderedHash.new
25
+
26
+ params[:offset] = consume_int
27
+ params[:limit] = consume_int
28
+ params[:match_mode] = consume_int
29
+ params[:rank_mode] = consume_int
30
+ params[:sort_mode] = consume_int
31
+ params[:sort_by] = consume_string
32
+ params[:query] = consume_string
33
+ wl = consume_int
34
+ weights = []
35
+ wl.times do weights << consume_int end
36
+ params[:weights] = weights
37
+
38
+ params[:index] = consume_string
39
+
40
+ consume_string
41
+
42
+ params[:id_range] = [consume_64int, consume_64int]
43
+ params
44
+ end
45
+
46
+ def consume_int
47
+ i = @m.unpack("N").first
48
+ @m = @m.slice(4, @m.length - 4)
49
+ i
50
+ end
51
+
52
+ def consume_64int
53
+ i = @m.unpack("NN").first
54
+ @m = @m.slice(8, @m.length - 8)
55
+ i
56
+ end
57
+
58
+ def consume_string
59
+ len = consume_int
60
+ s = @m.slice(0, len)
61
+ @m = @m.slice(len, @m.length - len)
62
+ s
63
+ end
64
+ end
65
+
66
+ attr_reader :calls
67
+ attr_reader :queries
68
+
69
+ def initialize
70
+ @queries = []
71
+ @calls = 0
72
+ @time = 0.0
73
+ end
74
+
75
+ def record_call(time, *command_args)
76
+
77
+ @queries << Query.new(time, command_args)
78
+ @calls += 1
79
+ @time += time
80
+ end
81
+
82
+ def display_time
83
+ "%.2fms" % time
84
+ end
85
+
86
+ def time
87
+ @queries.inject(0) do |memo, query|
88
+ memo + query.time
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,53 @@
1
+ require "digest"
2
+
3
+ module Rack::Insight
4
+
5
+ class SQLPanel < Panel
6
+
7
+ require "rack/insight/panels/sql_panel/panel_app"
8
+ require "rack/insight/panels/sql_panel/query"
9
+
10
+ def initialize(app)
11
+ super
12
+ probe(self) do
13
+ %w{ PostgreSQLAdapter MysqlAdapter SQLiteAdapter
14
+ Mysql2Adapter OracleEnhancedAdapter }.each do |adapter|
15
+ instrument "ActiveRecord::ConnectionAdapters::#{adapter}" do
16
+ instance_probe :execute
17
+ end
18
+ end
19
+ end
20
+ table_setup("sql_queries")
21
+ end
22
+
23
+ def self.panel_mappings
24
+ { "sql" => PanelApp.new }
25
+ end
26
+
27
+ def after_detect(method_call, timing, arguments, results)
28
+ store(@env, QueryResult.new(arguments.first, timing.duration, method_call.backtrace, results))
29
+ end
30
+
31
+ def total_time(queries)
32
+ (queries.inject(0) do |memo, query|
33
+ memo + query.time
34
+ end)
35
+ end
36
+
37
+ def name
38
+ "sql"
39
+ end
40
+
41
+ def heading_for_request(number)
42
+ queries = retrieve(number)
43
+ "#{queries.size} Queries (%.2fms)" % total_time(queries)
44
+ end
45
+
46
+ def content_for_request(number)
47
+ queries = retrieve(number)
48
+ render_template "panels/sql", :queries => queries
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,37 @@
1
+ require 'rack/insight/panels/sql_panel/query'
2
+
3
+ module Rack::Insight
4
+ class SQLPanel
5
+
6
+ class PanelApp < ::Rack::Insight::PanelApp
7
+
8
+ def dispatch
9
+ case request.path_info
10
+ when "/explain" then explain_sql
11
+ when "/profile" then profile_sql
12
+ when "/execute" then execute_sql
13
+ else not_found
14
+ end
15
+ end
16
+
17
+ def explain_sql
18
+ validate_params
19
+ query = ExplainResult.new(params["query"], params["time"].to_f)
20
+ render_template "panels/explain_sql", :query => query
21
+ end
22
+
23
+ def profile_sql
24
+ validate_params
25
+ query = ProfileResult.new(params["query"], params["time"].to_f)
26
+ render_template "panels/profile_sql", :query => query
27
+ end
28
+
29
+ def execute_sql
30
+ validate_params
31
+ query = QueryResult.new(params["query"], params["time"].to_f)
32
+ render_template "panels/execute_sql", :query => query
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,94 @@
1
+ module Rack::Insight
2
+ class SQLPanel
3
+
4
+ class QueryResult
5
+ include Rack::Insight::FilteredBacktrace
6
+
7
+ attr_reader :sql
8
+ attr_reader :time
9
+
10
+ def initialize(sql, time, backtrace = [], result=nil)
11
+ @sql = sql
12
+ @time = time
13
+ @backtrace = backtrace
14
+ @result = result
15
+ @results = nil
16
+ end
17
+
18
+ def result
19
+ @results ||= execute
20
+ return @results
21
+ end
22
+
23
+ def column_names
24
+ if result.respond_to?(:fields)
25
+ return result.fields
26
+ else
27
+ return result.fetch_fields.map{|col| col.name}
28
+ end
29
+ end
30
+
31
+ def rows
32
+ if result.respond_to?(:values)
33
+ result.values
34
+ else
35
+ result.map do |row|
36
+ row
37
+ end
38
+ end
39
+ end
40
+
41
+ def human_time
42
+ "%.2fms" % (@time)
43
+ end
44
+
45
+ def inspectable?
46
+ sql.strip =~ /^SELECT /i
47
+ end
48
+
49
+ #Downside is: we re-execute the SQL...
50
+ def self.execute(sql)
51
+ ActiveRecord::Base.connection.execute(sql)
52
+ end
53
+
54
+ def execute
55
+ self.class.execute(@sql)
56
+ end
57
+
58
+ def valid_hash?(secret_key, possible_hash)
59
+ hash = Digest::SHA1.hexdigest [secret_key, @sql].join(":")
60
+ possible_hash == hash
61
+ end
62
+ end
63
+
64
+ class ExplainResult < QueryResult
65
+ def execute
66
+ self.class.execute "EXPLAIN #{@sql}"
67
+ end
68
+ end
69
+
70
+ class ProfileResult < QueryResult
71
+ def with_profiling
72
+ result = nil
73
+ begin
74
+ self.class.execute("SET PROFILING=1")
75
+ result = yield
76
+ ensure
77
+ self.class.execute("SET PROFILING=0")
78
+ end
79
+ return result
80
+ end
81
+
82
+ def execute
83
+ with_profiling do
84
+ super
85
+ self.class.execute <<-SQL
86
+ SELECT *
87
+ FROM information_schema.profiling
88
+ WHERE query_id = (SELECT query_id FROM information_schema.profiling ORDER BY query_id DESC LIMIT 1)
89
+ SQL
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,58 @@
1
+ module Rack::Insight
2
+
3
+ class TemplatesPanel < Panel
4
+ require "rack/insight/panels/templates_panel/rendering"
5
+
6
+ def initialize(app)
7
+ super
8
+
9
+ probe(self) do
10
+ instrument "ActionView::Template" do
11
+ instance_probe :render
12
+ end
13
+ end
14
+
15
+ table_setup("templates")
16
+
17
+ @current = nil
18
+ end
19
+
20
+
21
+ def request_start(env, start)
22
+ @current = Rendering.new("root")
23
+ end
24
+
25
+ def request_finish(env, status, headers, body, timing)
26
+ store(env, @current)
27
+ @current = nil
28
+ end
29
+
30
+ def before_detect(method_call, args)
31
+ template_name = method_call.object.virtual_path
32
+
33
+ rendering = Rendering.new(template_name)
34
+ @current.add(rendering)
35
+ @current = rendering
36
+ end
37
+
38
+ def after_detect(method_call, timing, args, result)
39
+ @current.timing = timing
40
+ @current = @current.parent
41
+ end
42
+
43
+ def name
44
+ "templates"
45
+ end
46
+
47
+ def heading_for_request(number)
48
+ "Templates: %.2fms" % (retrieve(number).inject(0.0){|memo, rendering| memo + rendering.duration})
49
+ end
50
+
51
+ def content_for_request(number)
52
+ result = render_template "panels/templates", :root_rendering => retrieve(number).first
53
+ return result
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,81 @@
1
+ module Rack::Insight
2
+ class TemplatesPanel
3
+
4
+ class Rendering
5
+ attr_accessor :name
6
+ attr_accessor :parent
7
+ attr_accessor :timing
8
+ attr_reader :children
9
+
10
+ def initialize(name, timing = nil)
11
+ @name = name
12
+ @timing = timing
13
+ @children = []
14
+ end
15
+
16
+ def start_time
17
+ @timing.start
18
+ end
19
+ alias_method :time, :start_time
20
+
21
+ def end_time
22
+ @timing.end
23
+ end
24
+
25
+ def add(rendering)
26
+ @children << rendering
27
+ rendering.parent = self
28
+ end
29
+
30
+ def delete(rendering)
31
+ @children.delete(rendering)
32
+ end
33
+
34
+ def duration
35
+ if @timing
36
+ @timing.duration
37
+ else
38
+ child_duration
39
+ end
40
+ end
41
+
42
+ def exclusive_duration
43
+ duration - child_duration
44
+ end
45
+
46
+ def child_duration
47
+ children.inject(0.0) { |memo, c| memo + c.duration }
48
+ end
49
+
50
+ def duration_summary
51
+ if children.any?
52
+ "%.2fms, %.2f exclusive" % [duration, exclusive_duration]
53
+ else
54
+ "%.2fms" % (duration)
55
+ end
56
+ end
57
+ def html
58
+ <<-HTML
59
+ <li>
60
+ <p>#{name} (#{duration_summary})</p>
61
+
62
+ #{children_html}
63
+ </li>
64
+ HTML
65
+ end
66
+
67
+ def children_html
68
+ return "" unless children.any?
69
+
70
+ <<-HTML
71
+ <ul>#{joined_children_html}</ul>
72
+ HTML
73
+ end
74
+
75
+ def joined_children_html
76
+ children.map { |c| c.html }.join
77
+ end
78
+ end
79
+
80
+ end
81
+ end