logical-insight 0.4.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 (100) hide show
  1. data/History.txt +45 -0
  2. data/MIT-LICENSE.txt +19 -0
  3. data/README.md +123 -0
  4. data/Rakefile +24 -0
  5. data/Thorfile +113 -0
  6. data/lib/insight.rb +17 -0
  7. data/lib/insight/app.rb +189 -0
  8. data/lib/insight/database.rb +186 -0
  9. data/lib/insight/enable-button.rb +43 -0
  10. data/lib/insight/filtered_backtrace.rb +45 -0
  11. data/lib/insight/instrumentation.rb +9 -0
  12. data/lib/insight/instrumentation/backstage.rb +10 -0
  13. data/lib/insight/instrumentation/client.rb +20 -0
  14. data/lib/insight/instrumentation/instrument.rb +109 -0
  15. data/lib/insight/instrumentation/package-definition.rb +58 -0
  16. data/lib/insight/instrumentation/probe-definition.rb +20 -0
  17. data/lib/insight/instrumentation/probe.rb +199 -0
  18. data/lib/insight/instrumentation/setup.rb +32 -0
  19. data/lib/insight/logger.rb +55 -0
  20. data/lib/insight/options.rb +102 -0
  21. data/lib/insight/panel.rb +119 -0
  22. data/lib/insight/panel_app.rb +31 -0
  23. data/lib/insight/panels-content.rb +22 -0
  24. data/lib/insight/panels-header.rb +18 -0
  25. data/lib/insight/panels/active_record_panel.rb +46 -0
  26. data/lib/insight/panels/cache_panel.rb +69 -0
  27. data/lib/insight/panels/cache_panel/panel_app.rb +46 -0
  28. data/lib/insight/panels/cache_panel/stats.rb +98 -0
  29. data/lib/insight/panels/log_panel.rb +54 -0
  30. data/lib/insight/panels/memory_panel.rb +32 -0
  31. data/lib/insight/panels/rails_info_panel.rb +19 -0
  32. data/lib/insight/panels/redis_panel.rb +42 -0
  33. data/lib/insight/panels/redis_panel/redis_extension.rb +23 -0
  34. data/lib/insight/panels/redis_panel/stats.rb +50 -0
  35. data/lib/insight/panels/request_variables_panel.rb +70 -0
  36. data/lib/insight/panels/speedtracer_panel.rb +89 -0
  37. data/lib/insight/panels/speedtracer_panel/trace-app.rb +52 -0
  38. data/lib/insight/panels/speedtracer_panel/tracer.rb +212 -0
  39. data/lib/insight/panels/sql_panel.rb +53 -0
  40. data/lib/insight/panels/sql_panel/panel_app.rb +37 -0
  41. data/lib/insight/panels/sql_panel/query.rb +94 -0
  42. data/lib/insight/panels/templates_panel.rb +58 -0
  43. data/lib/insight/panels/templates_panel/rendering.rb +81 -0
  44. data/lib/insight/panels/timer_panel.rb +40 -0
  45. data/lib/insight/params_signature.rb +61 -0
  46. data/lib/insight/public/__insight__/bookmarklet.html +10 -0
  47. data/lib/insight/public/__insight__/bookmarklet.js +223 -0
  48. data/lib/insight/public/__insight__/insight.css +235 -0
  49. data/lib/insight/public/__insight__/insight.js +123 -0
  50. data/lib/insight/public/__insight__/jquery-1.3.2.js +4376 -0
  51. data/lib/insight/public/__insight__/jquery.tablesorter.min.js +1 -0
  52. data/lib/insight/public/__insight__/spinner.gif +0 -0
  53. data/lib/insight/rack_static_bug_avoider.rb +16 -0
  54. data/lib/insight/redirect_interceptor.rb +25 -0
  55. data/lib/insight/render.rb +72 -0
  56. data/lib/insight/request-recorder.rb +23 -0
  57. data/lib/insight/toolbar.rb +63 -0
  58. data/lib/insight/views/enable-button.html.erb +1 -0
  59. data/lib/insight/views/error.html.erb +17 -0
  60. data/lib/insight/views/headers_fragment.html.erb +20 -0
  61. data/lib/insight/views/panels/active_record.html.erb +17 -0
  62. data/lib/insight/views/panels/cache.html.erb +93 -0
  63. data/lib/insight/views/panels/execute_sql.html.erb +32 -0
  64. data/lib/insight/views/panels/explain_sql.html.erb +32 -0
  65. data/lib/insight/views/panels/log.html.erb +21 -0
  66. data/lib/insight/views/panels/profile_sql.html.erb +32 -0
  67. data/lib/insight/views/panels/rails_info.html.erb +19 -0
  68. data/lib/insight/views/panels/redis.html.erb +46 -0
  69. data/lib/insight/views/panels/request_variables.html.erb +25 -0
  70. data/lib/insight/views/panels/speedtracer/serverevent.html.erb +10 -0
  71. data/lib/insight/views/panels/speedtracer/servertrace.html.erb +12 -0
  72. data/lib/insight/views/panels/speedtracer/traces.html.erb +18 -0
  73. data/lib/insight/views/panels/sql.html.erb +43 -0
  74. data/lib/insight/views/panels/templates.html.erb +6 -0
  75. data/lib/insight/views/panels/timer.html.erb +19 -0
  76. data/lib/insight/views/panels/view_cache.html.erb +19 -0
  77. data/lib/insight/views/redirect.html.erb +16 -0
  78. data/lib/insight/views/request_fragment.html.erb +25 -0
  79. data/lib/insight/views/toolbar.html.erb +29 -0
  80. data/lib/logical-insight.rb +1 -0
  81. data/spec/custom_matchers.rb +31 -0
  82. data/spec/fixtures/config.ru +8 -0
  83. data/spec/fixtures/dummy_panel.rb +2 -0
  84. data/spec/fixtures/sample_app.rb +72 -0
  85. data/spec/insight/panels/active_record_panel_spec.rb +42 -0
  86. data/spec/insight/panels/cache_panel_spec.rb +176 -0
  87. data/spec/insight/panels/log_panel_spec.rb +44 -0
  88. data/spec/insight/panels/memory_panel_spec.rb +19 -0
  89. data/spec/insight/panels/mongo_panel_spec_pending.rb +50 -0
  90. data/spec/insight/panels/rails_info_panel_spec.rb +27 -0
  91. data/spec/insight/panels/redis_panel_spec.rb +66 -0
  92. data/spec/insight/panels/sql_panel_spec.rb +145 -0
  93. data/spec/insight/panels/templates_panel_spec.rb +84 -0
  94. data/spec/insight/panels/timer_panel_spec.rb +36 -0
  95. data/spec/insight_spec.rb +141 -0
  96. data/spec/instrumentation_spec.rb +188 -0
  97. data/spec/rcov.opts +1 -0
  98. data/spec/spec.opts +1 -0
  99. data/spec/spec_helper.rb +93 -0
  100. metadata +187 -0
@@ -0,0 +1,31 @@
1
+ module Insight
2
+
3
+ class PanelApp
4
+ include Insight::Render
5
+
6
+ attr_reader :request
7
+
8
+ def call(env)
9
+ @request = Rack::Request.new(env)
10
+ dispatch
11
+ end
12
+
13
+ def render_template(*args)
14
+ Rack::Response.new([super]).to_a
15
+ end
16
+
17
+ def params
18
+ @request.GET
19
+ end
20
+
21
+ def not_found(message="")
22
+ [404, {}, [message]]
23
+ end
24
+
25
+ def validate_params
26
+ ParamsSignature.new(request).validate!
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,22 @@
1
+ module Insight
2
+ class PanelsContent < PanelApp
3
+ def initialize(insight_app)
4
+ @insight_app = insight_app
5
+ @request_table = Database::RequestTable.new
6
+ end
7
+
8
+ def dispatch
9
+ return not_found("not get") unless @request.get?
10
+ return not_found("id nil") if params['request_id'].nil?
11
+ request = @request_table.select("*", "id = #{params['request_id']}").first
12
+ return not_found("id not found") if request.nil?
13
+ requests = @request_table.to_a.map do |row|
14
+ { :id => row[0], :method => row[1], :path => row[2] }
15
+ end
16
+ render_template("request_fragment",
17
+ :request_id => params['request_id'].to_i,
18
+ :requests => requests,
19
+ :panels => @insight_app.panels)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ module Insight
2
+ class PanelsHeader < PanelApp
3
+ def initialize(insight_app)
4
+ @insight_app = insight_app
5
+ @request_table = Database::RequestTable.new
6
+ end
7
+
8
+ def dispatch
9
+ return not_found("not get") unless @request.get?
10
+ return not_found("id nil") if params['request_id'].nil?
11
+ request = @request_table.select("*", "id = #{params['request_id']}").first
12
+ return not_found("id not found") if request.nil?
13
+ render_template("headers_fragment",
14
+ :request_id => params['request_id'].to_i,
15
+ :panels => @insight_app.panels)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ module Insight
2
+ class ActiveRecordPanel < Panel
3
+ def initialize(app)
4
+ super
5
+
6
+ table_setup("active_record")
7
+
8
+ probe(self) do
9
+ instrument "ActiveRecord::Base" do
10
+ class_probe :allocate
11
+ end
12
+ end
13
+ end
14
+
15
+ def request_start(env, start)
16
+ @records = Hash.new{ 0 }
17
+ end
18
+
19
+ def after_detect(method_call, timing, results, args)
20
+ @records[method_call.object.base_class.name] += 1
21
+ end
22
+
23
+ def request_finish(env, status, headers, body, timing)
24
+ store(env, @records)
25
+ end
26
+
27
+ def name
28
+ "active_record"
29
+ end
30
+
31
+ def heading_for_request(number)
32
+ record = retrieve(number).first
33
+ total = record.inject(0) do |memo, (key, value)|
34
+ memo + value
35
+ end
36
+ "#{total} AR Objects"
37
+ end
38
+
39
+ def content_for_request(number)
40
+ records = retreive(number).first.to_a.sort_by { |key, value| value }.reverse
41
+ render_template "panels/active_record", :records => records
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,69 @@
1
+
2
+ module Insight
3
+
4
+ class CachePanel < Panel
5
+ require "insight/panels/cache_panel/panel_app"
6
+ require "insight/panels/cache_panel/stats"
7
+
8
+ def initialize(app)
9
+ super
10
+
11
+ probe(self) do
12
+ instrument("Memcached") do
13
+ instance_probe :decrement, :get, :increment, :set, :add,
14
+ :replace, :delete, :prepend, :append
15
+ end
16
+
17
+ instrument("MemCache") do
18
+ instance_probe :decr, :get, :get_multi, :incr, :set, :add, :delete
19
+ end
20
+
21
+ instrument("Dalli::Client") do
22
+ instance_probe :perform
23
+ end
24
+ end
25
+
26
+ table_setup("cache")
27
+ end
28
+
29
+ def request_start(env, start)
30
+ @stats = Stats.new
31
+ end
32
+
33
+ def request_finish(env, st, hd, bd, timing)
34
+ logger(env).debug{ "Stats: #@stats" }
35
+ store(env, @stats)
36
+ end
37
+
38
+ def after_detect(method_call, timing, args, result)
39
+ method, key = method_call.method, args.first
40
+ if(defined? Dalli and Dalli::Client === method_call.object)
41
+ method, key = args[0], args[1]
42
+ end
43
+ logger.info{ "Cache panel got #{method} #{key.inspect}" }
44
+ @stats.record_call(method, timing.duration, !result.nil?, key)
45
+ end
46
+
47
+ def panel_app
48
+ PanelApp.new
49
+ end
50
+
51
+ def name
52
+ "cache"
53
+ end
54
+
55
+ def heading_for_request(number)
56
+ stats = retreive(number).first
57
+
58
+ "Cache: %.2fms (#{stats.queries.size} calls)" % stats.time
59
+ end
60
+
61
+ def content_for_request(number)
62
+ logger.debug{{ :req_num => number }}
63
+ stats = retreive(number).first
64
+ render_template "panels/cache", :stats => stats
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,46 @@
1
+ module Insight
2
+ class CachePanel
3
+
4
+ class PanelApp < ::Insight::PanelApp
5
+
6
+ def dispatch
7
+ case request.path_info
8
+ when "/__insight__/view_cache" then view_cache
9
+ when "/__insight__/delete_cache" then delete_cache
10
+ when "/__insight__/delete_cache_list" then delete_cache_list
11
+ else not_found
12
+ end
13
+ end
14
+
15
+ def ok
16
+ Rack::Response.new(["OK"]).to_a
17
+ end
18
+
19
+ def view_cache
20
+ validate_params
21
+ render_template "panels/view_cache", :key => params["key"], :value => Rails.cache.read(params["key"])
22
+ end
23
+
24
+ def delete_cache
25
+ validate_params
26
+ raise "Rails not found... can't delete key" unless defined?(Rails)
27
+ Rails.cache.delete(params["key"])
28
+ ok
29
+ end
30
+
31
+ def delete_cache_list
32
+ validate_params
33
+ raise "Rails not found... can't delete key" unless defined?(Rails)
34
+
35
+ params.each do |key, value|
36
+ next unless key =~ /^keys_/
37
+ Rails.cache.delete(value)
38
+ end
39
+
40
+ ok
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,98 @@
1
+ module Insight
2
+ class CachePanel
3
+
4
+ class Stats
5
+ class Query
6
+ attr_reader :method, :time, :hit, :keys
7
+
8
+ def initialize(method, time, hit, keys)
9
+ @method = method
10
+ @time = time
11
+ @hit = hit
12
+ @keys = keys
13
+ end
14
+
15
+ def display_time
16
+ "%.2fms" % time
17
+ end
18
+
19
+ def display_keys
20
+ if keys.size == 1
21
+ keys.first
22
+ else
23
+ keys.join(", ")
24
+ end
25
+ end
26
+ end
27
+
28
+ attr_reader :calls
29
+ attr_reader :keys
30
+ attr_reader :queries
31
+
32
+ def initialize
33
+ @queries = []
34
+ @misses =
35
+ @calls = 0
36
+ @time = 0.0
37
+ @keys = []
38
+ end
39
+
40
+ def record_call(method, time, hit, key)
41
+ if Array === key
42
+ @queries << Query.new(:get_multi, time, hit, key)
43
+ else
44
+ @queries << Query.new(method.to_sym, time, hit, [key])
45
+ end
46
+ @calls += 1
47
+ @time += time
48
+ @keys += keys
49
+ end
50
+
51
+ def display_time
52
+ "%.2fms" % time
53
+ end
54
+
55
+ def time
56
+ @queries.inject(0) do |memo, query|
57
+ memo + query.time
58
+ end
59
+ end
60
+
61
+ def gets
62
+ count_queries(:get)
63
+ end
64
+
65
+ def sets
66
+ count_queries(:set)
67
+ end
68
+
69
+ def deletes
70
+ count_queries(:delete)
71
+ end
72
+
73
+ def get_multis
74
+ count_queries(:get_multi)
75
+ end
76
+
77
+ def hits
78
+ @queries.select { |q| [:get, :get_multi].include?(q.method) && q.hit }.size
79
+ end
80
+
81
+ def misses
82
+ @queries.select { |q| [:get, :get_multi].include?(q.method) && !q.hit }.size
83
+ end
84
+
85
+ def count_queries(method)
86
+ @queries.select { |q| q.method == method }.size
87
+ end
88
+
89
+ def queries_to_param
90
+ params = {}
91
+ @queries.each_with_index do |query, index|
92
+ params["keys_#{index}"] = query.keys.first
93
+ end
94
+ params
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,54 @@
1
+ module Insight
2
+ class LogPanel < Panel
3
+ class LogEntry
4
+ attr_reader :level, :time, :message
5
+ LEVELS = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL']
6
+
7
+ def initialize(level, time, message)
8
+ @level = LEVELS[level]
9
+ @time = time
10
+ @message = message
11
+ end
12
+
13
+ def cleaned_message
14
+ @message.to_s.gsub(/\e\[[;\d]+m/, "")
15
+ end
16
+ end
17
+
18
+
19
+ def after_detect(method_call, timing, args, message)
20
+ message = args[1] || args[2] unless message.is_a?(String)
21
+ log_level = args[0]
22
+ store(@env, LogEntry.new(log_level, timing.delta_t, message))
23
+ end
24
+
25
+ def initialize(app)
26
+ probe(self) do
27
+ instrument "ActiveSupport::BufferedLogger" do
28
+ instance_probe :add
29
+ end
30
+
31
+ instrument "Logger" do
32
+ instance_probe :add
33
+ end
34
+ end
35
+
36
+ table_setup("log_entries")
37
+
38
+ super
39
+ end
40
+
41
+ def name
42
+ "log"
43
+ end
44
+
45
+ def heading
46
+ "Log"
47
+ end
48
+
49
+ def content_for_request(number)
50
+ render_template "panels/log", :logs => retrieve(number)
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,32 @@
1
+ module Insight
2
+
3
+ class MemoryPanel < Panel
4
+ def initialize(app)
5
+ super
6
+ table_setup("memory_records")
7
+ end
8
+
9
+ def before(env)
10
+ @original_memory = `ps -o rss= -p #{$$}`.to_i
11
+ end
12
+
13
+ def after(env, status, headers, body)
14
+ total_memory = `ps -o rss= -p #{$$}`.to_i
15
+ store(env, {:total_memory => total_memory,
16
+ :memory_increase => total_memory - @original_memory,
17
+ :original_memory => @original_memory})
18
+ end
19
+
20
+ def heading_for_request(number)
21
+ record = retrieve(number).first
22
+
23
+ "#{record[:memory_increase]} KB &#916;, #{record[:total_memory]} KB total"
24
+ end
25
+
26
+ def has_content?
27
+ false
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,19 @@
1
+ module Insight
2
+ class RailsInfoPanel < Panel
3
+
4
+ def name
5
+ "rails_info"
6
+ end
7
+
8
+ def heading
9
+ return unless (defined?(Rails) && defined?(Rails::Info))
10
+ "Rails #{Rails.version}"
11
+ end
12
+
13
+ def content
14
+ return unless (defined?(Rails) && defined?(Rails::Info))
15
+ render_template "panels/rails_info"
16
+ end
17
+
18
+ end
19
+ end