rack-mini-profiler 0.1.23 → 0.1.24
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 +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
|