rack-mini-profiler 2.3.4 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6021effb717c193c4c70b3ac7a3550fd99c9abfd19432ac0ab9db63f62b11321
4
- data.tar.gz: 8bce19855d2f6d908339e3108201282c519b4d8ad99b8e7e90dad9d3f718c929
3
+ metadata.gz: 7544df4d22f5f615b146fb0d63f18098ffd702ac9dec966d8040187f2e8302b9
4
+ data.tar.gz: 7c39c41a96205c8c7fbd69765ab5f1e0a3a3a37950055fc58d0f27f4a0591e0f
5
5
  SHA512:
6
- metadata.gz: 0c4354c194a6fa6018c57162e24e1cee85de7f296e45cd6182360b39abeb803b29a14674e2dfe93b3ba5d9bcb192814de8d091ad6b7db20d71af95e2cddcf4eb
7
- data.tar.gz: e3c7f4da5fd5c79bfdb4c3582b50a38346d38784bfd92c6e7ce3fa41713104c74b6ad5622084d42624011ef850533c403002664f44ad7f6e1b1ac16a245c633b
6
+ metadata.gz: 6e3f5f9a51fc5395dda4a7e793903f998513499d850aeb291fe036171a78617d11c3d8cd6fef57d1144359aa4992055ad54ba878075896c3d0280c426a84e3be
7
+ data.tar.gz: c4e353c8442db93f29d11d04e2724e6c3f8dacaaf082210acbbb7d47655ffc3a6b5297079750e305fe9f1cd3e7efb1d806aef0ec68bbc1a8e40a0f958de0ec52
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.1.0 - 2023-04-11
4
+
5
+ - [FEATURE] The query parameter that RMP uses (by default, pp) is now configurable [#553](https://github.com/MiniProfiler/rack-mini-profiler/pull/553)
6
+ - [FEATURE] You can now opt-out of the Net::HTTP patch by using RACK_MINI_PROFILER_PATCH_NET_HTTP="false"
7
+ - [FIX] Error responses now include header values from the app, and stackprof not installed message now has correct content [#547](https://github.com/MiniProfiler/rack-mini-profiler/pull/547)
8
+ - [FIX] RMP pages now have more valid HTML, with title elements [#562](https://github.com/MiniProfiler/rack-mini-profiler/pull/562)
9
+ - [BREAKING CHANGE] Ruby 2.4 and Ruby 2.5 are no longer supported.
10
+ - [FIX] Now works with apps that don't otherwise require erb [#531](https://github.com/MiniProfiler/rack-mini-profiler/pull/531)
11
+ - [DOCS] Added Heroku Redis instructions
12
+ - [DEPRECATION] We are changing the name of the generators to `rack_mini_profiler`, e.g. `rack_mini_profiler:install` [#550](https://github.com/MiniProfiler/rack-mini-profiler/pull/550)
13
+
14
+ ## 3.0.0 - 2022-02-24
15
+
16
+ - PERF: Improve snapshots page performance (#518) (introduces breaking changes to the API of `AbstractStore`, `MemoryStore` and `RedisStore`, and removes the `snapshots_limit` config option.)
17
+
3
18
  ## 2.3.4 - 2022-02-23
4
19
 
5
20
  - [FEATURE] Add cookie path support for subfolder sites
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # rack-mini-profiler
2
2
 
3
- Middleware that displays speed badge for every html page. Designed to work both in production and in development.
3
+ Middleware that displays speed badge for every HTML page, along with (optional) flamegraphs and memory profiling. Designed to work both in production and in development.
4
+
5
+ ![Screenshot 2023-04-05 at 3 13 52 PM](https://user-images.githubusercontent.com/845662/229996538-0f2d9c48-23d9-4d53-a1de-8b4c84c87fbd.png)
4
6
 
5
7
  #### Features
6
8
 
@@ -27,7 +29,7 @@ If you feel like taking on any of this start an issue and update us on your prog
27
29
 
28
30
  ## Installation
29
31
 
30
- Install/add to Gemfile in Ruby 2.4+
32
+ Install/add to Gemfile in Ruby 2.6+
31
33
 
32
34
  ```ruby
33
35
  gem 'rack-mini-profiler'
@@ -101,7 +103,7 @@ be loaded outright, and an attempt to re-initialize it manually will raise an ex
101
103
  Then run the generator which will set up rack-mini-profiler in development:
102
104
 
103
105
  ```bash
104
- bundle exec rails g rack_profiler:install
106
+ bundle exec rails g rack_mini_profiler:install
105
107
  ```
106
108
 
107
109
  #### Rack Builder
@@ -164,11 +166,19 @@ export RACK_MINI_PROFILER_PATCH="false"
164
166
  # initializers/rack_profiler.rb: SqlPatches.patch %w(mongo)
165
167
  ```
166
168
 
169
+ #### Patching Net::HTTP
170
+
171
+ Other than databases, `rack-mini-profiler` applies a patch to `Net::HTTP`. You may want to disable this patch:
172
+
173
+ ```bash
174
+ export RACK_MINI_PROFILER_PATCH_NET_HTTP="false"
175
+ ```
176
+
167
177
  ### Flamegraphs
168
178
 
169
179
  To generate [flamegraphs](http://samsaffron.com/archive/2013/03/19/flame-graphs-in-ruby-miniprofiler), add the [**stackprof**](https://rubygems.org/gems/stackprof) gem to your Gemfile.
170
180
 
171
- Then, to view the flamegraph as a direct HTML response from your request, just visit any page in your app with `?pp=flamegraph` appended to the URL.
181
+ Then, to view the flamegraph as a direct HTML response from your request, just visit any page in your app with `?pp=flamegraph` appended to the URL.
172
182
 
173
183
  Conversely, if you want your regular response instead (which is specially useful for JSON and/or XHR requests), just append the `?pp=async-flamegraph` parameter to your request/fetch URL; the request will then return as normal, and the flamegraph data will be stored for later *async* viewing, both for this request and for all subsequent requests made by this page (based on the `REFERER` header). For viewing these async flamegraphs, use the 'flamegraph' link that will appear inside the MiniProfiler UI for these requests.
174
184
 
@@ -207,7 +217,7 @@ After enabling snapshots sampling, you can see the snapshots that have been coll
207
217
 
208
218
  Access to the snapshots page is restricted to only those who can see the speed badge on their own requests, see the section below this one about access control.
209
219
 
210
- Mini Profiler will keep a maximum of 1000 snapshots by default, and you can change that via the `snapshots_limit` config. When snapshots reach the configured limit, Mini Profiler will save a new snapshot only if it's worse than at least one of the existing snapshots and delete the best one (i.e. the snapshot whose request took the least time compared to other snapshots).
220
+ Mini Profiler will keep a maximum of 50 snapshot groups and a maximum of 15 snapshots per group making the default maximum number of snapshots in the system 750. The default group and per group limits can be changed via the `max_snapshot_groups` and `max_snapshots_per_group` configuration options, see the configurations table below.
211
221
 
212
222
  #### Snapshots Transporter
213
223
 
@@ -406,38 +416,40 @@ Rack::MiniProfiler.config.start_hidden = true
406
416
  ```
407
417
  The available configuration options are:
408
418
 
409
- Option|Default|Description
410
- -------|---|--------
411
- pre_authorize_cb|Rails: dev only<br>Rack: always on|A lambda callback that returns true to make mini_profiler visible on a given request.
412
- position|`'top-left'`|Display mini_profiler on `'top-right'`, `'top-left'`, `'bottom-right'` or `'bottom-left'`.
413
- skip_paths|`[]`|An array of paths that skip profiling. Both `String` and `Regexp` are acceptable in the array.
414
- skip_schema_queries|Rails dev: `true`<br>Othwerwise: `false`|`true` to skip schema queries.
415
- auto_inject|`true`|`true` to inject the miniprofiler script in the page.
416
- backtrace_ignores|`[]`|Regexes of lines to be removed from backtraces.
417
- backtrace_includes|Rails: `[/^\/?(app\|config\|lib\|test)/]`<br>Rack: `[]`|Regexes of lines to keep in backtraces.
418
- backtrace_remove|rails: `Rails.root`<br>Rack: `nil`|A string or regex to remove part of each line in the backtrace.
419
- toggle_shortcut|Alt+P|Keyboard shortcut to toggle the mini_profiler's visibility. See [jquery.hotkeys](https://github.com/jeresig/jquery.hotkeys).
420
- start_hidden|`false`|`false` to make mini_profiler visible on page load.
421
- backtrace_threshold_ms|`0`|Minimum SQL query elapsed time before a backtrace is recorded.
422
- flamegraph_sample_rate|`0.5`|How often to capture stack traces for flamegraphs in milliseconds.
423
- flamegraph_mode|`:wall`|The [StackProf mode](https://github.com/tmm1/stackprof#all-options) to pass to `StackProf.run`.
424
- base_url_path|`'/mini-profiler-resources/'`|Path for assets; added as a prefix when naming assets and sought when responding to requests.
425
- cookie_path|`'/'`|Set-Cookie header path for profile cookie
426
- collapse_results|`true`|If multiple timing results exist in a single page, collapse them till clicked.
427
- max_traces_to_show|20|Maximum number of mini profiler timing blocks to show on one page
428
- html_container|`body`|The HTML container (as a jQuery selector) to inject the mini_profiler UI into
429
- show_total_sql_count|`false`|Displays the total number of SQL executions.
430
- enable_advanced_debugging_tools|`false`|Enables sensitive debugging tools that can be used via the UI. In production we recommend keeping this disabled as memory and environment debugging tools can expose contents of memory that may contain passwords. Defaults to `true` in development.
431
- assets_url|`nil`|See the "Register MiniProfiler's assets in the Rails assets pipeline" section above.
432
- snapshot_every_n_requests|`-1`|Determines how frequently snapshots are taken. See the "Snapshots Sampling" above for more details.
433
- snapshots_limit|`1000`|Determines how many snapshots Mini Profiler is allowed to keep.
434
- snapshot_hidden_custom_fields|`[]`|Each snapshot custom field will have a dedicated column in the UI by default. Use this config to exclude certain custom fields from having their own columns.
435
- snapshots_transport_destination_url|`nil`|Set this config to a valid URL to enable snapshots transporter which will `POST` snapshots to the given URL. The transporter requires `snapshots_transport_auth_key` config to be set as well.
436
- snapshots_transport_auth_key|`nil`|`POST` requests made by the snapshots transporter to the destination URL will have a `Mini-Profiler-Transport-Auth` header with the value of this config. Make sure you use a secure and random key for this config.
437
- snapshots_redact_sql_queries|`true`|When this is true, SQL queries will be redacted from sampling snapshots, but the backtrace and duration of each SQL query will be saved with the snapshot to keep debugging performance issues possible.
438
- snapshots_transport_gzip_requests|`false`|Make the snapshots transporter gzip the requests it makes to `snapshots_transport_destination_url`.
439
- content_security_policy_nonce|Rails: Current nonce<br>Rack: nil|Set the content security policy nonce to use when inserting MiniProfiler's script block.
440
- enable_hotwire_turbo_drive_support| `false` | Enable support for Hotwire TurboDrive page transitions.
419
+ Option | Default | Description
420
+ ------------------------------------|---------------------------------------------------------|------------------------
421
+ pre_authorize_cb | Rails: dev only<br>Rack: always on | A lambda callback that returns true to make mini_profiler visible on a given request.
422
+ position | `'top-left'` | Display mini_profiler on `'top-right'`, `'top-left'`, `'bottom-right'` or `'bottom-left'`.
423
+ skip_paths | `[]` | An array of paths that skip profiling. Both `String` and `Regexp` are acceptable in the array.
424
+ skip_schema_queries | Rails dev: `true`<br>Othwerwise: `false` | `true` to skip schema queries.
425
+ auto_inject | `true` | `true` to inject the miniprofiler script in the page.
426
+ backtrace_ignores | `[]` | Regexes of lines to be removed from backtraces.
427
+ backtrace_includes | Rails: `[/^\/?(app\|config\|lib\|test)/]`<br>Rack: `[]` | Regexes of lines to keep in backtraces.
428
+ backtrace_remove | rails: `Rails.root`<br>Rack: `nil` | A string or regex to remove part of each line in the backtrace.
429
+ toggle_shortcut | Alt+P | Keyboard shortcut to toggle the mini_profiler's visibility. See [jquery.hotkeys](https://github.com/jeresig/jquery.hotkeys).
430
+ start_hidden | `false` | `false` to make mini_profiler visible on page load.
431
+ backtrace_threshold_ms | `0` | Minimum SQL query elapsed time before a backtrace is recorded.
432
+ flamegraph_sample_rate | `0.5` | How often to capture stack traces for flamegraphs in milliseconds.
433
+ flamegraph_mode | `:wall` | The [StackProf mode](https://github.com/tmm1/stackprof#all-options) to pass to `StackProf.run`.
434
+ base_url_path | `'/mini-profiler-resources/'` | Path for assets; added as a prefix when naming assets and sought when responding to requests.
435
+ cookie_path | `'/'` | Set-Cookie header path for profile cookie
436
+ collapse_results | `true` | If multiple timing results exist in a single page, collapse them till clicked.
437
+ max_traces_to_show | 20 | Maximum number of mini profiler timing blocks to show on one page
438
+ html_container | `body` | The HTML container (as a jQuery selector) to inject the mini_profiler UI into
439
+ show_total_sql_count | `false` | Displays the total number of SQL executions.
440
+ enable_advanced_debugging_tools | `false` | Enables sensitive debugging tools that can be used via the UI. In production we recommend keeping this disabled as memory and environment debugging tools can expose contents of memory that may contain passwords. Defaults to `true` in development.
441
+ assets_url | `nil` | See the "Register MiniProfiler's assets in the Rails assets pipeline" section above.
442
+ snapshot_every_n_requests | `-1` | Determines how frequently snapshots are taken. See the "Snapshots Sampling" above for more details.
443
+ max_snapshot_groups | `50` | Determines how many snapshot groups Mini Profiler is allowed to keep.
444
+ max_snapshots_per_group | `15` | Determines how many snapshots per group Mini Profiler is allowed to keep.
445
+ snapshot_hidden_custom_fields | `[]` | Each snapshot custom field will have a dedicated column in the UI by default. Use this config to exclude certain custom fields from having their own columns.
446
+ snapshots_transport_destination_url | `nil` | Set this config to a valid URL to enable snapshots transporter which will `POST` snapshots to the given URL. The transporter requires `snapshots_transport_auth_key` config to be set as well.
447
+ snapshots_transport_auth_key | `nil` | `POST` requests made by the snapshots transporter to the destination URL will have a `Mini-Profiler-Transport-Auth` header with the value of this config. Make sure you use a secure and random key for this config.
448
+ snapshots_redact_sql_queries | `true` | When this is true, SQL queries will be redacted from sampling snapshots, but the backtrace and duration of each SQL query will be saved with the snapshot to keep debugging performance issues possible.
449
+ snapshots_transport_gzip_requests | `false` | Make the snapshots transporter gzip the requests it makes to `snapshots_transport_destination_url`.
450
+ content_security_policy_nonce | Rails: Current nonce<br>Rack: nil | Set the content security policy nonce to use when inserting MiniProfiler's script block.
451
+ enable_hotwire_turbo_drive_support | `false` | Enable support for Hotwire TurboDrive page transitions.
452
+ profile_parameter | `'pp'` | The query parameter used to interact with this gem.
441
453
 
442
454
  ### Using MiniProfiler with `Rack::Deflate` middleware
443
455
 
@@ -447,41 +459,26 @@ which means it will run after `Rack::Deflate` on response processing. To prevent
447
459
  HTML in already compressed response body MiniProfiler will suppress compression by setting
448
460
  `identity` encoding in `Accept-Encoding` request header.
449
461
 
450
- ## Special query strings
451
-
452
- If you include the query string `pp=help` at the end of your request you will see the various options available. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
453
-
454
-
455
- ## Rails 2.X support
462
+ ### Using MiniProfiler with Heroku Redis
456
463
 
457
- To get MiniProfiler working with Rails 2.3.X you need to do the initialization manually as well as monkey patch away an incompatibility between activesupport and json_pure.
458
-
459
- Add the following code to your environment.rb (or just in a specific environment such as development.rb) for initialization and configuration of MiniProfiler.
464
+ If you are using Heroku Redis, you may need to add the following to your `config/initializers/mini_profiler.rb`, in order to get Mini Profiler to work:
460
465
 
461
466
  ```ruby
462
- # configure and initialize MiniProfiler
463
- require 'rack-mini-profiler'
464
- c = ::Rack::MiniProfiler.config
465
- c.pre_authorize_cb = lambda { |env|
466
- Rails.env.development? || Rails.env.production?
467
- }
468
- tmp = Rails.root.to_s + "/tmp/miniprofiler"
469
- FileUtils.mkdir_p(tmp) unless File.exist?(tmp)
470
- c.storage_options = {:path => tmp}
471
- c.storage = ::Rack::MiniProfiler::FileStore
472
- config.middleware.use(::Rack::MiniProfiler)
473
- ::Rack::MiniProfiler.profile_method(ActionController::Base, :process) {|action| "Executing action: #{action}"}
474
- ::Rack::MiniProfiler.profile_method(ActionView::Template, :render) {|x,y| "Rendering: #{path_without_format_and_extension}"}
475
-
476
- # monkey patch away an activesupport and json_pure incompatability
477
- # http://pivotallabs.com/users/alex/blog/articles/1332-monkey-patch-of-the-day-activesupport-vs-json-pure-vs-ruby-1-8
478
- if JSON.const_defined?(:Pure)
479
- class JSON::Pure::Generator::State
480
- include ActiveSupport::CoreExtensions::Hash::Except
481
- end
467
+ if Rails.env.production?
468
+ Rack::MiniProfiler.config.storage_options = {
469
+ url: ENV["REDIS_URL"],
470
+ ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
471
+ }
472
+ Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore
482
473
  end
483
474
  ```
484
475
 
476
+ The above code snippet is [Heroku's officially suggested workaround](https://help.heroku.com/HC0F8CUS/redis-connection-issues).
477
+
478
+ ## Special query strings
479
+
480
+ If you include the query string `pp=help` at the end of your request you will see the various options available. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
481
+
485
482
  ## Development
486
483
 
487
484
  If you want to contribute to this project, that's great, thank you! You can run the following rake task:
@@ -500,8 +497,8 @@ Make sure to prepend `bundle exec` before any Rake tasks you run.
500
497
  You need Memcached and Redis services running for the specs.
501
498
 
502
499
  ```
503
- $ rake build
504
- $ rake spec
500
+ $ bundle exec rake build
501
+ $ bundle exec rake spec
505
502
  ```
506
503
 
507
504
  ## Licence
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generates an initializer for rack-mini-profiler. Use an initializer when manually
3
+ requiring rack-mini-profiler in your application (using require: false in your Gemfile).
4
+
5
+ Example:
6
+ `bin/rails generate rack_mini_profiler:install`
7
+
8
+ This generates a an initializer that requires and initializes
9
+ rack-mini-profiler in development mode.
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RackMiniProfiler
4
+ module Generators
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ def create_initializer_file
9
+ copy_file "rack_mini_profiler.rb", "config/initializers/rack_mini_profiler.rb"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -3,6 +3,6 @@
3
3
  if Rails.env.development?
4
4
  require "rack-mini-profiler"
5
5
 
6
- # initialization is skipped so trigger it
6
+ # The initializer was required late, so initialize it manually.
7
7
  Rack::MiniProfilerRails.initialize!(Rails.application)
8
8
  end
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "generators/rack_mini_profiler/install_generator"
4
+
3
5
  module RackProfiler
4
6
  module Generators
5
- class InstallGenerator < ::Rails::Generators::Base
6
- source_root File.expand_path("templates", __dir__)
7
+ class InstallGenerator < RackMiniProfiler::Generators::InstallGenerator
8
+ source_root File.expand_path("../rack_mini_profiler/templates", __dir__)
7
9
 
8
10
  def create_initializer_file
9
- copy_file "rack_profiler.rb", "config/initializers/rack_profiler.rb"
11
+ warn("bin/rails generate rack_profiler:install is deprecated. Please use rack_mini_profiler:install instead.")
12
+ super
10
13
  end
11
14
  end
12
15
  end
@@ -40,7 +40,8 @@ module Rack
40
40
  @skip_sql_param_names = /password/ # skips parameters with the name password by default
41
41
  @enable_advanced_debugging_tools = false
42
42
  @snapshot_every_n_requests = -1
43
- @snapshots_limit = 1000
43
+ @max_snapshot_groups = 50
44
+ @max_snapshots_per_group = 15
44
45
 
45
46
  # ui parameters
46
47
  @autorized = true
@@ -61,6 +62,8 @@ module Rack
61
62
  @snapshots_transport_gzip_requests = false
62
63
  @enable_hotwire_turbo_drive_support = false
63
64
 
65
+ @profile_parameter = "pp"
66
+
64
67
  self
65
68
  }
66
69
  end
@@ -73,7 +76,7 @@ module Rack
73
76
  :storage_options, :user_provider, :enable_advanced_debugging_tools,
74
77
  :skip_sql_param_names, :suppress_encoding, :max_sql_param_length,
75
78
  :content_security_policy_nonce, :enable_hotwire_turbo_drive_support,
76
- :flamegraph_mode
79
+ :flamegraph_mode, :profile_parameter
77
80
 
78
81
  # ui accessors
79
82
  attr_accessor :collapse_results, :max_traces_to_show, :position,
@@ -81,10 +84,10 @@ module Rack
81
84
  :start_hidden, :toggle_shortcut, :html_container
82
85
 
83
86
  # snapshot related config
84
- attr_accessor :snapshot_every_n_requests, :snapshots_limit,
87
+ attr_accessor :snapshot_every_n_requests, :max_snapshots_per_group,
85
88
  :snapshot_hidden_custom_fields, :snapshots_transport_destination_url,
86
89
  :snapshots_transport_auth_key, :snapshots_redact_sql_queries,
87
- :snapshots_transport_gzip_requests
90
+ :snapshots_transport_gzip_requests, :max_snapshot_groups
88
91
 
89
92
  # Deprecated options
90
93
  attr_accessor :use_existing_jquery
@@ -45,80 +45,53 @@ module Rack
45
45
  raise NotImplementedError.new("should_take_snapshot? is not implemented")
46
46
  end
47
47
 
48
- def push_snapshot(page_struct, config)
48
+ def push_snapshot(page_struct, group_name, config)
49
49
  raise NotImplementedError.new("push_snapshot is not implemented")
50
50
  end
51
51
 
52
- def fetch_snapshots(batch_size: 200, &blk)
53
- raise NotImplementedError.new("fetch_snapshots is not implemented")
52
+ # returns a hash where the keys are group names and the values
53
+ # are hashes that contain 3 keys:
54
+ # 1. `:worst_score` => the duration of the worst/slowest snapshot in the group (float)
55
+ # 2. `:best_score` => the duration of the best/fastest snapshot in the group (float)
56
+ # 3. `:snapshots_count` => the number of snapshots in the group (integer)
57
+ def fetch_snapshots_overview
58
+ raise NotImplementedError.new("fetch_snapshots_overview is not implemented")
54
59
  end
55
60
 
56
- def snapshot_groups_overview
57
- groups = {}
58
- fetch_snapshots do |batch|
59
- batch.each do |snapshot|
60
- group_name = default_snapshot_grouping(snapshot)
61
- hash = groups[group_name] ||= {}
62
- hash[:snapshots_count] ||= 0
63
- hash[:snapshots_count] += 1
64
- if !hash[:worst_score] || hash[:worst_score] < snapshot.duration_ms
65
- groups[group_name][:worst_score] = snapshot.duration_ms
66
- end
67
- if !hash[:best_score] || hash[:best_score] > snapshot.duration_ms
68
- groups[group_name][:best_score] = snapshot.duration_ms
69
- end
70
- end
71
- end
72
- groups = groups.to_a
61
+ # @param group_name [String]
62
+ # @return [Array<Rack::MiniProfiler::TimerStruct::Page>] list of snapshots of the group. Blank array if the group doesn't exist.
63
+ def fetch_snapshots_group(group_name)
64
+ raise NotImplementedError.new("fetch_snapshots_group is not implemented")
65
+ end
66
+
67
+ def load_snapshot(id, group_name)
68
+ raise NotImplementedError.new("load_snapshot is not implemented")
69
+ end
70
+
71
+ def snapshots_overview
72
+ groups = fetch_snapshots_overview.to_a
73
73
  groups.sort_by! { |name, hash| hash[:worst_score] }
74
74
  groups.reverse!
75
75
  groups.map! { |name, hash| hash.merge(name: name) }
76
76
  groups
77
77
  end
78
78
 
79
- def find_snapshots_group(group_name)
79
+ def snapshots_group(group_name)
80
+ snapshots = fetch_snapshots_group(group_name)
80
81
  data = []
81
- fetch_snapshots do |batch|
82
- batch.each do |snapshot|
83
- snapshot_group_name = default_snapshot_grouping(snapshot)
84
- if group_name == snapshot_group_name
85
- data << {
86
- id: snapshot[:id],
87
- duration: snapshot.duration_ms,
88
- sql_count: snapshot[:sql_count],
89
- timestamp: snapshot[:started_at],
90
- custom_fields: snapshot[:custom_fields]
91
- }
92
- end
93
- end
82
+ snapshots.each do |snapshot|
83
+ data << {
84
+ id: snapshot[:id],
85
+ duration: snapshot.duration_ms,
86
+ sql_count: snapshot[:sql_count],
87
+ timestamp: snapshot[:started_at],
88
+ custom_fields: snapshot[:custom_fields]
89
+ }
94
90
  end
95
91
  data.sort_by! { |s| s[:duration] }
96
92
  data.reverse!
97
93
  data
98
94
  end
99
-
100
- def load_snapshot(id)
101
- raise NotImplementedError.new("load_snapshot is not implemented")
102
- end
103
-
104
- private
105
-
106
- def default_snapshot_grouping(snapshot)
107
- group_name = rails_route_from_path(snapshot[:request_path], snapshot[:request_method])
108
- group_name ||= snapshot[:request_path]
109
- "#{snapshot[:request_method]} #{group_name}"
110
- end
111
-
112
- def rails_route_from_path(path, method)
113
- if defined?(Rails) && defined?(ActionController::RoutingError)
114
- hash = Rails.application.routes.recognize_path(path, method: method)
115
- if hash && hash[:controller] && hash[:action]
116
- "#{hash[:controller]}##{hash[:action]}"
117
- end
118
- end
119
- rescue ActionController::RoutingError
120
- nil
121
- end
122
95
  end
123
96
  end
124
97
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'securerandom'
4
+
3
5
  module Rack
4
6
  class MiniProfiler
5
7
  class FileStore < AbstractStore
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'securerandom'
4
+
3
5
  module Rack
4
6
  class MiniProfiler
5
7
  class MemoryStore < AbstractStore
@@ -53,6 +55,7 @@ module Rack
53
55
 
54
56
  @token1, @token2, @cycle_at = nil
55
57
  @snapshots_cycle = 0
58
+ @snapshot_groups = {}
56
59
  @snapshots = []
57
60
 
58
61
  initialize_locks
@@ -152,28 +155,69 @@ module Rack
152
155
  end
153
156
  end
154
157
 
155
- def push_snapshot(page_struct, config)
158
+ def push_snapshot(page_struct, group_name, config)
159
+ @snapshots_lock.synchronize do
160
+ group = @snapshot_groups[group_name]
161
+ if !group
162
+ @snapshot_groups[group_name] = {
163
+ worst_score: page_struct.duration_ms,
164
+ best_score: page_struct.duration_ms,
165
+ snapshots: [page_struct]
166
+ }
167
+ if @snapshot_groups.size > config.max_snapshot_groups
168
+ group_keys = @snapshot_groups.keys
169
+ group_keys.sort_by! do |key|
170
+ @snapshot_groups[key][:worst_score]
171
+ end
172
+ group_keys.reverse!
173
+ group_keys.pop(group_keys.size - config.max_snapshot_groups)
174
+ @snapshot_groups = @snapshot_groups.slice(*group_keys)
175
+ end
176
+ else
177
+ snapshots = group[:snapshots]
178
+ snapshots << page_struct
179
+ snapshots.sort_by!(&:duration_ms)
180
+ snapshots.reverse!
181
+ if snapshots.size > config.max_snapshots_per_group
182
+ snapshots.pop(snapshots.size - config.max_snapshots_per_group)
183
+ end
184
+ group[:worst_score] = snapshots[0].duration_ms
185
+ group[:best_score] = snapshots[-1].duration_ms
186
+ end
187
+ end
188
+ end
189
+
190
+ def fetch_snapshots_overview
156
191
  @snapshots_lock.synchronize do
157
- @snapshots << page_struct
158
- @snapshots.sort_by! { |s| s.duration_ms }
159
- @snapshots.reverse!
160
- if @snapshots.size > config.snapshots_limit
161
- @snapshots.slice!(-1)
192
+ groups = {}
193
+ @snapshot_groups.each do |name, group|
194
+ groups[name] = {
195
+ worst_score: group[:worst_score],
196
+ best_score: group[:best_score],
197
+ snapshots_count: group[:snapshots].size
198
+ }
162
199
  end
200
+ groups
163
201
  end
164
202
  end
165
203
 
166
- def fetch_snapshots(batch_size: 200, &blk)
204
+ def fetch_snapshots_group(group_name)
167
205
  @snapshots_lock.synchronize do
168
- @snapshots.each_slice(batch_size) do |batch|
169
- blk.call(batch)
206
+ group = @snapshot_groups[group_name]
207
+ if group
208
+ group[:snapshots].dup
209
+ else
210
+ []
170
211
  end
171
212
  end
172
213
  end
173
214
 
174
- def load_snapshot(id)
215
+ def load_snapshot(id, group_name)
175
216
  @snapshots_lock.synchronize do
176
- @snapshots.find { |s| s[:id] == id }
217
+ group = @snapshot_groups[group_name]
218
+ if group
219
+ group[:snapshots].find { |s| s[:id] == id }
220
+ end
177
221
  end
178
222
  end
179
223
 
@@ -182,7 +226,7 @@ module Rack
182
226
  # used in tests only
183
227
  def wipe_snapshots_data
184
228
  @snapshots_cycle = 0
185
- @snapshots = []
229
+ @snapshot_groups = {}
186
230
  end
187
231
  end
188
232
  end