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.
- data/History.txt +45 -0
- data/MIT-LICENSE.txt +19 -0
- data/README.md +123 -0
- data/Rakefile +24 -0
- data/Thorfile +113 -0
- data/lib/insight.rb +17 -0
- data/lib/insight/app.rb +189 -0
- data/lib/insight/database.rb +186 -0
- data/lib/insight/enable-button.rb +43 -0
- data/lib/insight/filtered_backtrace.rb +45 -0
- data/lib/insight/instrumentation.rb +9 -0
- data/lib/insight/instrumentation/backstage.rb +10 -0
- data/lib/insight/instrumentation/client.rb +20 -0
- data/lib/insight/instrumentation/instrument.rb +109 -0
- data/lib/insight/instrumentation/package-definition.rb +58 -0
- data/lib/insight/instrumentation/probe-definition.rb +20 -0
- data/lib/insight/instrumentation/probe.rb +199 -0
- data/lib/insight/instrumentation/setup.rb +32 -0
- data/lib/insight/logger.rb +55 -0
- data/lib/insight/options.rb +102 -0
- data/lib/insight/panel.rb +119 -0
- data/lib/insight/panel_app.rb +31 -0
- data/lib/insight/panels-content.rb +22 -0
- data/lib/insight/panels-header.rb +18 -0
- data/lib/insight/panels/active_record_panel.rb +46 -0
- data/lib/insight/panels/cache_panel.rb +69 -0
- data/lib/insight/panels/cache_panel/panel_app.rb +46 -0
- data/lib/insight/panels/cache_panel/stats.rb +98 -0
- data/lib/insight/panels/log_panel.rb +54 -0
- data/lib/insight/panels/memory_panel.rb +32 -0
- data/lib/insight/panels/rails_info_panel.rb +19 -0
- data/lib/insight/panels/redis_panel.rb +42 -0
- data/lib/insight/panels/redis_panel/redis_extension.rb +23 -0
- data/lib/insight/panels/redis_panel/stats.rb +50 -0
- data/lib/insight/panels/request_variables_panel.rb +70 -0
- data/lib/insight/panels/speedtracer_panel.rb +89 -0
- data/lib/insight/panels/speedtracer_panel/trace-app.rb +52 -0
- data/lib/insight/panels/speedtracer_panel/tracer.rb +212 -0
- data/lib/insight/panels/sql_panel.rb +53 -0
- data/lib/insight/panels/sql_panel/panel_app.rb +37 -0
- data/lib/insight/panels/sql_panel/query.rb +94 -0
- data/lib/insight/panels/templates_panel.rb +58 -0
- data/lib/insight/panels/templates_panel/rendering.rb +81 -0
- data/lib/insight/panels/timer_panel.rb +40 -0
- data/lib/insight/params_signature.rb +61 -0
- data/lib/insight/public/__insight__/bookmarklet.html +10 -0
- data/lib/insight/public/__insight__/bookmarklet.js +223 -0
- data/lib/insight/public/__insight__/insight.css +235 -0
- data/lib/insight/public/__insight__/insight.js +123 -0
- data/lib/insight/public/__insight__/jquery-1.3.2.js +4376 -0
- data/lib/insight/public/__insight__/jquery.tablesorter.min.js +1 -0
- data/lib/insight/public/__insight__/spinner.gif +0 -0
- data/lib/insight/rack_static_bug_avoider.rb +16 -0
- data/lib/insight/redirect_interceptor.rb +25 -0
- data/lib/insight/render.rb +72 -0
- data/lib/insight/request-recorder.rb +23 -0
- data/lib/insight/toolbar.rb +63 -0
- data/lib/insight/views/enable-button.html.erb +1 -0
- data/lib/insight/views/error.html.erb +17 -0
- data/lib/insight/views/headers_fragment.html.erb +20 -0
- data/lib/insight/views/panels/active_record.html.erb +17 -0
- data/lib/insight/views/panels/cache.html.erb +93 -0
- data/lib/insight/views/panels/execute_sql.html.erb +32 -0
- data/lib/insight/views/panels/explain_sql.html.erb +32 -0
- data/lib/insight/views/panels/log.html.erb +21 -0
- data/lib/insight/views/panels/profile_sql.html.erb +32 -0
- data/lib/insight/views/panels/rails_info.html.erb +19 -0
- data/lib/insight/views/panels/redis.html.erb +46 -0
- data/lib/insight/views/panels/request_variables.html.erb +25 -0
- data/lib/insight/views/panels/speedtracer/serverevent.html.erb +10 -0
- data/lib/insight/views/panels/speedtracer/servertrace.html.erb +12 -0
- data/lib/insight/views/panels/speedtracer/traces.html.erb +18 -0
- data/lib/insight/views/panels/sql.html.erb +43 -0
- data/lib/insight/views/panels/templates.html.erb +6 -0
- data/lib/insight/views/panels/timer.html.erb +19 -0
- data/lib/insight/views/panels/view_cache.html.erb +19 -0
- data/lib/insight/views/redirect.html.erb +16 -0
- data/lib/insight/views/request_fragment.html.erb +25 -0
- data/lib/insight/views/toolbar.html.erb +29 -0
- data/lib/logical-insight.rb +1 -0
- data/spec/custom_matchers.rb +31 -0
- data/spec/fixtures/config.ru +8 -0
- data/spec/fixtures/dummy_panel.rb +2 -0
- data/spec/fixtures/sample_app.rb +72 -0
- data/spec/insight/panels/active_record_panel_spec.rb +42 -0
- data/spec/insight/panels/cache_panel_spec.rb +176 -0
- data/spec/insight/panels/log_panel_spec.rb +44 -0
- data/spec/insight/panels/memory_panel_spec.rb +19 -0
- data/spec/insight/panels/mongo_panel_spec_pending.rb +50 -0
- data/spec/insight/panels/rails_info_panel_spec.rb +27 -0
- data/spec/insight/panels/redis_panel_spec.rb +66 -0
- data/spec/insight/panels/sql_panel_spec.rb +145 -0
- data/spec/insight/panels/templates_panel_spec.rb +84 -0
- data/spec/insight/panels/timer_panel_spec.rb +36 -0
- data/spec/insight_spec.rb +141 -0
- data/spec/instrumentation_spec.rb +188 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +93 -0
- metadata +187 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
|
|
3
|
+
class RedisPanel < Panel
|
|
4
|
+
require "insight/panels/redis_panel/redis_extension"
|
|
5
|
+
|
|
6
|
+
require "insight/panels/redis_panel/stats"
|
|
7
|
+
|
|
8
|
+
def self.record(redis_command_args, backtrace, &block)
|
|
9
|
+
return block.call unless Insight.enabled?
|
|
10
|
+
|
|
11
|
+
start_time = Time.now
|
|
12
|
+
result = block.call
|
|
13
|
+
total_time = Time.now - start_time
|
|
14
|
+
stats.record_call(total_time * 1_000, redis_command_args, backtrace)
|
|
15
|
+
return result
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.reset
|
|
19
|
+
Thread.current["insight.redis"] = Stats.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.stats
|
|
23
|
+
Thread.current["insight.redis"] ||= Stats.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def name
|
|
27
|
+
"redis"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def heading
|
|
31
|
+
"Redis: %.2fms (#{self.class.stats.queries.size} calls)" % self.class.stats.time
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def content
|
|
35
|
+
result = render_template "panels/redis", :stats => self.class.stats
|
|
36
|
+
self.class.reset
|
|
37
|
+
return result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
if defined?(Redis)
|
|
2
|
+
Redis.class_eval do
|
|
3
|
+
if Redis.methods.include?('call_command') # older versions of redis-rb
|
|
4
|
+
def call_command_with_insight(*argv)
|
|
5
|
+
Insight::RedisPanel.record(argv, Kernel.caller) do
|
|
6
|
+
call_command_without_insight(*argv)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
alias_method_chain :call_command, :insight
|
|
11
|
+
|
|
12
|
+
elsif defined?(Redis::Client) # newer versions of redis-rb
|
|
13
|
+
|
|
14
|
+
Redis::Client.class_eval do
|
|
15
|
+
def call_with_insight(*argv)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
alias_method_chain :call, :insight
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
class RedisPanel
|
|
3
|
+
|
|
4
|
+
class Stats
|
|
5
|
+
class Query
|
|
6
|
+
include Insight::FilteredBacktrace
|
|
7
|
+
|
|
8
|
+
attr_reader :time
|
|
9
|
+
attr_reader :command
|
|
10
|
+
|
|
11
|
+
def initialize(time, command_args, backtrace)
|
|
12
|
+
@time = time
|
|
13
|
+
@command = command_args.inspect
|
|
14
|
+
@backtrace = backtrace
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def display_time
|
|
18
|
+
"%.2fms" % time
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :calls
|
|
23
|
+
attr_reader :queries
|
|
24
|
+
|
|
25
|
+
def initialize
|
|
26
|
+
@queries = []
|
|
27
|
+
@calls = 0
|
|
28
|
+
@time = 0.0
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def record_call(time, command_args, backtrace)
|
|
32
|
+
@queries << Query.new(time, command_args, backtrace)
|
|
33
|
+
@calls += 1
|
|
34
|
+
@time += time
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def display_time
|
|
38
|
+
"%.2fms" % time
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def time
|
|
42
|
+
@queries.inject(0) do |memo, query|
|
|
43
|
+
memo + query.time
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
class RequestVariablesPanel < Panel
|
|
3
|
+
def initialize(app)
|
|
4
|
+
super
|
|
5
|
+
|
|
6
|
+
table_setup("request_variables")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def name
|
|
10
|
+
"request_variables"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def after(env,status,headers,body)
|
|
14
|
+
sections = {}
|
|
15
|
+
sections["GET"] = sort(@request.GET) if @request.GET.any?
|
|
16
|
+
sections["POST"] = sort(@request.POST) if @request.POST.any?
|
|
17
|
+
sections["Session"] = sort(@request.env["rack.session"]) if @request.env["rack.session"] && @request.env["rack.session"].any?
|
|
18
|
+
sections["Cookies"] = sort(@request.env["rack.request.cookie_hash"]) if @request.env["rack.request.cookie_hash"] && @request.env["rack.request.cookie_hash"].any?
|
|
19
|
+
server, rack = split_and_filter_env(@env)
|
|
20
|
+
sections["SERVER VARIABLES"] = sort(server)
|
|
21
|
+
sections["Rack ENV"] = sort(rack)
|
|
22
|
+
|
|
23
|
+
# require 'pp'
|
|
24
|
+
# ::File.open("sections.dump", "w") do |file|
|
|
25
|
+
# PP.pp(sections, file)
|
|
26
|
+
# end
|
|
27
|
+
store(env, sections)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def heading
|
|
31
|
+
"Rack Env"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def content_for_request(number)
|
|
35
|
+
sections = retrieve(number).first
|
|
36
|
+
|
|
37
|
+
render_template "panels/request_variables", :sections => sections
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
def sort(hash)
|
|
42
|
+
scrub(hash.sort_by { |k, v| k.to_s })
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def scrub(enum)
|
|
46
|
+
enum.map do |k,v|
|
|
47
|
+
if Hash === v
|
|
48
|
+
[k, v.inspect]
|
|
49
|
+
else
|
|
50
|
+
[k, v.to_s]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def split_and_filter_env(env)
|
|
56
|
+
server, rack = {}, {}
|
|
57
|
+
env.each do |k,v|
|
|
58
|
+
if k.index("rack.") == 0
|
|
59
|
+
rack[k] = v
|
|
60
|
+
elsif k.index("insight") == 0 or k.index("insight") == 0
|
|
61
|
+
#don't output the insight variables - especially secret_key
|
|
62
|
+
else
|
|
63
|
+
server[k] = v
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
return server, rack
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'yajl'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
#Means no Chrome Speedtracer...
|
|
5
|
+
end
|
|
6
|
+
require 'uuid'
|
|
7
|
+
require 'insight/panels/speedtracer_panel/trace-app'
|
|
8
|
+
require 'insight/panels/speedtracer_panel/tracer'
|
|
9
|
+
|
|
10
|
+
module Insight
|
|
11
|
+
module SpeedTracer
|
|
12
|
+
class Panel < ::Insight::Panel
|
|
13
|
+
|
|
14
|
+
def initialize(app)
|
|
15
|
+
@app = app
|
|
16
|
+
@uuid = UUID.new
|
|
17
|
+
table_setup("speedtracer", "uuid")
|
|
18
|
+
key_sql_template("'%s'")
|
|
19
|
+
|
|
20
|
+
@tracer = Tracer.new(@table)
|
|
21
|
+
probe(@tracer) do
|
|
22
|
+
instrument("ActiveSupport::Notifications") do
|
|
23
|
+
class_probe :instrument
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
instrument("ActionView::Rendering") do
|
|
27
|
+
instance_probe :render
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
instrument("ActionView::Helpers::RecordTagHelper") do
|
|
31
|
+
instance_probe :content_tag_for
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
instrument("ActionView::Partials::PartialRenderer") do
|
|
35
|
+
instance_probe :render, :find_template, :render_collection, :collection_with_template, :collection_without_template, :partial_path, :collection_paths
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
instrument("ActionView::Template") do
|
|
39
|
+
instance_probe :render, :compile
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
instrument("ActiveRecord::Base") do
|
|
43
|
+
class_probe :find, :find_by_sql, :all, :first, :last, :count, :delete_all
|
|
44
|
+
instance_probe :save, :save!, :destroy, :delete
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
instrument("ActionController::Base") do
|
|
48
|
+
instance_probe :process, :render
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
super
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def call(env)
|
|
57
|
+
env['insight.speedtracer-id'] = @uuid.generate
|
|
58
|
+
|
|
59
|
+
status, headers, body = @app.call(env)
|
|
60
|
+
|
|
61
|
+
store(env, env['insight.speedtracer-id'], env['insight.speedtracer-record'])
|
|
62
|
+
headers['X-TraceUrl'] = '__insight__/speedtracer?id=' + env['insight.speedtracer-id']
|
|
63
|
+
return [status, headers, body]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.panel_mappings
|
|
67
|
+
{ "speedtracer" => TraceApp.new }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def name
|
|
71
|
+
"speedtracer"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def heading
|
|
75
|
+
"#{table_length} traces"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def content_for_request(request_id)
|
|
79
|
+
trace = retrieve(request_id).first
|
|
80
|
+
return "" if trace.nil?
|
|
81
|
+
advice = []
|
|
82
|
+
if not defined?(Yajl)
|
|
83
|
+
advice << "yajl-ruby not installed - Speedtracer server events won't be available"
|
|
84
|
+
end
|
|
85
|
+
render_template "panels/speedtracer/traces", :trace => trace, :advice => advice
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
module SpeedTracer
|
|
3
|
+
class TraceApp
|
|
4
|
+
include Database::RequestDataClient
|
|
5
|
+
|
|
6
|
+
CONTENT_TYPE = 'application/json;charset=UTF-8'.freeze
|
|
7
|
+
|
|
8
|
+
FourOhFour = [404, {"Content-Type" => "text/html"}, "App tracker doesn't know that path or id"].freeze
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
table_setup("speedtracer", "uuid")
|
|
12
|
+
key_sql_template = "'%s'"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(env)
|
|
16
|
+
resp = Rack::Response.new('', 200)
|
|
17
|
+
resp['Content-Type'] = CONTENT_TYPE
|
|
18
|
+
|
|
19
|
+
case env['REQUEST_METHOD']
|
|
20
|
+
when 'HEAD' then
|
|
21
|
+
# SpeedTracer dispatches HEAD requests to verify the
|
|
22
|
+
# tracer endpoint when it detects the X-TraceUrl
|
|
23
|
+
# header for the first time. After the initial load
|
|
24
|
+
# the verification is cached by the extension.
|
|
25
|
+
#
|
|
26
|
+
# By default, we'll return 200.
|
|
27
|
+
|
|
28
|
+
when 'GET' then
|
|
29
|
+
# GET requests for specific trace are generated by
|
|
30
|
+
# the extension when the user expands the network
|
|
31
|
+
# resource tab. Hence, server-side tracer data is
|
|
32
|
+
# request on-demand, and we need to store it for
|
|
33
|
+
# some time.
|
|
34
|
+
#
|
|
35
|
+
|
|
36
|
+
qs = Rack::Utils.parse_query(env['QUERY_STRING'])
|
|
37
|
+
if qs['id'] && (trace = @table.retrieve("uuid = '#{qs['id']}'"))
|
|
38
|
+
resp.write trace.to_json
|
|
39
|
+
else
|
|
40
|
+
# Invalid request or missing request trace id
|
|
41
|
+
return FourOhFour
|
|
42
|
+
end
|
|
43
|
+
else
|
|
44
|
+
# SpeedTracer should only issue GET & HEAD requests
|
|
45
|
+
resp.status = 400
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
return resp.finish
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
module SpeedTracer
|
|
3
|
+
class Tracer
|
|
4
|
+
def initialize(table)
|
|
5
|
+
@pstack = []
|
|
6
|
+
@table = table
|
|
7
|
+
@event_id = 0
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def request_start(env, start)
|
|
11
|
+
id, method, uri = env.values_at("insight.speedtracer-id", "REQUEST_METHOD", "PATH_INFO")
|
|
12
|
+
@pstack.push RequestRecord.new(id, method, uri)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def request_finish(env, status, headers, body, timing)
|
|
16
|
+
env["insight.speedtracer-record"] = @pstack.pop
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def before_detect(method_call, arguments)
|
|
20
|
+
@event_id += 1
|
|
21
|
+
|
|
22
|
+
arguments_string = make_string_of(arguments)
|
|
23
|
+
#XXX ServerEvent use method call...
|
|
24
|
+
event = ServerEvent.new(method_call, arguments_string)
|
|
25
|
+
@pstack.push event
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def after_detect(method_call, timing, arguments, result)
|
|
29
|
+
event = @pstack.pop
|
|
30
|
+
if event.nil?
|
|
31
|
+
else
|
|
32
|
+
event.finish
|
|
33
|
+
|
|
34
|
+
unless (parent = @pstack.last).nil?
|
|
35
|
+
parent.children.push event
|
|
36
|
+
else
|
|
37
|
+
@children.push event
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def make_string_of(array)
|
|
43
|
+
array.map do |item|
|
|
44
|
+
short_string(item)
|
|
45
|
+
end.join(",")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def short_string(item, max_per_elem = 50)
|
|
49
|
+
begin
|
|
50
|
+
string = item.inspect
|
|
51
|
+
if string.length > max_per_elem
|
|
52
|
+
case item
|
|
53
|
+
when NilClass
|
|
54
|
+
"nil"
|
|
55
|
+
when Hash
|
|
56
|
+
"{ " + item.map do |key, value|
|
|
57
|
+
short_string(key, 15) + "=>" + short_string(value, 30)
|
|
58
|
+
end.join(", ") + " }"
|
|
59
|
+
when find_constant("ActionView::Base")
|
|
60
|
+
tmpl = item.template
|
|
61
|
+
if tmpl.nil?
|
|
62
|
+
item.path.inspect
|
|
63
|
+
else
|
|
64
|
+
[tmpl.base_path, tmpl.name].join("/")
|
|
65
|
+
end
|
|
66
|
+
when find_constant("ActiveRecord::Base")
|
|
67
|
+
string = "#{item.class.name}(#{item.id})"
|
|
68
|
+
else
|
|
69
|
+
string = item.class.name
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
string
|
|
73
|
+
end
|
|
74
|
+
rescue Exception => ex
|
|
75
|
+
"..."
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class TraceRecord
|
|
82
|
+
include Render
|
|
83
|
+
def initialize
|
|
84
|
+
@start = Time.now
|
|
85
|
+
@children = []
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
attr_accessor :children
|
|
89
|
+
attr_reader :start
|
|
90
|
+
|
|
91
|
+
def finish
|
|
92
|
+
@finish ||= Time.now
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def time_in_children
|
|
96
|
+
@children.inject(0) do |time, child|
|
|
97
|
+
time + child.duration
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def duration
|
|
102
|
+
((@finish - @start) * 1000).to_i
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def to_json
|
|
106
|
+
Yajl::Encoder.encode(hash_representation, :pretty => true, :indent => ' ')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
# all timestamps in SpeedTracer are in milliseconds
|
|
111
|
+
def range(start, finish)
|
|
112
|
+
{
|
|
113
|
+
'duration' => ((finish - start) * 1000).to_i,
|
|
114
|
+
'start' => [start.to_i, start.usec/1000].join(''),
|
|
115
|
+
#'end' => [finish.to_i, finish.usec/1000].join('')
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def symbolize_hash(hash)
|
|
120
|
+
symbolled_hash = {}
|
|
121
|
+
hash.each_key do |key|
|
|
122
|
+
if String === key
|
|
123
|
+
next if hash.has_key?(key.to_sym)
|
|
124
|
+
symbolled_hash[key.to_sym] = hash[key]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
hash.merge!(symbolled_hash)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class ServerEvent < TraceRecord
|
|
132
|
+
attr_reader :name
|
|
133
|
+
|
|
134
|
+
def initialize(method_call, arguments)
|
|
135
|
+
super()
|
|
136
|
+
@arguments = arguments
|
|
137
|
+
@name = "#{method_call.context}#{method_call.kind == :instance ? "#" : "::"}#{method_call.method}(#{arguments})"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def hash_representation
|
|
141
|
+
{
|
|
142
|
+
'range' => range(@start, @finish),
|
|
143
|
+
'operation' => {
|
|
144
|
+
# 'sourceCodeLocation' => {
|
|
145
|
+
# 'className' => @file,
|
|
146
|
+
# 'methodName' => @method,
|
|
147
|
+
# 'lineNumber' => @line
|
|
148
|
+
# },
|
|
149
|
+
'type' => 'METHOD',
|
|
150
|
+
'label' => @name
|
|
151
|
+
},
|
|
152
|
+
'children' => @children
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def to_html
|
|
157
|
+
render_template('panels/speedtracer/serverevent',
|
|
158
|
+
{:self_time => duration - time_in_children}.merge(symbolize_hash(hash_representation)))
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class RequestRecord < TraceRecord
|
|
164
|
+
def initialize(id, method, uri)
|
|
165
|
+
super()
|
|
166
|
+
|
|
167
|
+
@id = id
|
|
168
|
+
@method = method
|
|
169
|
+
@uri = uri
|
|
170
|
+
@event_id = 0
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def uuid
|
|
174
|
+
@id
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def hash_representation
|
|
178
|
+
finish
|
|
179
|
+
{ 'trace' => {
|
|
180
|
+
|
|
181
|
+
'url' => "/__insight__/speedtracer?id=#@id",
|
|
182
|
+
|
|
183
|
+
'frameStack' => {
|
|
184
|
+
|
|
185
|
+
'range' => range(@start, @finish),
|
|
186
|
+
'operation' => {
|
|
187
|
+
'type' => 'HTTP',
|
|
188
|
+
'label' => [@method, @uri].join(' ')
|
|
189
|
+
},
|
|
190
|
+
'children' => @children
|
|
191
|
+
|
|
192
|
+
}, #end frameStack
|
|
193
|
+
|
|
194
|
+
'resources' => {
|
|
195
|
+
'Application' => '/', #Should get the Rails app name...
|
|
196
|
+
'Application.endpoint' => '/' #Should get the env path thing
|
|
197
|
+
}, #From what I can tell, Speed Tracer treats this whole hash as optional
|
|
198
|
+
|
|
199
|
+
'range' => range(@start, @finish)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def to_html
|
|
205
|
+
hash = hash_representation
|
|
206
|
+
extra = {:self_time => duration - time_in_children}
|
|
207
|
+
"<a href='#{hash['trace']['url']}'>Raw JSON</a>\n" +
|
|
208
|
+
render_template('panels/speedtracer/serverevent', extra.merge(symbolize_hash(hash['trace']['frameStack'])))
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|