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,84 @@
1
+ module Insight
2
+ describe "TemplatesPanel" do
3
+ before do
4
+ mock_constant("ActionView::Template")
5
+ reset_insight :panel_classes => [TemplatesPanel]
6
+ end
7
+
8
+ describe "heading" do
9
+ it "displays the total rendering time" do
10
+ response = get_via_rack "/"
11
+ response.should have_heading("Templates: 0.00ms")
12
+ end
13
+ end
14
+
15
+ def mock_template(path)
16
+ template = stub("ActionView::Template")
17
+ template.stub!(:virtual_path => path)
18
+ template
19
+ end
20
+
21
+ describe "content" do
22
+ it "displays the template paths" do
23
+ app.before_return do
24
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/show"))
25
+ end
26
+ response = get_via_rack "/"
27
+ response.should contain("users/show")
28
+ end
29
+
30
+ it "displays the template children" do
31
+ app.before_return do
32
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/show")) do
33
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/toolbar"))
34
+ end
35
+ end
36
+ response = get_via_rack "/"
37
+ response.should have_selector("li", :content => "users/show") do |li|
38
+ li.should contain("users/toolbar")
39
+ end
40
+ end
41
+
42
+ context "for templates that rendered templates" do
43
+ it "displays the total time" do
44
+ app.before_return do
45
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/show")) do
46
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/toolbar"))
47
+ end
48
+ end
49
+
50
+ response = get_via_rack "/"
51
+ response.should have_selector("li", :content => "users/show") do |li|
52
+ li.should contain(TIME_MS_REGEXP)
53
+ end
54
+ end
55
+
56
+ it "displays the exclusive time" do
57
+ app.before_return do
58
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/show")) do
59
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/toolbar"))
60
+ end
61
+ end
62
+
63
+ response = get_via_rack "/"
64
+ response.should have_selector("li", :content => "users/show") do |li|
65
+ li.should contain(/\d\.\d{2} exclusive/)
66
+ end
67
+ end
68
+ end
69
+
70
+ context "for leaf templates" do
71
+ it "does not display the exclusive time" do
72
+ app.before_return do
73
+ mock_method_call("ActionView::Template", :render, [], :instance, mock_template("users/show"))
74
+ end
75
+
76
+ response = get_via_rack "/"
77
+ response.should contain("users/show") do |li|
78
+ li.should_not contain("exclusive")
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,36 @@
1
+ module Insight
2
+ describe "TimerPanel" do
3
+ before do
4
+ reset_insight :panel_classes => [TimerPanel]
5
+ end
6
+
7
+ describe "heading" do
8
+ it "displays the elapsed time" do
9
+ response = get_via_rack "/"
10
+ response.should have_heading(TIME_MS_REGEXP)
11
+ end
12
+ end
13
+
14
+ describe "content" do
15
+ it "displays the user CPU time" do
16
+ response = get_via_rack "/"
17
+ response.should have_row("#timer", "User CPU time", TIME_MS_REGEXP)
18
+ end
19
+
20
+ it "displays the system CPU time" do
21
+ response = get_via_rack "/"
22
+ response.should have_row("#timer", "System CPU time", TIME_MS_REGEXP)
23
+ end
24
+
25
+ it "displays the total CPU time" do
26
+ response = get_via_rack "/"
27
+ response.should have_row("#timer", "Total CPU time", TIME_MS_REGEXP)
28
+ end
29
+
30
+ it "displays the elapsed time" do
31
+ response = get_via_rack "/"
32
+ response.should have_row("#timer", "Elapsed time", TIME_MS_REGEXP)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,141 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'logical-insight'
3
+
4
+ describe Insight do
5
+ before :each do
6
+ reset_insight
7
+ end
8
+
9
+ it "inserts the Insight toolbar" do
10
+ response = get "/"
11
+ response.should have_selector("div#insight")
12
+ end
13
+
14
+ it "updates the Content-Length" do
15
+ response = get "/"
16
+ response["Content-Length"].should == response.body.size.to_s
17
+ end
18
+
19
+ it "serves the Insight assets under /__insight__/" do
20
+ response = get "/__insight__/insight.css"
21
+ response.should be_ok
22
+ end
23
+
24
+ it "modifies HTML responses with a charset" do
25
+ response = get "/", :content_type => "application/xhtml+xml; charset=utf-8"
26
+ response.should have_selector("div#insight")
27
+ end
28
+
29
+ it "does not modify XMLHttpRequest responses" do
30
+ response = get "/", {}, { :xhr => true }
31
+ response.should_not have_selector("div#insight")
32
+ end
33
+
34
+ it "modifies XHTML responses" do
35
+ response = get "/", :content_type => "application/xhtml+xml"
36
+ response.should have_selector("div#insight")
37
+ end
38
+
39
+ it "does not modify non-HTML responses" do
40
+ response = get "/", :content_type => "text/csv"
41
+ response.should_not have_selector("div#insight")
42
+ end
43
+
44
+ it "does not modify server errors" do
45
+ app.disable :raise_errors
46
+ response = get "/error"
47
+ app.enable :raise_errors
48
+ response.should_not have_selector("div#insight")
49
+ end
50
+
51
+ context "redirected when not configured to intercept redirects" do
52
+ it "passes the redirect unmodified" do
53
+ response = get "/redirect"
54
+ response.status.should == 302
55
+ end
56
+
57
+ it "does not show the interception page" do
58
+ response = get "/redirect"
59
+ response.body.should_not contain("Location: /")
60
+ end
61
+
62
+ it "does not insert the toolbar" do
63
+ header 'cookie', ""
64
+ response = get "/redirect"
65
+ response.should_not have_selector("div#insight")
66
+ end
67
+
68
+ it "does not insert the toolbar if even toolbar requested" do
69
+ response = get "/redirect"
70
+ response.should_not have_selector("div#insight")
71
+ end
72
+ end
73
+
74
+ context "redirected when configured to intercept redirects" do
75
+ it "shows the interception page" do
76
+ response = get "/redirect", {}, "insight.intercept_redirects" => true
77
+ response.should have_selector("div#insight")
78
+ end
79
+
80
+ it "should provide a link to the target URL" do
81
+ response = get "/redirect", {}, "insight.intercept_redirects" => true
82
+ response.should have_selector("a[href='http://example.org/']")
83
+ end
84
+
85
+ it "inserts the toolbar if requested" do
86
+ response = get "/redirect", {}, "insight.intercept_redirects" => true
87
+ response.should have_selector("div#insight")
88
+ end
89
+
90
+ it "does not inserts the toolbar if not requested" do
91
+ header 'cookie', ""
92
+ response = get "/redirect", {}, "insight.intercept_redirects" => true
93
+ response.should_not have_selector("div#insight")
94
+ end
95
+ end
96
+
97
+ context "configured with an IP address restriction" do
98
+ before do
99
+ rack_env "insight.ip_masks", [IPAddr.new("127.0.0.1/255.255.255.0")]
100
+ end
101
+
102
+ it "inserts the Insight toolbar when the IP matches" do
103
+ response = get_via_rack "/", {}, "REMOTE_ADDR" => "127.0.0.2"
104
+ response.should have_selector("div#insight")
105
+ end
106
+
107
+ it "is disabled when the IP doesn't match" do
108
+ response = get_via_rack "/", {}, "REMOTE_ADDR" => "128.0.0.1"
109
+ response.should_not have_selector("div#insight")
110
+ end
111
+
112
+ it "doesn't use any panels" do
113
+ DummyPanel.should_not_receive(:new)
114
+ rack_env "insight.panel_classes", [DummyPanel]
115
+ get_via_rack "/", {}, "REMOTE_ADDR" => "128.0.0.1"
116
+ end
117
+ end
118
+
119
+ context "configured with a password" do
120
+ before do
121
+ rack_env "insight.password", "secret"
122
+ end
123
+
124
+ it "should insert the Insight toolbar when the password matches" do
125
+ sha = Digest::SHA1.hexdigest ["insight", "secret"].join(":")
126
+ set_cookie ["insight_enabled=1", "insight_password=#{sha}"]
127
+ response = get_via_rack "/"
128
+ response.should have_selector("div#insight")
129
+ end
130
+
131
+ it "should be disabled when the password doesn't match" do
132
+ response = get_via_rack "/"
133
+ response.should_not have_selector("div#insight")
134
+ end
135
+ it "doesn't use any panels" do
136
+ DummyPanel.should_not_receive(:new)
137
+ rack_env "insight.panel_classes", [DummyPanel]
138
+ get_via_rack "/"
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,188 @@
1
+ require 'insight/instrumentation'
2
+ require 'insight/instrumentation/setup'
3
+
4
+ class One
5
+ def initialize(arry)
6
+ @array = arry
7
+ end
8
+
9
+ def self.record_self(array)
10
+ array << self
11
+ end
12
+
13
+ def self.a_class_method(array)
14
+ yield(array)
15
+ end
16
+
17
+ def an_instance_method()
18
+ yield(@array)
19
+ end
20
+ end
21
+
22
+ module Mod
23
+ def module_method(array)
24
+ yield(array)
25
+ end
26
+ end
27
+
28
+ class Two < One
29
+ def an_instance_method
30
+ super
31
+ end
32
+
33
+ def self.a_class_method(array)
34
+ super
35
+ end
36
+ end
37
+
38
+ describe "Setting up probes" do
39
+ let :method_calls do [] end
40
+ let :method_subject do [] end
41
+ let :recording_list do [] end
42
+
43
+ let :test_collector do
44
+ collector = Object.new
45
+ collector.extend Insight::Instrumentation::Client
46
+ collector.instance_variable_set("@calls", method_calls)
47
+ def collector.after_detect(method_call, timing, arguments, results)
48
+ @calls << [method_call, timing, arguments, results]
49
+ end
50
+ collector
51
+ end
52
+
53
+ let :instrument_setup do
54
+ Insight::Instrumentation::Setup.new(nil)
55
+ end
56
+
57
+ let :fake_env do
58
+ {}
59
+ end
60
+
61
+ before :each do
62
+ test_collector.probe(test_collector) do
63
+ instrument "One" do
64
+ instance_probe :an_instance_method
65
+ class_probe :a_class_method
66
+ class_probe :record_self
67
+ class_probe :allocate
68
+ end
69
+ end
70
+ instrument_setup.setup(fake_env)
71
+ end
72
+
73
+ after :each do
74
+ instrument_setup.teardown(fake_env, 200, {}, "")
75
+ end
76
+
77
+ # Test cases:
78
+ #
79
+ # Methods are called on
80
+ # instance
81
+ # class
82
+ # modules
83
+ #
84
+ # Methods are defined/probed at/called on
85
+ # Called on:
86
+ # obj: instance of class C
87
+ #
88
+ # Defined:
89
+ # class C
90
+ # super class B
91
+ # class C and parent B (overridden)
92
+ # module Mc - included in C
93
+ # class C and Mc
94
+ # module Mb - included in B
95
+ # class C and Mb
96
+ # module Me - extended in obj
97
+ # Me and C - Me overrides C
98
+ #
99
+ # Probed:
100
+ # On C
101
+ # On B
102
+ # On Mc, Mb, Me
103
+ #
104
+ # Not interfere with:
105
+ # Arguments
106
+ # Blocks
107
+ # Return Values
108
+ # Exceptions
109
+ #
110
+
111
+ it "should catch class methods defined on a superclass" do
112
+ #expect do
113
+ One.allocate
114
+ #end.to change(method_calls.length).by(1)
115
+ method_calls.last[0].method.should == :allocate
116
+ end
117
+
118
+ it "should not interfere with instance method call on One" do
119
+ expect do
120
+ One.new(method_subject).an_instance_method{|arr| arr << __LINE__}
121
+ end.to change{method_subject.length}.by(1)
122
+ end
123
+
124
+ it "should not interfere with class method call" do
125
+ expect do
126
+ One.a_class_method(method_subject){|arr| arr << __LINE__}
127
+ end.to change{method_subject.length}.by(1)
128
+ end
129
+
130
+ it "should collect calls made on One#an_instance_method" do
131
+ expect do
132
+ One.new(method_subject).an_instance_method{|arr| arr << __LINE__}
133
+ end.to change{method_calls.length}.by(1)
134
+ method_calls.last[0].method.should == :an_instance_method
135
+ end
136
+
137
+ it "should collect calls made on One#a_class_method" do
138
+ expect do
139
+ One.a_class_method(method_subject){|arr| arr << __LINE__}
140
+ end.to change{method_calls.length}.by(1)
141
+ method_calls.last[0].method.should == :a_class_method
142
+ end
143
+
144
+ it "should not mess with receive of class methods" do
145
+ One.record_self(recording_list)
146
+ recording_list.should include(One)
147
+ end
148
+
149
+ it "should not mess with receive of class methods on subclasses" do
150
+ Two.record_self(recording_list)
151
+ recording_list.should include(Two)
152
+ end
153
+
154
+
155
+ it "should not interfere with instance method call on Two" do
156
+ expect do
157
+ Two.new(method_subject).an_instance_method{|arr| arr << __LINE__}
158
+ end.to change{method_subject.length}.by(1)
159
+ end
160
+
161
+ it "should not interfere with class method call" do
162
+ expect do
163
+ Two.a_class_method(method_subject){|arr| arr << __LINE__}
164
+ end.to change{method_subject.length}.by(1)
165
+ end
166
+
167
+ it "should collect calls made on Two#an_instance_method" do
168
+ expect do
169
+ Two.new(method_subject).an_instance_method{|arr| arr << __LINE__}
170
+ end.to change{method_calls.length}.by(2)
171
+ method_calls.each do |method_call|
172
+ method_call[0].method.should == :an_instance_method
173
+ end
174
+
175
+ method_calls.map{|mc| mc[0].context}.should include("One", "Two")
176
+ end
177
+
178
+ it "should collect calls made on Two#a_class_method" do
179
+ expect do
180
+ Two.a_class_method(method_subject){|arr| arr << __LINE__}
181
+ end.to change{method_calls.length}.by(2)
182
+ method_calls.each do |method_call|
183
+ method_call[0].method.should == :a_class_method
184
+ end
185
+
186
+ method_calls.map{|mc| mc[0].context}.should include("One", "Two")
187
+ end
188
+ end