rack-mini-profiler 0.1.22 → 0.1.23
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.
- data/Ruby/README.md +22 -9
- data/Ruby/lib/html/includes.js +78 -15
- data/Ruby/lib/html/includes.tmpl +39 -15
- data/Ruby/lib/html/jquery.1.7.1.js +1 -1
- data/Ruby/lib/html/jquery.tmpl.js +1 -1
- data/Ruby/lib/html/list.js +7 -6
- data/Ruby/lib/html/profile_handler.js +1 -62
- data/Ruby/lib/mini_profiler/client_timer_struct.rb +1 -1
- data/Ruby/lib/mini_profiler/config.rb +54 -52
- data/Ruby/lib/mini_profiler/custom_timer_struct.rb +22 -0
- data/Ruby/lib/mini_profiler/page_timer_struct.rb +7 -2
- data/Ruby/lib/mini_profiler/profiler.rb +61 -17
- data/Ruby/lib/mini_profiler/request_timer_struct.rb +20 -1
- data/Ruby/lib/mini_profiler/storage/redis_store.rb +44 -44
- data/Ruby/lib/mini_profiler/version.rb +5 -0
- data/Ruby/lib/mini_profiler_rails/railtie.rb +1 -1
- data/Ruby/lib/patches/net_patches.rb +14 -0
- data/Ruby/lib/patches/sql_patches.rb +20 -1
- data/Ruby/lib/rack-mini-profiler.rb +1 -0
- data/rack-mini-profiler.gemspec +1 -1
- metadata +7 -4
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mini_profiler/timer_struct'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class MiniProfiler
|
5
|
+
|
6
|
+
# Timing system for a custom timers such as cache, redis, RPC, external API
|
7
|
+
# calls, etc.
|
8
|
+
class CustomTimerStruct < TimerStruct
|
9
|
+
def initialize(type, duration_ms, page, parent)
|
10
|
+
@parent = parent
|
11
|
+
@page = page
|
12
|
+
@type = type
|
13
|
+
|
14
|
+
super("Type" => type,
|
15
|
+
"StartMilliseconds" => ((Time.now.to_f * 1000).to_i - page['Started']) - duration_ms,
|
16
|
+
"DurationMilliseconds" => duration_ms,
|
17
|
+
"ParentTimingId" => nil)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -7,6 +7,7 @@ module Rack
|
|
7
7
|
# Root: RequestTimer
|
8
8
|
# :has_many RequestTimer children
|
9
9
|
# :has_many SqlTimer children
|
10
|
+
# :has_many CustomTimer children
|
10
11
|
class PageTimerStruct < TimerStruct
|
11
12
|
def initialize(env)
|
12
13
|
super("Id" => MiniProfiler.generate_id,
|
@@ -27,7 +28,10 @@ module Rack
|
|
27
28
|
"HasDuplicateSqlTimings" => false,
|
28
29
|
"ExecutedReaders" => 0,
|
29
30
|
"ExecutedScalars" => 0,
|
30
|
-
"ExecutedNonQueries" => 0
|
31
|
+
"ExecutedNonQueries" => 0,
|
32
|
+
"CustomTimingNames" => [],
|
33
|
+
"CustomTimingStats" => {}
|
34
|
+
)
|
31
35
|
name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
|
32
36
|
self['Root'] = RequestTimerStruct.createRoot(name, self)
|
33
37
|
end
|
@@ -43,7 +47,8 @@ module Rack
|
|
43
47
|
def to_json(*a)
|
44
48
|
attribs = @attributes.merge(
|
45
49
|
"Started" => '/Date(%d)/' % @attributes['Started'],
|
46
|
-
"DurationMilliseconds" => @attributes['Root']['DurationMilliseconds']
|
50
|
+
"DurationMilliseconds" => @attributes['Root']['DurationMilliseconds'],
|
51
|
+
"CustomTimingNames" => @attributes['CustomTimingStats'].keys.sort
|
47
52
|
)
|
48
53
|
::JSON.generate(attribs, :max_nesting => 100)
|
49
54
|
end
|
@@ -2,8 +2,10 @@ require 'json'
|
|
2
2
|
require 'timeout'
|
3
3
|
require 'thread'
|
4
4
|
|
5
|
+
require 'mini_profiler/version'
|
5
6
|
require 'mini_profiler/page_timer_struct'
|
6
7
|
require 'mini_profiler/sql_timer_struct'
|
8
|
+
require 'mini_profiler/custom_timer_struct'
|
7
9
|
require 'mini_profiler/client_timer_struct'
|
8
10
|
require 'mini_profiler/request_timer_struct'
|
9
11
|
require 'mini_profiler/storage/abstract_store'
|
@@ -19,11 +21,8 @@ require 'mini_profiler/gc_profiler'
|
|
19
21
|
|
20
22
|
module Rack
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
# we really should add a cleaner way to version JS and includes
|
25
|
-
VERSION = '108'.freeze
|
26
|
-
|
24
|
+
class MiniProfiler
|
25
|
+
|
27
26
|
class << self
|
28
27
|
|
29
28
|
include Rack::MiniProfiler::ProfilingMethods
|
@@ -79,6 +78,29 @@ module Rack
|
|
79
78
|
def request_authorized?
|
80
79
|
Thread.current[:mp_authorized]
|
81
80
|
end
|
81
|
+
|
82
|
+
# Add a custom timing. These are displayed similar to SQL/query time in
|
83
|
+
# columns expanding to the right.
|
84
|
+
#
|
85
|
+
# type - String counter type. Each distinct type gets its own column.
|
86
|
+
# duration_ms - Duration of the call in ms. Either this or a block must be
|
87
|
+
# given but not both.
|
88
|
+
#
|
89
|
+
# When a block is given, calculate the duration by yielding to the block
|
90
|
+
# and keeping a record of its run time.
|
91
|
+
#
|
92
|
+
# Returns the result of the block, or nil when no block is given.
|
93
|
+
def counter(type, duration_ms=nil)
|
94
|
+
result = nil
|
95
|
+
if block_given?
|
96
|
+
start = Time.now
|
97
|
+
result = yield
|
98
|
+
duration_ms = (Time.now - start).to_f * 1000
|
99
|
+
end
|
100
|
+
return result if current.nil? || !request_authorized?
|
101
|
+
current.current_timer.add_custom(type, duration_ms, current.page_struct)
|
102
|
+
result
|
103
|
+
end
|
82
104
|
end
|
83
105
|
|
84
106
|
#
|
@@ -127,7 +149,7 @@ module Rack
|
|
127
149
|
html.gsub!(/\{json\}/, result_json)
|
128
150
|
html.gsub!(/\{includes\}/, get_profile_script(env))
|
129
151
|
html.gsub!(/\{name\}/, page_struct['Name'])
|
130
|
-
html.gsub!(/\{duration\}/, page_struct.duration_ms
|
152
|
+
html.gsub!(/\{duration\}/, "%.1f" % page_struct.duration_ms)
|
131
153
|
|
132
154
|
[200, {'Content-Type' => 'text/html'}, [html]]
|
133
155
|
end
|
@@ -209,6 +231,8 @@ module Rack
|
|
209
231
|
client_settings.disable_profiling = true
|
210
232
|
client_settings.write!(headers)
|
211
233
|
return [status,headers,body]
|
234
|
+
else
|
235
|
+
client_settings.disable_profiling = false
|
212
236
|
end
|
213
237
|
|
214
238
|
if query_string =~ /pp=profile-gc/
|
@@ -316,6 +340,7 @@ module Rack
|
|
316
340
|
end
|
317
341
|
|
318
342
|
page_struct = current.page_struct
|
343
|
+
page_struct['User'] = user(env)
|
319
344
|
page_struct['Root'].record_time((Time.now - start) * 1000)
|
320
345
|
|
321
346
|
if backtraces
|
@@ -325,7 +350,7 @@ module Rack
|
|
325
350
|
|
326
351
|
|
327
352
|
# no matter what it is, it should be unviewed, otherwise we will miss POST
|
328
|
-
@storage.set_unviewed(
|
353
|
+
@storage.set_unviewed(page_struct['User'], page_struct['Id'])
|
329
354
|
@storage.save(page_struct)
|
330
355
|
|
331
356
|
# inject headers, script
|
@@ -369,13 +394,29 @@ module Rack
|
|
369
394
|
end
|
370
395
|
|
371
396
|
def inject(fragment, script)
|
372
|
-
fragment.
|
373
|
-
#
|
374
|
-
|
397
|
+
if fragment.match(/<\/body>/i)
|
398
|
+
# explicit </body>
|
399
|
+
|
400
|
+
regex = /<\/body>/i
|
401
|
+
close_tag = '</body>'
|
402
|
+
elsif fragment.match(/<\/html>/i)
|
403
|
+
# implicit </body>
|
404
|
+
|
405
|
+
regex = /<\/html>/i
|
406
|
+
close_tag = '</html>'
|
407
|
+
else
|
408
|
+
# implicit </body> and </html>. Just append the script.
|
409
|
+
|
410
|
+
return fragment + script
|
411
|
+
end
|
412
|
+
|
413
|
+
fragment.sub(regex) do
|
414
|
+
# if for whatever crazy reason we dont get a utf string,
|
415
|
+
# just force the encoding, no utf in the mp scripts anyway
|
375
416
|
if script.respond_to?(:encoding) && script.respond_to?(:force_encoding)
|
376
|
-
(script +
|
417
|
+
(script + close_tag).force_encoding(fragment.encoding)
|
377
418
|
else
|
378
|
-
script +
|
419
|
+
script + close_tag
|
379
420
|
end
|
380
421
|
end
|
381
422
|
end
|
@@ -449,6 +490,12 @@ module Rack
|
|
449
490
|
::JSON.generate(ids.uniq)
|
450
491
|
end
|
451
492
|
|
493
|
+
def ids_comma_separated(env)
|
494
|
+
# cap at 10 ids, otherwise there is a chance you can blow the header
|
495
|
+
ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]
|
496
|
+
ids.join(",")
|
497
|
+
end
|
498
|
+
|
452
499
|
# get_profile_script returns script to be injected inside current html page
|
453
500
|
# By default, profile_script is appended to the end of all html requests automatically.
|
454
501
|
# Calling get_profile_script cancels automatic append for the current page
|
@@ -456,7 +503,7 @@ module Rack
|
|
456
503
|
# * you have disabled auto append behaviour throught :auto_inject => false flag
|
457
504
|
# * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
|
458
505
|
def get_profile_script(env)
|
459
|
-
ids =
|
506
|
+
ids = ids_comma_separated(env)
|
460
507
|
path = @config.base_url_path
|
461
508
|
version = MiniProfiler::VERSION
|
462
509
|
position = @config.position
|
@@ -466,16 +513,13 @@ module Rack
|
|
466
513
|
showControls = false
|
467
514
|
currentId = current.page_struct["Id"]
|
468
515
|
authorized = true
|
469
|
-
useExistingjQuery = @config.use_existing_jquery
|
470
516
|
# TODO : cache this snippet
|
471
517
|
script = IO.read(::File.expand_path('../html/profile_handler.js', ::File.dirname(__FILE__)))
|
472
518
|
# replace the variables
|
473
|
-
[:ids, :path, :version, :position, :showTrivial, :showChildren, :maxTracesToShow, :showControls, :currentId, :authorized
|
519
|
+
[:ids, :path, :version, :position, :showTrivial, :showChildren, :maxTracesToShow, :showControls, :currentId, :authorized].each do |v|
|
474
520
|
regex = Regexp.new("\\{#{v.to_s}\\}")
|
475
521
|
script.gsub!(regex, eval(v.to_s).to_s)
|
476
522
|
end
|
477
|
-
# replace the '{{' and '}}''
|
478
|
-
script.gsub!(/\{\{/, '{').gsub!(/\}\}/, '}')
|
479
523
|
current.inject_js = false
|
480
524
|
script
|
481
525
|
end
|
@@ -33,7 +33,9 @@ module Rack
|
|
33
33
|
"Depth"=> parent ? parent.depth + 1 : 0,
|
34
34
|
"ExecutedReaders"=> 0,
|
35
35
|
"ExecutedScalars"=> 0,
|
36
|
-
"ExecutedNonQueries"=> 0
|
36
|
+
"ExecutedNonQueries"=> 0,
|
37
|
+
"CustomTimingStats" => {},
|
38
|
+
"CustomTimings" => {})
|
37
39
|
@children_duration = 0
|
38
40
|
@start = Time.now
|
39
41
|
@parent = parent
|
@@ -79,6 +81,23 @@ module Rack
|
|
79
81
|
timer
|
80
82
|
end
|
81
83
|
|
84
|
+
def add_custom(type, elapsed_ms, page)
|
85
|
+
timer = CustomTimerStruct.new(type, elapsed_ms, page, self)
|
86
|
+
timer['ParentTimingId'] = self['Id']
|
87
|
+
self['CustomTimings'][type] ||= []
|
88
|
+
self['CustomTimings'][type].push(timer)
|
89
|
+
|
90
|
+
self['CustomTimingStats'][type] ||= {"Count" => 0, "Duration" => 0.0}
|
91
|
+
self['CustomTimingStats'][type]['Count'] += 1
|
92
|
+
self['CustomTimingStats'][type]['Duration'] += elapsed_ms
|
93
|
+
|
94
|
+
page['CustomTimingStats'][type] ||= {"Count" => 0, "Duration" => 0.0}
|
95
|
+
page['CustomTimingStats'][type]['Count'] += 1
|
96
|
+
page['CustomTimingStats'][type]['Duration'] += elapsed_ms
|
97
|
+
|
98
|
+
timer
|
99
|
+
end
|
100
|
+
|
82
101
|
def record_time(milliseconds = nil)
|
83
102
|
milliseconds ||= (Time.now - @start) * 1000
|
84
103
|
self['DurationMilliseconds'] = milliseconds
|
@@ -1,44 +1,44 @@
|
|
1
|
-
module Rack
|
2
|
-
class MiniProfiler
|
3
|
-
class RedisStore < AbstractStore
|
4
|
-
|
5
|
-
EXPIRE_SECONDS = 60 * 60 * 24
|
6
|
-
|
7
|
-
def initialize(args)
|
8
|
-
args
|
9
|
-
@prefix = args
|
10
|
-
end
|
11
|
-
|
12
|
-
def save(page_struct)
|
13
|
-
redis.setex "#{@prefix}#{page_struct['Id']}", EXPIRE_SECONDS, Marshal::dump(page_struct)
|
14
|
-
end
|
15
|
-
|
16
|
-
def load(id)
|
17
|
-
raw = redis.get "#{@prefix}#{id}"
|
18
|
-
if raw
|
19
|
-
Marshal::load raw
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def set_unviewed(user, id)
|
24
|
-
redis.sadd "#{@prefix}-#{user}-v", id
|
25
|
-
end
|
26
|
-
|
27
|
-
def set_viewed(user, id)
|
28
|
-
redis.srem "#{@prefix}-#{user}-v", id
|
29
|
-
end
|
30
|
-
|
31
|
-
def get_unviewed_ids(user)
|
32
|
-
redis.smembers "#{@prefix}-#{user}-v"
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def redis
|
38
|
-
require 'redis' unless defined? Redis
|
39
|
-
Redis.new
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class RedisStore < AbstractStore
|
4
|
+
|
5
|
+
EXPIRE_SECONDS = 60 * 60 * 24
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@args = args || {}
|
9
|
+
@prefix = @args.delete(:prefix) || 'MPRedisStore'
|
10
|
+
end
|
11
|
+
|
12
|
+
def save(page_struct)
|
13
|
+
redis.setex "#{@prefix}#{page_struct['Id']}", EXPIRE_SECONDS, Marshal::dump(page_struct)
|
14
|
+
end
|
15
|
+
|
16
|
+
def load(id)
|
17
|
+
raw = redis.get "#{@prefix}#{id}"
|
18
|
+
if raw
|
19
|
+
Marshal::load raw
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_unviewed(user, id)
|
24
|
+
redis.sadd "#{@prefix}-#{user}-v", id
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_viewed(user, id)
|
28
|
+
redis.srem "#{@prefix}-#{user}-v", id
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_unviewed_ids(user)
|
32
|
+
redis.smembers "#{@prefix}-#{user}-v"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def redis
|
38
|
+
require 'redis' unless defined? Redis
|
39
|
+
Redis.new @args
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -9,7 +9,7 @@ module MiniProfilerRails
|
|
9
9
|
|
10
10
|
# By default, only show the MiniProfiler in development mode, in production allow profiling if post_authorize_cb is set
|
11
11
|
c.pre_authorize_cb = lambda { |env|
|
12
|
-
Rails.env.
|
12
|
+
!Rails.env.test?
|
13
13
|
}
|
14
14
|
|
15
15
|
c.skip_paths ||= []
|
@@ -0,0 +1,14 @@
|
|
1
|
+
if (defined?(Net) && defined?(Net::HTTP))
|
2
|
+
|
3
|
+
Net::HTTP.class_eval do
|
4
|
+
def request_with_mini_profiler(*args, &block)
|
5
|
+
request = args[0]
|
6
|
+
Rack::MiniProfiler.step("Net::HTTP #{request.method} #{request.path}") do
|
7
|
+
request_without_mini_profiler(*args, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
alias request_without_mini_profiler request
|
11
|
+
alias request request_with_mini_profiler
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -169,6 +169,25 @@ if SqlPatches.class_exists? "PG::Result"
|
|
169
169
|
end
|
170
170
|
|
171
171
|
|
172
|
+
# Mongoid 3 patches
|
173
|
+
if SqlPatches.class_exists?("Moped::Node")
|
174
|
+
class Moped::Node
|
175
|
+
alias_method :process_without_profiling, :process
|
176
|
+
def process(*args,&blk)
|
177
|
+
current = ::Rack::MiniProfiler.current
|
178
|
+
return process_without_profiling(*args,&blk) unless current
|
179
|
+
|
180
|
+
start = Time.now
|
181
|
+
result = process_without_profiling(*args,&blk)
|
182
|
+
elapsed_time = ((Time.now - start).to_f * 1000).round(1)
|
183
|
+
result.instance_variable_set("@miniprofiler_sql_id", ::Rack::MiniProfiler.record_sql(args[0].log_inspect, elapsed_time))
|
184
|
+
|
185
|
+
result
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
|
172
191
|
|
173
192
|
# Fallback for sequel
|
174
193
|
if SqlPatches.class_exists?("Sequel::Database") && !SqlPatches.patched?
|
@@ -186,7 +205,7 @@ end
|
|
186
205
|
|
187
206
|
## based off https://github.com/newrelic/rpm/blob/master/lib/new_relic/agent/instrumentation/active_record.rb
|
188
207
|
## fallback for alls sorts of weird dbs
|
189
|
-
if SqlPatches.module_exists?('ActiveRecord')
|
208
|
+
if SqlPatches.module_exists?('ActiveRecord') && !SqlPatches.patched?
|
190
209
|
module Rack
|
191
210
|
class MiniProfiler
|
192
211
|
module ActiveRecordInstrumentation
|
data/rack-mini-profiler.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "rack-mini-profiler"
|
3
|
-
s.version = "0.1.
|
3
|
+
s.version = "0.1.23"
|
4
4
|
s.summary = "Profiles loading speed for rack applications."
|
5
5
|
s.authors = ["Sam Saffron", "Robin Ward","Aleks Totic"]
|
6
6
|
s.description = "Profiling toolkit for Rack applications with Rails integration. Client Side profiling, DB profiling and Server profiling."
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-mini-profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.23
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-11-15 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rack
|
@@ -87,6 +87,7 @@ extra_rdoc_files:
|
|
87
87
|
- Ruby/CHANGELOG
|
88
88
|
files:
|
89
89
|
- rack-mini-profiler.gemspec
|
90
|
+
- Ruby/lib/mini_profiler/version.rb
|
90
91
|
- Ruby/lib/mini_profiler/sql_timer_struct.rb
|
91
92
|
- Ruby/lib/mini_profiler/request_timer_struct.rb
|
92
93
|
- Ruby/lib/mini_profiler/timer_struct.rb
|
@@ -103,6 +104,7 @@ files:
|
|
103
104
|
- Ruby/lib/mini_profiler/storage/redis_store.rb
|
104
105
|
- Ruby/lib/mini_profiler/storage/abstract_store.rb
|
105
106
|
- Ruby/lib/mini_profiler/storage/memcache_store.rb
|
107
|
+
- Ruby/lib/mini_profiler/custom_timer_struct.rb
|
106
108
|
- Ruby/lib/rack-mini-profiler.rb
|
107
109
|
- Ruby/lib/html/jquery.1.7.1.js
|
108
110
|
- Ruby/lib/html/includes.tmpl
|
@@ -117,6 +119,7 @@ files:
|
|
117
119
|
- Ruby/lib/html/list.js
|
118
120
|
- Ruby/lib/mini_profiler_rails/railtie.rb
|
119
121
|
- Ruby/lib/patches/sql_patches.rb
|
122
|
+
- Ruby/lib/patches/net_patches.rb
|
120
123
|
- Ruby/README.md
|
121
124
|
- Ruby/CHANGELOG
|
122
125
|
homepage: http://miniprofiler.com
|
@@ -133,7 +136,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
133
136
|
version: '0'
|
134
137
|
segments:
|
135
138
|
- 0
|
136
|
-
hash: -
|
139
|
+
hash: -387077425
|
137
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
141
|
none: false
|
139
142
|
requirements:
|
@@ -142,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
145
|
version: '0'
|
143
146
|
segments:
|
144
147
|
- 0
|
145
|
-
hash: -
|
148
|
+
hash: -387077425
|
146
149
|
requirements: []
|
147
150
|
rubyforge_project:
|
148
151
|
rubygems_version: 1.8.24
|