rack-mini-profiler 0.1.22 → 0.1.23

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.

@@ -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
- class MiniProfiler
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.round(1).to_s)
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(user(env), page_struct['Id'])
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.sub(/<\/body>/i) do
373
- # if for whatever crazy reason we dont get a utf string,
374
- # just force the encoding, no utf in the mp scripts anyway
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 + "</body>").force_encoding(fragment.encoding)
417
+ (script + close_tag).force_encoding(fragment.encoding)
377
418
  else
378
- script + "</body>"
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 = ids_json(env)
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, :useExistingjQuery].each do |v|
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[: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
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
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class MiniProfiler
3
+ VERSION = '33d69ecf833daec8db07a9a0b6cf0bd3'.freeze
4
+ end
5
+ 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.development? || Rails.env.production?
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
@@ -1,5 +1,6 @@
1
1
  require 'mini_profiler/profiler'
2
2
  require 'patches/sql_patches'
3
+ require 'patches/net_patches'
3
4
 
4
5
  if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i >= 3
5
6
  require 'mini_profiler_rails/railtie'
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack-mini-profiler"
3
- s.version = "0.1.22"
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.22
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-09-20 00:00:00.000000000 Z
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: -138209491
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: -138209491
148
+ hash: -387077425
146
149
  requirements: []
147
150
  rubyforge_project:
148
151
  rubygems_version: 1.8.24