rack-mini-profiler 0.9.2 → 0.9.3
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.
Potentially problematic release.
This version of rack-mini-profiler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +17 -3
- data/lib/html/includes.css +15 -4
- data/lib/html/includes.js +93 -58
- data/lib/html/includes.less +21 -5
- data/lib/html/includes.tmpl +49 -49
- data/lib/html/list.tmpl +8 -8
- data/lib/mini_profiler/asset_version.rb +5 -0
- data/lib/mini_profiler/client_settings.rb +3 -3
- data/lib/mini_profiler/config.rb +11 -11
- data/lib/mini_profiler/gc_profiler.rb +10 -10
- data/lib/mini_profiler/profiler.rb +49 -71
- data/lib/mini_profiler/profiling_methods.rb +15 -17
- data/lib/mini_profiler/storage/file_store.rb +4 -4
- data/lib/mini_profiler/storage/memcache_store.rb +5 -7
- data/lib/mini_profiler/storage/memory_store.rb +56 -27
- data/lib/mini_profiler/storage/redis_store.rb +19 -11
- data/lib/mini_profiler/timer_struct/base.rb +33 -0
- data/lib/mini_profiler/timer_struct/client.rb +89 -0
- data/lib/mini_profiler/timer_struct/custom.rb +22 -0
- data/lib/mini_profiler/timer_struct/page.rb +62 -0
- data/lib/mini_profiler/timer_struct/request.rb +126 -0
- data/lib/mini_profiler/timer_struct/sql.rb +59 -0
- data/lib/mini_profiler/version.rb +2 -2
- data/lib/patches/db/activerecord.rb +42 -0
- data/lib/patches/db/moped.rb +12 -0
- data/lib/patches/db/mysql2.rb +30 -0
- data/lib/patches/db/pg.rb +104 -0
- data/lib/patches/db/plucky.rb +47 -0
- data/lib/patches/db/rsolr.rb +24 -0
- data/lib/patches/db/sequel.rb +10 -0
- data/lib/patches/sql_patches.rb +17 -255
- data/lib/rack-mini-profiler.rb +28 -0
- data/rack-mini-profiler.gemspec +6 -2
- metadata +16 -8
- data/lib/mini_profiler/client_timer_struct.rb +0 -78
- data/lib/mini_profiler/custom_timer_struct.rb +0 -22
- data/lib/mini_profiler/page_timer_struct.rb +0 -58
- data/lib/mini_profiler/request_timer_struct.rb +0 -115
- data/lib/mini_profiler/sql_timer_struct.rb +0 -58
- data/lib/mini_profiler/timer_struct.rb +0 -33
@@ -39,9 +39,9 @@ module Rack
|
|
39
39
|
@expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
|
40
40
|
raise ArgumentError.new :path unless @path
|
41
41
|
@timer_struct_cache = FileCache.new(@path, "mp_timers")
|
42
|
-
@timer_struct_lock
|
43
|
-
@user_view_cache
|
44
|
-
@user_view_lock
|
42
|
+
@timer_struct_lock = Mutex.new
|
43
|
+
@user_view_cache = FileCache.new(@path, "mp_views")
|
44
|
+
@user_view_lock = Mutex.new
|
45
45
|
|
46
46
|
me = self
|
47
47
|
t = CacheCleanupThread.new do
|
@@ -77,7 +77,7 @@ module Rack
|
|
77
77
|
|
78
78
|
def save(page_struct)
|
79
79
|
@timer_struct_lock.synchronize {
|
80
|
-
@timer_struct_cache[page_struct[
|
80
|
+
@timer_struct_cache[page_struct[:id]] = page_struct
|
81
81
|
}
|
82
82
|
end
|
83
83
|
|
@@ -3,25 +3,23 @@ module Rack
|
|
3
3
|
class MemcacheStore < AbstractStore
|
4
4
|
|
5
5
|
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
|
-
MAX_RETRIES
|
6
|
+
MAX_RETRIES = 10
|
7
7
|
|
8
8
|
def initialize(args = nil)
|
9
9
|
require 'dalli' unless defined? Dalli
|
10
10
|
args ||= {}
|
11
|
-
@prefix
|
12
|
-
@client
|
11
|
+
@prefix = args[:prefix] || "MPMemcacheStore"
|
12
|
+
@client = args[:client] || Dalli::Client.new
|
13
13
|
@expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
|
14
14
|
end
|
15
15
|
|
16
16
|
def save(page_struct)
|
17
|
-
@client.set("#{@prefix}#{page_struct[
|
17
|
+
@client.set("#{@prefix}#{page_struct[:id]}", Marshal::dump(page_struct), @expires_in_seconds)
|
18
18
|
end
|
19
19
|
|
20
20
|
def load(id)
|
21
21
|
raw = @client.get("#{@prefix}#{id}")
|
22
|
-
if raw
|
23
|
-
Marshal::load raw
|
24
|
-
end
|
22
|
+
Marshal::load(raw) if raw
|
25
23
|
end
|
26
24
|
|
27
25
|
def set_unviewed(user, id)
|
@@ -4,48 +4,77 @@ module Rack
|
|
4
4
|
|
5
5
|
# Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
6
6
|
class CacheCleanupThread < Thread
|
7
|
+
|
8
|
+
def initialize(interval, cycle, store)
|
9
|
+
super
|
10
|
+
@store = store
|
11
|
+
@interval = interval
|
12
|
+
@cycle = cycle
|
13
|
+
@cycle_count = 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def should_cleanup?
|
17
|
+
@cycle_count * @interval >= @cycle
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# We don't want to hit the filesystem every 10s to clean up the cache so we need to do a bit of
|
22
|
+
# accounting to avoid sleeping that entire time. We don't want to sleep for the entire period because
|
23
|
+
# it means the thread will stay live in hot deployment scenarios, keeping a potentially large memory
|
24
|
+
# graph from being garbage collected upon undeploy.
|
25
|
+
def sleepy_run
|
26
|
+
cleanup if should_cleanup?
|
27
|
+
sleep(@interval)
|
28
|
+
increment_cycle
|
29
|
+
end
|
30
|
+
|
31
|
+
def cleanup
|
32
|
+
@store.cleanup_cache
|
33
|
+
@cycle_count = 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def cycle_count
|
37
|
+
@cycle_count
|
38
|
+
end
|
39
|
+
|
40
|
+
def increment_cycle
|
41
|
+
@cycle_count += 1
|
42
|
+
end
|
7
43
|
end
|
8
44
|
|
9
45
|
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
46
|
+
CLEANUP_INTERVAL = 10
|
47
|
+
CLEANUP_CYCLE = 3600
|
10
48
|
|
11
49
|
def initialize(args = nil)
|
12
50
|
args ||= {}
|
13
|
-
@expires_in_seconds = args
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@user_view_cache = {}
|
51
|
+
@expires_in_seconds = args.fetch(:expires_in) { EXPIRES_IN_SECONDS }
|
52
|
+
initialize_locks
|
53
|
+
initialize_cleanup_thread(args)
|
54
|
+
end
|
18
55
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
56
|
+
def initialize_locks
|
57
|
+
@timer_struct_lock = Mutex.new
|
58
|
+
@user_view_lock = Mutex.new
|
59
|
+
@timer_struct_cache = {}
|
60
|
+
@user_view_cache = {}
|
61
|
+
end
|
25
62
|
|
63
|
+
#FIXME: use weak ref, trouble it may be broken in 1.9 so need to use the 'ref' gem
|
64
|
+
def initialize_cleanup_thread(args={})
|
65
|
+
cleanup_interval = args.fetch(:cleanup_interval) { CLEANUP_INTERVAL }
|
66
|
+
cleanup_cycle = args.fetch(:cleanup_cycle) { CLEANUP_CYCLE }
|
67
|
+
t = CacheCleanupThread.new(cleanup_interval, cleanup_cycle, self) do |t|
|
26
68
|
until Thread.current[:should_exit] do
|
27
|
-
|
28
|
-
# accounting to avoid sleeping that entire time. We don't want to sleep for the entire period because
|
29
|
-
# it means the thread will stay live in hot deployment scenarios, keeping a potentially large memory
|
30
|
-
# graph from being garbage collected upon undeploy.
|
31
|
-
if cycle_count * interval >= cleanup_cache_cycle
|
32
|
-
cycle_count = 1
|
33
|
-
me.cleanup_cache
|
34
|
-
end
|
35
|
-
|
36
|
-
sleep(interval)
|
37
|
-
cycle_count += 1
|
69
|
+
self.sleepy_run
|
38
70
|
end
|
39
71
|
end
|
40
|
-
|
41
72
|
at_exit { t[:should_exit] = true }
|
42
|
-
|
43
|
-
t
|
44
73
|
end
|
45
74
|
|
46
75
|
def save(page_struct)
|
47
76
|
@timer_struct_lock.synchronize {
|
48
|
-
@timer_struct_cache[page_struct[
|
77
|
+
@timer_struct_cache[page_struct[:id]] = page_struct
|
49
78
|
}
|
50
79
|
end
|
51
80
|
|
@@ -78,7 +107,7 @@ module Rack
|
|
78
107
|
def cleanup_cache
|
79
108
|
expire_older_than = ((Time.now.to_f - @expires_in_seconds) * 1000).to_i
|
80
109
|
@timer_struct_lock.synchronize {
|
81
|
-
@timer_struct_cache.delete_if { |k, v| v[
|
110
|
+
@timer_struct_cache.delete_if { |k, v| v[:started] < expire_older_than }
|
82
111
|
}
|
83
112
|
end
|
84
113
|
end
|
@@ -5,25 +5,32 @@ module Rack
|
|
5
5
|
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
6
|
|
7
7
|
def initialize(args = nil)
|
8
|
-
@args
|
9
|
-
@prefix
|
10
|
-
@redis_connection
|
8
|
+
@args = args || {}
|
9
|
+
@prefix = @args.delete(:prefix) || 'MPRedisStore'
|
10
|
+
@redis_connection = @args.delete(:connection)
|
11
11
|
@expires_in_seconds = @args.delete(:expires_in) || EXPIRES_IN_SECONDS
|
12
12
|
end
|
13
13
|
|
14
14
|
def save(page_struct)
|
15
|
-
redis.setex "#{@prefix}#{page_struct[
|
15
|
+
redis.setex "#{@prefix}#{page_struct[:id]}", @expires_in_seconds, Marshal::dump(page_struct)
|
16
16
|
end
|
17
17
|
|
18
18
|
def load(id)
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
key = "#{@prefix}#{id}"
|
20
|
+
raw = redis.get key
|
21
|
+
begin
|
22
|
+
Marshal::load(raw) if raw
|
23
|
+
rescue
|
24
|
+
# bad format, junk old data
|
25
|
+
redis.del key
|
26
|
+
nil
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
25
30
|
def set_unviewed(user, id)
|
26
|
-
|
31
|
+
key = "#{@prefix}-#{user}-v"
|
32
|
+
redis.sadd key, id
|
33
|
+
redis.expire key, @expires_in_seconds
|
27
34
|
end
|
28
35
|
|
29
36
|
def set_viewed(user, id)
|
@@ -44,9 +51,10 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
44
51
|
private
|
45
52
|
|
46
53
|
def redis
|
47
|
-
|
48
|
-
|
49
|
-
|
54
|
+
@redis_connection ||= begin
|
55
|
+
require 'redis' unless defined? Redis
|
56
|
+
Redis.new(@args)
|
57
|
+
end
|
50
58
|
end
|
51
59
|
|
52
60
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
module TimerStruct
|
4
|
+
# A base class for timing structures
|
5
|
+
class Base
|
6
|
+
|
7
|
+
def initialize(attrs={})
|
8
|
+
@attributes = attrs
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes
|
12
|
+
@attributes ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](name)
|
16
|
+
attributes[name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(name, val)
|
20
|
+
attributes[name] = val
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_json(*a)
|
25
|
+
# this does could take in an option hash, but the only interesting there is max_nesting.
|
26
|
+
# if this becomes an option we could increase
|
27
|
+
::JSON.generate( @attributes, :max_nesting => 100 )
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
module TimerStruct
|
4
|
+
|
5
|
+
# This class holds the client timings
|
6
|
+
class Client < TimerStruct::Base
|
7
|
+
|
8
|
+
def self.init_instrumentation
|
9
|
+
%Q{
|
10
|
+
<script type="text/javascript">
|
11
|
+
mPt=function(){var t=[];return{t:t,probe:function(n){t.push({d:new Date(),n:n})}}}()
|
12
|
+
</script>
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
# used by Railtie to instrument asset_tag for JS / CSS
|
17
|
+
def self.instrument(name, orig)
|
18
|
+
probe = "<script>mPt.probe('#{name}')</script>"
|
19
|
+
wrapped = probe
|
20
|
+
wrapped << orig
|
21
|
+
wrapped << probe
|
22
|
+
wrapped
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def initialize(env={})
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def redirect_count
|
31
|
+
self[:redirect_count]
|
32
|
+
end
|
33
|
+
|
34
|
+
def timings
|
35
|
+
self[:timings]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.init_from_form_data(env, page_struct)
|
39
|
+
timings = []
|
40
|
+
clientTimes, clientPerf, baseTime = nil
|
41
|
+
form = env['rack.request.form_hash']
|
42
|
+
|
43
|
+
clientPerf = form['clientPerformance'] if form
|
44
|
+
clientTimes = clientPerf['timing'] if clientPerf
|
45
|
+
baseTime = clientTimes['navigationStart'].to_i if clientTimes
|
46
|
+
return unless clientTimes && baseTime
|
47
|
+
|
48
|
+
probes = form['clientProbes']
|
49
|
+
translated = {}
|
50
|
+
if probes && !["null", ""].include?(probes)
|
51
|
+
probes.each do |id, val|
|
52
|
+
name = val["n"]
|
53
|
+
translated[name] ||= {}
|
54
|
+
if translated[name][:start]
|
55
|
+
translated[name][:finish] = val["d"]
|
56
|
+
else
|
57
|
+
translated[name][:start] = val["d"]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
translated.each do |name, data|
|
63
|
+
h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
|
64
|
+
h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
|
65
|
+
timings.push(h)
|
66
|
+
end
|
67
|
+
|
68
|
+
clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
|
69
|
+
start = clientTimes[k].to_i - baseTime
|
70
|
+
finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
|
71
|
+
duration = 0
|
72
|
+
duration = finish - start if finish > start
|
73
|
+
name = k.sub(/Start$/, "").split(/(?=[A-Z])/).map{|s| s.capitalize}.join(' ')
|
74
|
+
timings.push({"Name" => name, "Start" => start, "Duration" => duration}) if start >= 0
|
75
|
+
end
|
76
|
+
|
77
|
+
clientTimes.keys.find_all{|k| !(k =~ /(End|Start)$/)}.each do |k|
|
78
|
+
timings.push("Name" => k, "Start" => clientTimes[k].to_i - baseTime, "Duration" => -1)
|
79
|
+
end
|
80
|
+
|
81
|
+
TimerStruct::Client.new.tap do |rval|
|
82
|
+
rval[:redirect_count] = env['rack.request.form_hash']['clientPerformance']['navigation']['redirect_count']
|
83
|
+
rval[:timings] = timings
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
module TimerStruct
|
4
|
+
# Timing system for a custom timers such as cache, redis, RPC, external API
|
5
|
+
# calls, etc.
|
6
|
+
class Custom < TimerStruct::Base
|
7
|
+
def initialize(type, duration_ms, page, parent)
|
8
|
+
@parent = parent
|
9
|
+
@page = page
|
10
|
+
@type = type
|
11
|
+
start_millis = ((Time.now.to_f * 1000).to_i - page[:started]) - duration_ms
|
12
|
+
super(
|
13
|
+
:type => type,
|
14
|
+
:start_milliseconds => start_millis,
|
15
|
+
:duration_milliseconds => duration_ms,
|
16
|
+
:parent_timing_id => nil
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
module TimerStruct
|
4
|
+
|
5
|
+
# TimerStruct::Page
|
6
|
+
# Root: TimerStruct::Request
|
7
|
+
# :has_many TimerStruct::Request children
|
8
|
+
# :has_many TimerStruct::Sql children
|
9
|
+
# :has_many TimerStruct::Custom children
|
10
|
+
class Page < TimerStruct::Base
|
11
|
+
def initialize(env)
|
12
|
+
timer_id = MiniProfiler.generate_id
|
13
|
+
page_name = env['PATH_INFO']
|
14
|
+
started_at = (Time.now.to_f * 1000).to_i
|
15
|
+
machine_name = env['SERVER_NAME']
|
16
|
+
super(
|
17
|
+
:id => timer_id,
|
18
|
+
:name => page_name,
|
19
|
+
:started => started_at,
|
20
|
+
:machine_name => machine_name,
|
21
|
+
:level => 0,
|
22
|
+
:user => "unknown user",
|
23
|
+
:has_user_viewed => false,
|
24
|
+
:client_timings => nil,
|
25
|
+
:duration_milliseconds => 0,
|
26
|
+
:has_trivial_timings => true,
|
27
|
+
:has_all_trivial_timings => false,
|
28
|
+
:trivial_duration_threshold_milliseconds => 2,
|
29
|
+
:head => nil,
|
30
|
+
:duration_milliseconds_in_sql => 0,
|
31
|
+
:has_sql_timings => true,
|
32
|
+
:has_duplicate_sql_timings => false,
|
33
|
+
:executed_readers => 0,
|
34
|
+
:executed_scalars => 0,
|
35
|
+
:executed_non_queries => 0,
|
36
|
+
:custom_timing_names => [],
|
37
|
+
:custom_timing_stats => {}
|
38
|
+
)
|
39
|
+
name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
|
40
|
+
self[:root] = TimerStruct::Request.createRoot(name, self)
|
41
|
+
end
|
42
|
+
|
43
|
+
def duration_ms
|
44
|
+
@attributes[:root][:duration_milliseconds]
|
45
|
+
end
|
46
|
+
|
47
|
+
def root
|
48
|
+
@attributes[:root]
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_json(*a)
|
52
|
+
attribs = @attributes.merge(
|
53
|
+
:started => '/Date(%d)/' % @attributes[:started],
|
54
|
+
:duration_milliseconds => @attributes[:root][:duration_milliseconds],
|
55
|
+
:custom_timing_names => @attributes[:custom_timing_stats].keys.sort
|
56
|
+
)
|
57
|
+
::JSON.generate(attribs, :max_nesting => 100)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
module TimerStruct
|
4
|
+
class Request < TimerStruct::Base
|
5
|
+
|
6
|
+
def self.createRoot(name, page)
|
7
|
+
TimerStruct::Request.new(name, page, nil).tap do |timer|
|
8
|
+
timer[:is_root] = true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :children_duration
|
13
|
+
|
14
|
+
def initialize(name, page, parent)
|
15
|
+
start_millis = (Time.now.to_f * 1000).to_i - page[:started]
|
16
|
+
depth = parent ? parent.depth + 1 : 0
|
17
|
+
super(
|
18
|
+
:id => MiniProfiler.generate_id,
|
19
|
+
:name => name,
|
20
|
+
:duration_milliseconds => 0,
|
21
|
+
:duration_without_children_milliseconds => 0,
|
22
|
+
:start_milliseconds => start_millis,
|
23
|
+
:parent_timing_id => nil,
|
24
|
+
:children => [],
|
25
|
+
:has_children => false,
|
26
|
+
:key_values => nil,
|
27
|
+
:has_sql_timings => false,
|
28
|
+
:has_duplicate_sql_timings => false,
|
29
|
+
:trivial_duration_threshold_milliseconds => 2,
|
30
|
+
:sql_timings => [],
|
31
|
+
:sql_timings_duration_milliseconds => 0,
|
32
|
+
:is_trivial => false,
|
33
|
+
:is_root => false,
|
34
|
+
:depth => depth,
|
35
|
+
:executed_readers => 0,
|
36
|
+
:executed_scalars => 0,
|
37
|
+
:executed_non_queries => 0,
|
38
|
+
:custom_timing_stats => {},
|
39
|
+
:custom_timings => {}
|
40
|
+
)
|
41
|
+
@children_duration = 0
|
42
|
+
@start = Time.now
|
43
|
+
@parent = parent
|
44
|
+
@page = page
|
45
|
+
end
|
46
|
+
|
47
|
+
def duration_ms
|
48
|
+
self[:duration_milliseconds]
|
49
|
+
end
|
50
|
+
|
51
|
+
def start_ms
|
52
|
+
self[:start_milliseconds]
|
53
|
+
end
|
54
|
+
|
55
|
+
def start
|
56
|
+
@start
|
57
|
+
end
|
58
|
+
|
59
|
+
def depth
|
60
|
+
self[:depth]
|
61
|
+
end
|
62
|
+
|
63
|
+
def children
|
64
|
+
self[:children]
|
65
|
+
end
|
66
|
+
|
67
|
+
def custom_timings
|
68
|
+
self[:custom_timings]
|
69
|
+
end
|
70
|
+
|
71
|
+
def sql_timings
|
72
|
+
self[:sql_timings]
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_child(name)
|
76
|
+
TimerStruct::Request.new(name, @page, self).tap do |timer|
|
77
|
+
self[:children].push(timer)
|
78
|
+
self[:has_children] = true
|
79
|
+
timer[:parent_timing_id] = self[:id]
|
80
|
+
timer[:depth] = self[:depth] + 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_sql(query, elapsed_ms, page, skip_backtrace = false, full_backtrace = false)
|
85
|
+
TimerStruct::Sql.new(query, elapsed_ms, page, self , skip_backtrace, full_backtrace).tap do |timer|
|
86
|
+
self[:sql_timings].push(timer)
|
87
|
+
timer[:parent_timing_id] = self[:id]
|
88
|
+
self[:has_sql_timings] = true
|
89
|
+
self[:sql_timings_duration_milliseconds] += elapsed_ms
|
90
|
+
page[:duration_milliseconds_in_sql] += elapsed_ms
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_custom(type, elapsed_ms, page)
|
95
|
+
TimerStruct::Custom.new(type, elapsed_ms, page, self).tap do |timer|
|
96
|
+
timer[:parent_timing_id] = self[:id]
|
97
|
+
|
98
|
+
self[:custom_timings][type] ||= []
|
99
|
+
self[:custom_timings][type].push(timer)
|
100
|
+
|
101
|
+
self[:custom_timing_stats][type] ||= {:count => 0, :duration => 0.0}
|
102
|
+
self[:custom_timing_stats][type][:count] += 1
|
103
|
+
self[:custom_timing_stats][type][:duration] += elapsed_ms
|
104
|
+
|
105
|
+
page[:custom_timing_stats][type] ||= {:count => 0, :duration => 0.0}
|
106
|
+
page[:custom_timing_stats][type][:count] += 1
|
107
|
+
page[:custom_timing_stats][type][:duration] += elapsed_ms
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def record_time(milliseconds = nil)
|
112
|
+
milliseconds ||= (Time.now - @start) * 1000
|
113
|
+
self[:duration_milliseconds] = milliseconds
|
114
|
+
self[:is_trivial] = true if milliseconds < self[:trivial_duration_threshold_milliseconds]
|
115
|
+
self[:duration_without_children_milliseconds] = milliseconds - @children_duration
|
116
|
+
|
117
|
+
if @parent
|
118
|
+
@parent.children_duration += milliseconds
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|