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,45 @@
1
+ module Rack::Insight
2
+ module FilteredBacktrace
3
+
4
+ def backtrace
5
+ @backtrace
6
+ end
7
+
8
+ def has_backtrace?
9
+ filtered_backtrace.any?
10
+ end
11
+
12
+ def filtered_backtrace
13
+ @filtered_backtrace ||= @backtrace.grep(FilteredBacktrace.backtrace_regexp)
14
+ end
15
+
16
+ def self.backtrace_regexp
17
+ @backtrace_regexp ||=
18
+ begin
19
+ if true or (app_root = root_for_backtrace_filtering).nil?
20
+ /.*/
21
+ else
22
+ excludes = %w{vendor}
23
+ %r{^#{app_root}(?:#{::File::Separator}(?!#{excludes.join("|")}).+)$}
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.root_for_backtrace_filtering(sub_path = nil)
29
+ if defined?(Rails) && Rails.respond_to?(:root)
30
+ sub_path ? Rails.root.join(sub_path) : Rails.root
31
+ else
32
+ root = if defined?(RAILS_ROOT)
33
+ RAILS_ROOT
34
+ elsif defined?(ROOT)
35
+ ROOT
36
+ elsif defined?(Sinatra::Application)
37
+ Sinatra::Application.root
38
+ else
39
+ nil
40
+ end
41
+ sub_path ? ::File.join(root, sub_path) : root
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,9 @@
1
+ module Rack::Insight
2
+ module Instrumentation; end
3
+ end
4
+ require 'rack/insight/instrumentation/instrument'
5
+ require 'rack/insight/instrumentation/probe'
6
+ require 'rack/insight/instrumentation/client'
7
+ require 'rack/insight/instrumentation/setup'
8
+ require 'rack/insight/instrumentation/package-definition'
9
+ require 'rack/insight/instrumentation/probe-definition'
@@ -0,0 +1,10 @@
1
+ module Rack::Insight::Instrumentation
2
+ module Backstage
3
+ def backstage
4
+ Thread.current["instrumented_backstage"] = true
5
+ yield
6
+ ensure
7
+ Thread.current["instrumented_backstage"] = false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ require 'rack/insight/instrumentation/package-definition'
2
+ module Rack::Insight::Instrumentation
3
+ module Client
4
+ def probe(collector, &block)
5
+ ::Rack::Insight::Instrumentation::PackageDefinition::probe(collector, &block)
6
+ end
7
+
8
+ def request_start(env, start)
9
+ end
10
+
11
+ def before_detect(method_call, arguments)
12
+ end
13
+
14
+ def after_detect(method_call, timing, arguments, result)
15
+ end
16
+
17
+ def request_finish(env, status, headers, body, timing)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,109 @@
1
+ require 'rack/insight/instrumentation/backstage'
2
+ require 'rack/insight/logger'
3
+
4
+ module Rack::Insight
5
+ module Instrumentation
6
+ class Instrument
7
+ MethodCall = Struct.new(:call_number, :backtrace, :file, :line, :object, :context, :kind, :method, :thread)
8
+ class Timing
9
+ def initialize(request_start, start, finish)
10
+ @request_start, @start, @finish = request_start, start, finish
11
+ end
12
+
13
+ attr_reader :request_start, :start, :finish
14
+
15
+ def duration
16
+ @duration ||= ((@finish - @start) * 1000).to_i
17
+ end
18
+
19
+ def delta_t
20
+ @delta_t ||= ((@start - @request_start) * 1000).to_i
21
+ end
22
+ end
23
+
24
+ @@call_seq = 0
25
+
26
+ def self.seq_number
27
+ Thread.exclusive do
28
+ return @@call_seq += 1
29
+ end
30
+ end
31
+
32
+ def initialize()
33
+ @start = Time.now
34
+ @collectors = nil
35
+ end
36
+
37
+ include Backstage
38
+
39
+ include Logging
40
+
41
+ def run(object, context="::", kind=:instance, called_at = caller[0], method = "<unknown>", args=[], &blk)
42
+ file, line, rest = called_at.split(':')
43
+ call_number = backstage{ self.class.seq_number }
44
+ method_call = backstage{ MethodCall.new(call_number, caller(1), file, line, object, context, kind, method, Thread::current) }
45
+ #$stderr.puts [method_call.context, method_call.method].inspect
46
+ start_time = Time.now
47
+ backstage do
48
+ start_event(method_call, args)
49
+ end
50
+ result = blk.call # execute the provided code block
51
+ backstage do
52
+ finish_event(method_call, args, start_time, result)
53
+ end
54
+ end
55
+
56
+ def collectors_for(method_call)
57
+ probe_chain = if method_call.kind == :instance
58
+ InstanceProbe.get_probe_chain(method_call.context)
59
+ else
60
+ ClassProbe.get_probe_chain(method_call.context)
61
+ end
62
+ collectors = probe_chain.inject([]) do |list, probe|
63
+ probe.collectors(method_call.method)
64
+ end
65
+ logger.debug do
66
+ "Probe chain for: #{method_call.context} #{method_call.kind} #{method_call.method}:\n #{collectors.map{|col| col.class.name}.join(", ")}"
67
+ end
68
+ collectors
69
+ end
70
+
71
+ def start_event(method_call, arguments)
72
+ logger.debug{ "Starting event: #{method_call.context} #{method_call.kind} #{method_call.method}" }
73
+
74
+ collectors_for(method_call).each do |collector|
75
+ collector.before_detect(method_call, arguments)
76
+ end
77
+ end
78
+
79
+ def finish_event(method_call, arguments, start_time, result)
80
+ timing = Timing.new(@start, start_time, Time.now)
81
+ logger.debug{ "Finishing event: #{method_call.context} #{method_call.kind} #{method_call.method}" }
82
+ collectors_for(method_call).each do |collector|
83
+ collector.after_detect(method_call, timing, arguments, result)
84
+ end
85
+ end
86
+
87
+ def all_collectors
88
+ PackageDefinition.all_collectors
89
+ end
90
+
91
+ def start(env)
92
+ all_collectors.each do |collector|
93
+ collector.request_start(env, @start)
94
+ end
95
+ end
96
+
97
+ def finish(env, status, headers, body)
98
+ @timing = Timing.new(@start, @start, Time.now)
99
+ all_collectors.each do |collector|
100
+ collector.request_finish(env, status, headers, body, @timing)
101
+ end
102
+ end
103
+
104
+ def duration
105
+ @timing.duration
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,58 @@
1
+ module Rack::Insight::Instrumentation
2
+ class PackageDefinition
3
+ class << self
4
+ def start
5
+ @started = begin
6
+ probes.each do |probe|
7
+ probe.fulfill_probe_orders
8
+ end
9
+ true
10
+ end
11
+ end
12
+
13
+ def probes
14
+ InstanceProbe.all_probes + ClassProbe.all_probes
15
+ end
16
+
17
+ def clear_collectors
18
+ all_collectors.clear
19
+ end
20
+
21
+ def all_collectors
22
+ @all_collectors ||= []
23
+ end
24
+
25
+ def add_collector(collector)
26
+ unless all_collectors.include?(collector)
27
+ all_collectors << collector
28
+ end
29
+ end
30
+
31
+ def probe(collector, &block)
32
+ add_collector(collector)
33
+ definer = self.new(collector)
34
+ definer.instance_eval &block
35
+ end
36
+ end
37
+
38
+ def get_class_probe(name)
39
+ ClassProbe.probe_for(name)
40
+ end
41
+
42
+ def get_instance_probe(name)
43
+ InstanceProbe.probe_for(name)
44
+ end
45
+
46
+ def initialize(collector)
47
+ @collector = collector
48
+ end
49
+
50
+ attr_reader :collector
51
+
52
+ def instrument(name, &block)
53
+ definer = ProbeDefinition.new(self, name)
54
+ definer.instance_eval(&block) unless block.nil?
55
+ return definer
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,20 @@
1
+ module Rack::Insight::Instrumentation
2
+ class ProbeDefinition
3
+ def initialize(package, target_name)
4
+ @package = package
5
+ @target_name = target_name
6
+ end
7
+
8
+ def instance_probe(*method_names)
9
+ if probes = @package.get_instance_probe(@target_name)
10
+ probes.probe(@package.collector, *method_names)
11
+ end
12
+ end
13
+
14
+ def class_probe(*method_names)
15
+ if probes = @package.get_class_probe(@target_name)
16
+ probes.probe(@package.collector, *method_names)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,196 @@
1
+ module Rack::Insight
2
+ module Instrumentation
3
+ class Probe
4
+ module Interpose
5
+ end
6
+
7
+ @@class_list = nil
8
+
9
+ module ProbeRunner
10
+ include Backstage
11
+ include Logging
12
+
13
+ def probe_run(object, context = "::", kind=:instance, args=[], called_at=caller[1], method_name = nil)
14
+ if Thread.current['instrumented_backstage']
15
+ #warn "probe_run while backstage: #{context}, #{kind},
16
+ ##{method_name}" unless method_name.to_sym == :add
17
+ return yield
18
+ end
19
+ instrument = Thread.current['rack-insight.instrument']
20
+ result = nil
21
+ if instrument.nil?
22
+ backstage do
23
+ result = yield
24
+ end
25
+ else
26
+ instrument.run(object, context, kind, called_at, method_name, args){ result = yield }
27
+ end
28
+ result
29
+ end
30
+ extend self
31
+ end
32
+
33
+ class << self
34
+ include Logging
35
+
36
+ def class_list
37
+ @@class_list ||= begin
38
+ classes = []
39
+ ObjectSpace.each_object(Class) do |klass|
40
+ classes << klass
41
+ end
42
+ classes
43
+ end
44
+ end
45
+
46
+ def get_probe_chain(name)
47
+ const = const_from_name(name)
48
+ chain = []
49
+ const.ancestors.each do |mod|
50
+ if probes.has_key?(mod.name)
51
+ chain << probes[mod.name]
52
+ end
53
+ end
54
+ chain
55
+ end
56
+
57
+ def const_from_name(name)
58
+ parts = name.split("::")
59
+ const = parts.inject(Kernel) do |namespace, part|
60
+ namespace.const_get(part)
61
+ end
62
+ end
63
+
64
+ def probes
65
+ @probes ||= Hash.new do |h,k|
66
+ begin
67
+ h[k] = self.new(const_from_name(k))
68
+ rescue NameError
69
+ logger.warn{ "Cannot find constant: #{k}" }
70
+ end
71
+ end
72
+ end
73
+
74
+ def all_probes
75
+ probes.values
76
+ end
77
+
78
+ def probe_for(const)
79
+ probes[const]
80
+ end
81
+ end
82
+
83
+ def initialize(const)
84
+ @const = const
85
+ @probed = {}
86
+ @collectors = Hash.new{|h,k| h[k] = []}
87
+ @probe_orders = []
88
+ end
89
+
90
+ def collectors(key)
91
+ @collectors[key.to_sym]
92
+ end
93
+
94
+ def all_collectors
95
+ @collectors.values
96
+ end
97
+
98
+ def clear_collectors
99
+ @collectors.clear
100
+ end
101
+
102
+ def probe(collector, *methods)
103
+ methods.each do |name|
104
+ unless @collectors[name.to_sym].include?(collector)
105
+ @collectors[name.to_sym] << collector
106
+ end
107
+ @probe_orders << name
108
+ end
109
+ end
110
+
111
+ def descendants
112
+ @descendants ||= self.class.class_list.find_all do |klass|
113
+ klass.ancestors.include?(@const)
114
+ end
115
+ end
116
+
117
+ def local_method_defs(klass)
118
+ klass.instance_methods(false)
119
+ end
120
+
121
+ def descendants_that_define(method_name)
122
+ log{{ :descendants => descendants }}
123
+ descendants.find_all do |klass|
124
+ (@const != klass and local_method_defs(klass).include?(method_name))
125
+ end
126
+ end
127
+
128
+ include Logging
129
+ def log &block
130
+ logger.debug &block
131
+ end
132
+
133
+ def fulfill_probe_orders
134
+ log{{:probes_for => @const.name, :type => self.class}}
135
+ @probe_orders.each do |method_name|
136
+ log{{ :method => method_name }}
137
+ build_tracing_wrappers(@const, method_name)
138
+ descendants_that_define(method_name).each do |klass|
139
+ log{{ :subclass => klass.name }}
140
+ build_tracing_wrappers(klass, method_name)
141
+ end
142
+ end
143
+ @probe_orders.clear
144
+ end
145
+
146
+ def get_method(klass, method_name)
147
+ klass.instance_method(method_name)
148
+ end
149
+
150
+ def define_trace_method(target, method)
151
+ target.class_exec() do
152
+ define_method(method.name) do |*args, &block|
153
+ ProbeRunner::probe_run(self, method.owner.name, :instance, args, caller(0)[0], method.name) do
154
+ method.bind(self).call(*args, &block)
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def build_tracing_wrappers(target, method_name)
161
+ return if @probed.has_key?([target,method_name])
162
+ @probed[[target,method_name]] = true
163
+
164
+ meth = get_method(target, method_name)
165
+
166
+ log{ {:tracing => meth } }
167
+ define_trace_method(target, meth)
168
+ rescue NameError => ne
169
+ log{ {:not_tracing => NameError } }
170
+ end
171
+ end
172
+
173
+ class ClassProbe < Probe
174
+ def local_method_defs(klass)
175
+ klass.singleton_methods(false)
176
+ end
177
+
178
+ def get_method(klass, method_name)
179
+ (class << klass; self; end).instance_method(method_name)
180
+ end
181
+
182
+ def define_trace_method(target, method)
183
+ (class << target; self; end).class_exec() do
184
+ define_method(method.name) do |*args, &block|
185
+ ProbeRunner::probe_run(self, target.name, :class, args, caller(0)[0], method.name) do
186
+ method.bind(self).call(*args, &block)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ class InstanceProbe < Probe
194
+ end
195
+ end
196
+ end