rack-mini-profiler 0.1.23 → 0.1.24
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack-mini-profiler might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/Ruby/CHANGELOG +55 -35
- data/Ruby/README.md +19 -1
- data/Ruby/lib/html/includes.css +451 -75
- data/Ruby/lib/html/includes.js +59 -12
- data/Ruby/lib/html/includes.less +38 -35
- data/Ruby/lib/html/includes.tmpl +4 -3
- data/Ruby/lib/html/profile_handler.js +1 -1
- data/Ruby/lib/mini_profiler/config.rb +4 -1
- data/Ruby/lib/mini_profiler/context.rb +11 -10
- data/Ruby/lib/mini_profiler/flame_graph.rb +54 -0
- data/Ruby/lib/mini_profiler/gc_profiler.rb +8 -4
- data/Ruby/lib/mini_profiler/profiler.rb +179 -170
- data/Ruby/lib/mini_profiler/profiling_methods.rb +131 -108
- data/Ruby/lib/mini_profiler/storage/abstract_store.rb +31 -27
- data/Ruby/lib/mini_profiler/storage/file_store.rb +111 -109
- data/Ruby/lib/mini_profiler/storage/memcache_store.rb +11 -9
- data/Ruby/lib/mini_profiler/storage/memory_store.rb +65 -63
- data/Ruby/lib/mini_profiler/storage/redis_store.rb +14 -4
- data/Ruby/lib/mini_profiler/version.rb +2 -2
- data/Ruby/lib/patches/sql_patches.rb +70 -48
- data/rack-mini-profiler.gemspec +1 -1
- metadata +40 -55
@@ -2,17 +2,19 @@ module Rack
|
|
2
2
|
class MiniProfiler
|
3
3
|
class MemcacheStore < AbstractStore
|
4
4
|
|
5
|
-
|
5
|
+
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
6
|
MAX_RETRIES = 10
|
7
7
|
|
8
|
-
def initialize(
|
8
|
+
def initialize(args = nil)
|
9
9
|
require 'dalli' unless defined? Dalli
|
10
|
-
|
11
|
-
@
|
10
|
+
args ||= {}
|
11
|
+
@prefix = args[:prefix] || "MPMemcacheStore"
|
12
|
+
@client = args[:client] || Dalli::Client.new
|
13
|
+
@expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
|
12
14
|
end
|
13
15
|
|
14
16
|
def save(page_struct)
|
15
|
-
@client.set("#{@prefix}#{page_struct['Id']}", Marshal::dump(page_struct),
|
17
|
+
@client.set("#{@prefix}#{page_struct['Id']}", Marshal::dump(page_struct), @expires_in_seconds)
|
16
18
|
end
|
17
19
|
|
18
20
|
def load(id)
|
@@ -23,9 +25,9 @@ module Rack
|
|
23
25
|
end
|
24
26
|
|
25
27
|
def set_unviewed(user, id)
|
26
|
-
@client.add("#{@prefix}-#{user}-v", [],
|
28
|
+
@client.add("#{@prefix}-#{user}-v", [], @expires_in_seconds)
|
27
29
|
MAX_RETRIES.times do
|
28
|
-
break if @client.cas("#{@prefix}-#{user}-v",
|
30
|
+
break if @client.cas("#{@prefix}-#{user}-v", @expires_in_seconds) do |ids|
|
29
31
|
ids << id unless ids.include?(id)
|
30
32
|
ids
|
31
33
|
end
|
@@ -33,9 +35,9 @@ module Rack
|
|
33
35
|
end
|
34
36
|
|
35
37
|
def set_viewed(user, id)
|
36
|
-
@client.add("#{@prefix}-#{user}-v", [],
|
38
|
+
@client.add("#{@prefix}-#{user}-v", [], @expires_in_seconds)
|
37
39
|
MAX_RETRIES.times do
|
38
|
-
break if @client.cas("#{@prefix}-#{user}-v",
|
40
|
+
break if @client.cas("#{@prefix}-#{user}-v", @expires_in_seconds) do |ids|
|
39
41
|
ids.delete id
|
40
42
|
ids
|
41
43
|
end
|
@@ -1,63 +1,65 @@
|
|
1
|
-
module Rack
|
2
|
-
class MiniProfiler
|
3
|
-
class MemoryStore < AbstractStore
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(args)
|
8
|
-
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class MemoryStore < AbstractStore
|
4
|
+
|
5
|
+
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
|
+
|
7
|
+
def initialize(args = nil)
|
8
|
+
args ||= {}
|
9
|
+
@expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
|
10
|
+
@timer_struct_lock = Mutex.new
|
11
|
+
@timer_struct_cache = {}
|
12
|
+
@user_view_lock = Mutex.new
|
13
|
+
@user_view_cache = {}
|
14
|
+
|
15
|
+
# TODO: fix it to use weak ref, trouble is may be broken in 1.9 so need to use the 'ref' gem
|
16
|
+
me = self
|
17
|
+
Thread.new do
|
18
|
+
while true do
|
19
|
+
me.cleanup_cache
|
20
|
+
sleep(3600)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def save(page_struct)
|
26
|
+
@timer_struct_lock.synchronize {
|
27
|
+
@timer_struct_cache[page_struct['Id']] = page_struct
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def load(id)
|
32
|
+
@timer_struct_lock.synchronize {
|
33
|
+
@timer_struct_cache[id]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_unviewed(user, id)
|
38
|
+
@user_view_lock.synchronize {
|
39
|
+
@user_view_cache[user] ||= []
|
40
|
+
@user_view_cache[user] << id
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_viewed(user, id)
|
45
|
+
@user_view_lock.synchronize {
|
46
|
+
@user_view_cache[user] ||= []
|
47
|
+
@user_view_cache[user].delete(id)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_unviewed_ids(user)
|
52
|
+
@user_view_lock.synchronize {
|
53
|
+
@user_view_cache[user]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def cleanup_cache
|
58
|
+
expire_older_than = ((Time.now.to_f - @expires_in_seconds) * 1000).to_i
|
59
|
+
@timer_struct_lock.synchronize {
|
60
|
+
@timer_struct_cache.delete_if { |k, v| v['Started'] < expire_older_than }
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -2,15 +2,17 @@ module Rack
|
|
2
2
|
class MiniProfiler
|
3
3
|
class RedisStore < AbstractStore
|
4
4
|
|
5
|
-
|
5
|
+
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
6
|
|
7
|
-
def initialize(args)
|
7
|
+
def initialize(args = nil)
|
8
8
|
@args = args || {}
|
9
9
|
@prefix = @args.delete(:prefix) || 'MPRedisStore'
|
10
|
+
@redis_connection = @args.delete(:connection)
|
11
|
+
@expires_in_seconds = @args.delete(:expires_in) || EXPIRES_IN_SECONDS
|
10
12
|
end
|
11
13
|
|
12
14
|
def save(page_struct)
|
13
|
-
redis.setex "#{@prefix}#{page_struct['Id']}",
|
15
|
+
redis.setex "#{@prefix}#{page_struct['Id']}", @expires_in_seconds, Marshal::dump(page_struct)
|
14
16
|
end
|
15
17
|
|
16
18
|
def load(id)
|
@@ -32,11 +34,19 @@ module Rack
|
|
32
34
|
redis.smembers "#{@prefix}-#{user}-v"
|
33
35
|
end
|
34
36
|
|
37
|
+
def diagnostics(user)
|
38
|
+
"Redis prefix: #{@prefix}
|
39
|
+
Redis location: #{redis.client.host}:#{redis.client.port} db: #{redis.client.db}
|
40
|
+
unviewed_ids: #{get_unviewed_ids(user)}
|
41
|
+
"
|
42
|
+
end
|
43
|
+
|
35
44
|
private
|
36
45
|
|
37
46
|
def redis
|
47
|
+
return @redis_connection if @redis_connection
|
38
48
|
require 'redis' unless defined? Redis
|
39
|
-
Redis.new @args
|
49
|
+
@redis_connection ||= Redis.new @args
|
40
50
|
end
|
41
51
|
|
42
52
|
end
|
@@ -8,32 +8,32 @@ class SqlPatches
|
|
8
8
|
@patched = val
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
def self.class_exists?(name)
|
12
|
+
eval(name + ".class").to_s.eql?('Class')
|
13
|
+
rescue NameError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
17
|
def self.module_exists?(name)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
eval(name + ".class").to_s.eql?('Module')
|
19
|
+
rescue NameError
|
20
|
+
false
|
21
|
+
end
|
22
22
|
end
|
23
23
|
|
24
24
|
# The best kind of instrumentation is in the actual db provider, however we don't want to double instrument
|
25
25
|
if SqlPatches.class_exists? "Mysql2::Client"
|
26
|
-
|
26
|
+
|
27
27
|
class Mysql2::Result
|
28
28
|
alias_method :each_without_profiling, :each
|
29
29
|
def each(*args, &blk)
|
30
30
|
return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
31
31
|
|
32
32
|
start = Time.now
|
33
|
-
result = each_without_profiling(*args,&blk)
|
33
|
+
result = each_without_profiling(*args,&blk)
|
34
34
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
35
35
|
|
36
|
-
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
36
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
37
37
|
result
|
38
38
|
end
|
39
39
|
end
|
@@ -42,7 +42,7 @@ if SqlPatches.class_exists? "Mysql2::Client"
|
|
42
42
|
alias_method :query_without_profiling, :query
|
43
43
|
def query(*args,&blk)
|
44
44
|
current = ::Rack::MiniProfiler.current
|
45
|
-
return query_without_profiling(*args,&blk) unless current
|
45
|
+
return query_without_profiling(*args,&blk) unless current && current.measure
|
46
46
|
|
47
47
|
start = Time.now
|
48
48
|
result = query_without_profiling(*args,&blk)
|
@@ -53,14 +53,14 @@ if SqlPatches.class_exists? "Mysql2::Client"
|
|
53
53
|
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
SqlPatches.patched = true
|
58
58
|
end
|
59
59
|
|
60
60
|
|
61
|
-
# PG patches, keep in mind exec and async_exec have a exec{|r| } semantics that is yet to be implemented
|
61
|
+
# PG patches, keep in mind exec and async_exec have a exec{|r| } semantics that is yet to be implemented
|
62
62
|
if SqlPatches.class_exists? "PG::Result"
|
63
|
-
|
63
|
+
|
64
64
|
class PG::Result
|
65
65
|
alias_method :each_without_profiling, :each
|
66
66
|
alias_method :values_without_profiling, :values
|
@@ -69,10 +69,10 @@ if SqlPatches.class_exists? "PG::Result"
|
|
69
69
|
return values_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
70
70
|
|
71
71
|
start = Time.now
|
72
|
-
result = values_without_profiling(*args,&blk)
|
72
|
+
result = values_without_profiling(*args,&blk)
|
73
73
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
74
74
|
|
75
|
-
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
75
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
76
76
|
result
|
77
77
|
end
|
78
78
|
|
@@ -80,10 +80,10 @@ if SqlPatches.class_exists? "PG::Result"
|
|
80
80
|
return each_without_profiling(*args, &blk) unless @miniprofiler_sql_id
|
81
81
|
|
82
82
|
start = Time.now
|
83
|
-
result = each_without_profiling(*args,&blk)
|
83
|
+
result = each_without_profiling(*args,&blk)
|
84
84
|
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
85
85
|
|
86
|
-
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
86
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
87
87
|
result
|
88
88
|
end
|
89
89
|
end
|
@@ -96,23 +96,23 @@ if SqlPatches.class_exists? "PG::Result"
|
|
96
96
|
alias_method :prepare_without_profiling, :prepare
|
97
97
|
|
98
98
|
def prepare(*args,&blk)
|
99
|
-
# we have no choice but to do this here,
|
100
|
-
# if we do the check for profiling first, our cache may miss critical stuff
|
101
|
-
|
99
|
+
# we have no choice but to do this here,
|
100
|
+
# if we do the check for profiling first, our cache may miss critical stuff
|
101
|
+
|
102
102
|
@prepare_map ||= {}
|
103
103
|
@prepare_map[args[0]] = args[1]
|
104
104
|
# dont leak more than 10k ever
|
105
105
|
@prepare_map = {} if @prepare_map.length > 1000
|
106
106
|
|
107
107
|
current = ::Rack::MiniProfiler.current
|
108
|
-
return prepare_without_profiling(*args,&blk) unless current
|
108
|
+
return prepare_without_profiling(*args,&blk) unless current && current.measure
|
109
109
|
|
110
|
-
prepare_without_profiling(*args,&blk)
|
110
|
+
prepare_without_profiling(*args,&blk)
|
111
111
|
end
|
112
112
|
|
113
113
|
def exec(*args,&blk)
|
114
114
|
current = ::Rack::MiniProfiler.current
|
115
|
-
return exec_without_profiling(*args,&blk) unless current
|
115
|
+
return exec_without_profiling(*args,&blk) unless current && current.measure
|
116
116
|
|
117
117
|
start = Time.now
|
118
118
|
result = exec_without_profiling(*args,&blk)
|
@@ -124,7 +124,7 @@ if SqlPatches.class_exists? "PG::Result"
|
|
124
124
|
|
125
125
|
def exec_prepared(*args,&blk)
|
126
126
|
current = ::Rack::MiniProfiler.current
|
127
|
-
return exec_prepared_without_profiling(*args,&blk) unless current
|
127
|
+
return exec_prepared_without_profiling(*args,&blk) unless current && current.measure
|
128
128
|
|
129
129
|
start = Time.now
|
130
130
|
result = exec_prepared_without_profiling(*args,&blk)
|
@@ -135,10 +135,10 @@ if SqlPatches.class_exists? "PG::Result"
|
|
135
135
|
|
136
136
|
result
|
137
137
|
end
|
138
|
-
|
138
|
+
|
139
139
|
def send_query_prepared(*args,&blk)
|
140
140
|
current = ::Rack::MiniProfiler.current
|
141
|
-
return send_query_prepared_without_profiling(*args,&blk) unless current
|
141
|
+
return send_query_prepared_without_profiling(*args,&blk) unless current && current.measure
|
142
142
|
|
143
143
|
start = Time.now
|
144
144
|
result = send_query_prepared_without_profiling(*args,&blk)
|
@@ -149,10 +149,10 @@ if SqlPatches.class_exists? "PG::Result"
|
|
149
149
|
|
150
150
|
result
|
151
151
|
end
|
152
|
-
|
152
|
+
|
153
153
|
def async_exec(*args,&blk)
|
154
154
|
current = ::Rack::MiniProfiler.current
|
155
|
-
return exec_without_profiling(*args,&blk) unless current
|
155
|
+
return exec_without_profiling(*args,&blk) unless current && current.measure
|
156
156
|
|
157
157
|
start = Time.now
|
158
158
|
result = exec_without_profiling(*args,&blk)
|
@@ -161,10 +161,10 @@ if SqlPatches.class_exists? "PG::Result"
|
|
161
161
|
|
162
162
|
result
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
alias_method :query, :exec
|
166
166
|
end
|
167
|
-
|
167
|
+
|
168
168
|
SqlPatches.patched = true
|
169
169
|
end
|
170
170
|
|
@@ -175,7 +175,7 @@ if SqlPatches.class_exists?("Moped::Node")
|
|
175
175
|
alias_method :process_without_profiling, :process
|
176
176
|
def process(*args,&blk)
|
177
177
|
current = ::Rack::MiniProfiler.current
|
178
|
-
return process_without_profiling(*args,&blk) unless current
|
178
|
+
return process_without_profiling(*args,&blk) unless current && current.measure
|
179
179
|
|
180
180
|
start = Time.now
|
181
181
|
result = process_without_profiling(*args,&blk)
|
@@ -187,19 +187,41 @@ if SqlPatches.class_exists?("Moped::Node")
|
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
+
if SqlPatches.class_exists?("RSolr::Connection") && RSolr::VERSION[0] != "0" # requires at least v1.0.0
|
191
|
+
class RSolr::Connection
|
192
|
+
alias_method :execute_without_profiling, :execute
|
193
|
+
def execute_with_profiling(client, request_context)
|
194
|
+
current = ::Rack::MiniProfiler.current
|
195
|
+
return execute_without_profiling(client, request_context) unless current && current.measure
|
196
|
+
|
197
|
+
start = Time.now
|
198
|
+
result = execute_without_profiling(client, request_context)
|
199
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
200
|
+
|
201
|
+
data = "#{request_context[:method].upcase} #{request_context[:uri]}"
|
202
|
+
if request_context[:method] == :post and request_context[:data]
|
203
|
+
data << "\n#{Rack::Utils.unescape(request_context[:data])}"
|
204
|
+
end
|
205
|
+
result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(data, elapsed_time))
|
206
|
+
|
207
|
+
result
|
208
|
+
end
|
209
|
+
alias_method :execute, :execute_with_profiling
|
210
|
+
end
|
211
|
+
end
|
190
212
|
|
191
213
|
|
192
214
|
# Fallback for sequel
|
193
215
|
if SqlPatches.class_exists?("Sequel::Database") && !SqlPatches.patched?
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
216
|
+
module Sequel
|
217
|
+
class Database
|
218
|
+
alias_method :log_duration_original, :log_duration
|
219
|
+
def log_duration(duration, message)
|
220
|
+
::Rack::MiniProfiler.record_sql(message, duration)
|
221
|
+
log_duration_original(duration, message)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
203
225
|
end
|
204
226
|
|
205
227
|
|
@@ -207,7 +229,7 @@ end
|
|
207
229
|
## fallback for alls sorts of weird dbs
|
208
230
|
if SqlPatches.module_exists?('ActiveRecord') && !SqlPatches.patched?
|
209
231
|
module Rack
|
210
|
-
class MiniProfiler
|
232
|
+
class MiniProfiler
|
211
233
|
module ActiveRecordInstrumentation
|
212
234
|
def self.included(instrumented_class)
|
213
235
|
instrumented_class.class_eval do
|
@@ -221,12 +243,12 @@ if SqlPatches.module_exists?('ActiveRecord') && !SqlPatches.patched?
|
|
221
243
|
|
222
244
|
def log_with_miniprofiler(*args, &block)
|
223
245
|
current = ::Rack::MiniProfiler.current
|
224
|
-
return log_without_miniprofiler(*args, &block) unless current
|
246
|
+
return log_without_miniprofiler(*args, &block) unless current && current.measure
|
225
247
|
|
226
248
|
sql, name, binds = args
|
227
249
|
t0 = Time.now
|
228
250
|
rval = log_without_miniprofiler(*args, &block)
|
229
|
-
|
251
|
+
|
230
252
|
# Don't log schema queries if the option is set
|
231
253
|
return rval if Rack::MiniProfiler.config.skip_schema_queries and name =~ /SCHEMA/
|
232
254
|
|
@@ -237,7 +259,7 @@ if SqlPatches.module_exists?('ActiveRecord') && !SqlPatches.patched?
|
|
237
259
|
end
|
238
260
|
end
|
239
261
|
|
240
|
-
def self.insert_instrumentation
|
262
|
+
def self.insert_instrumentation
|
241
263
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
|
242
264
|
include ::Rack::MiniProfiler::ActiveRecordInstrumentation
|
243
265
|
end
|