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.

@@ -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