rorvswild 1.10.1 → 1.11.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec6464a7193ffe1e78cc6d84804a74e37dd66e20f086b357215e143cca8e3339
4
- data.tar.gz: 0547a433e44c778c2f7199be0e6532325e572ac868f85b2209dd99eea1ba4593
3
+ metadata.gz: 78dcaca37ba08f20d8b6fa091902f46973d48db05feabce1ab4600bd4afb48c3
4
+ data.tar.gz: 626507452a31fe1e63fab2826d239c4d13f03c36e8bda1690deb1ba766733bc6
5
5
  SHA512:
6
- metadata.gz: 5929a6e6deb9fab72c05e5446363ed3cab3368c2b3c7a2e68ef9e4fa58b0fcc1736421936b6153a8eab650f33a4337bb691744af4739bd99dad63db9c1dbdb2d
7
- data.tar.gz: d5533fe4bc53ec97eed6d52e8637a7f231ffed0d56af0716d7e1713d0d3042d88620c201d50edb61e29af26bd786d666d30a403d54ff9b64d046b15ae180fb71
6
+ metadata.gz: 89a838d0e59c7a552ef0b961efe32420e9f271dbff74ca9427be809dc07b8a5b6158be53c10aac5bb3c69cfb6d1682e034b5b3d5f01898814e68f2443b3ffa6e
7
+ data.tar.gz: 35e531b0cfe704c0641ac075713893a46a55aefd453cc6fea5a34f2f3b814639096834bb8e9a9adfb11d2041693282da525a2e2b47c3d8c830e9003050e43bdd
data/README.md CHANGED
@@ -1,26 +1,25 @@
1
1
 
2
- # RorVsWild
2
+ # RoRvsWild
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/rorvswild.svg)](https://badge.fury.io/rb/rorvswild)
5
- [![Maintainability](https://api.codeclimate.com/v1/badges/2c4805cf658d7af794fe/maintainability)](https://codeclimate.com/github/BaseSecrete/rorvswild/maintainability)
6
5
 
7
- <img align="right" width="120px" src="./images/rorvswild_logo.jpg">
6
+ <img align="right" width="120px" src="./images/rorvswild_logo.png">
8
7
 
9
- *RoRvsWild* is a ruby gem to monitor performances and exceptions in Ruby on Rails applications.
8
+ *RoRvsWild* is a Ruby gem to monitor performances and exceptions in Ruby on Rails applications.
10
9
 
11
- This gem has a double mode, **development** and **production**.
12
- It can be used without an account to monitor your requests performances in your development environment.
13
- It can also be used in your production and staging environments with an account on https://rorvswild.com. With such an account you also get extra benefits such as 30 day trace, background jobs monitoring, exceptions monitoring and notifications.
10
+ This gem has a double mode: **development** and **production**.
11
+ It can be used without an account to monitor the performance of your requests in your development environment.
12
+ It can also be used in your production and staging environments with an account on https://rorvswild.com. With such an account, you also get additional benefits, including 30-day trace, background job monitoring, exception monitoring, and notifications.
14
13
 
15
14
  ## Development mode
16
15
 
17
16
  ### Install the gem
18
17
 
19
18
  * Add in your Gemfile `gem "rorvswild"`
20
- * Run `bundle install` in you terminal
21
- * Restart your local server and you’ll see a small button in the bottom left corner of your page.
19
+ * Run `bundle install` in your terminal.
20
+ * Restart your local server, and you’ll see a small button in the bottom left corner of your page.
22
21
 
23
- <img width="166px" src="./images/rorvswild_local_button.png">
22
+ <img width="218px" src="./images/rorvswild_local_button.png" alt="RoRvsWild Local button">
24
23
 
25
24
  Click on the button, or navigate to http://localhost:3000/rorvswild to see the details panel:
26
25
 
@@ -29,13 +28,13 @@ Click on the button, or navigate to http://localhost:3000/rorvswild to see the d
29
28
  ## Production mode
30
29
 
31
30
  **To monitor your production or staging environment, you need an API key.**
32
- Signup on https://www.rorvswild.com and create an app to get one.
31
+ Sign up on https://www.rorvswild.com and create an app to get one.
33
32
 
34
33
  * Add in your Gemfile `gem "rorvswild"`
35
- * Run `bundle install` in you terminal
36
- * Run `rorvswild-install API_KEY` in you terminal
37
- * Deploy/Restart your app
38
- * Make a few requests and refresh your app page on rorvswild.com to view the dashboard.
34
+ * Run `bundle install` in your terminal.
35
+ * Run `rorvswild-install API_KEY` in your terminal.
36
+ * Deploy/Restart your app.
37
+ * Make a few requests, and refresh your app page on rorvswild.com to view the dashboard.
39
38
 
40
39
  ![RoRvsWild Production](./images/rorvswild_prod.png)
41
40
 
@@ -113,12 +113,10 @@ module RorVsWild
113
113
 
114
114
  def start_execution(execution)
115
115
  Thread.current[:rorvswild_execution] ||= execution
116
- RorVsWild::Section.start
117
116
  end
118
117
 
119
118
  def stop_execution
120
119
  return unless execution = current_execution
121
- RorVsWild::Section.stop
122
120
  execution.stop
123
121
  case execution
124
122
  when Execution::Job then queue_job
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "set"
4
4
  require "uri"
5
+ require "zlib"
5
6
  require "json/ext"
6
7
  require "net/http"
7
8
 
@@ -24,6 +25,7 @@ module RorVsWild
24
25
  @mutex = Mutex.new
25
26
  @config = config
26
27
  @headers = {
28
+ "Content-Encoding" => "deflate",
27
29
  "Content-Type" => "application/json",
28
30
  "X-RorVsWild-Version" => RorVsWild::VERSION,
29
31
  "X-Ruby-Version" => RUBY_VERSION,
@@ -36,7 +38,7 @@ module RorVsWild
36
38
  uri = URI(api_url + path)
37
39
  post = Net::HTTP::Post.new(uri.path, @headers)
38
40
  post.basic_auth(nil, api_key)
39
- post.body = JSON.generate(data)
41
+ post.body = Zlib.deflate(JSON.generate(data), Zlib::BEST_SPEED)
40
42
  transmit(post)
41
43
  end
42
44
 
@@ -2,21 +2,21 @@
2
2
 
3
3
  module RorVsWild
4
4
  class Execution
5
- attr_reader :parameters, :sections, :section_stack, :error_context, :runtime
5
+ attr_reader :parameters, :sections, :section_stack, :error_context, :runtime, :root_section, :queue_section
6
6
 
7
7
  attr_accessor :error, :name
8
8
 
9
9
  def initialize(name, parameters)
10
+ @root_section = Section::Root.new
11
+
10
12
  @name = name
11
13
  @parameters = parameters
12
14
  @runtime = nil
13
15
  @error = nil
14
16
  @error_context = nil
15
17
 
16
- @started_at = RorVsWild.clock_milliseconds
17
- @gc_section = Section.start_gc_timing
18
18
  @environment = Host.to_h
19
- @section_stack = []
19
+ @section_stack = [@root_section]
20
20
  @sections = []
21
21
  end
22
22
 
@@ -30,20 +30,18 @@ module RorVsWild
30
30
 
31
31
  def add_queue_time(queue_time_ms)
32
32
  return unless queue_time_ms
33
- @started_at -= queue_time_ms
34
- section = Section.new
35
- section.total_ms = queue_time_ms
36
- section.gc_time_ms = 0
37
- section.file = "queue"
38
- section.line = 0
39
- section.kind = "queue"
40
- add_section(section)
33
+ @queue_section = Section::Queue.new(queue_time_ms)
34
+ add_section(@queue_section)
35
+ @queue_section
41
36
  end
42
37
 
43
38
  def stop
44
- Section.stop_gc_timing(@gc_section)
45
- @sections << @gc_section if @gc_section.calls > 0 && @gc_section.total_ms > 0
46
- @runtime = RorVsWild.clock_milliseconds - @started_at
39
+ Section.stop # root section
40
+ if @root_section.gc_time_ms > 0
41
+ add_section(Section::GarbageCollection.new(@root_section.gc_time_ms, @root_section.gc_calls))
42
+ end
43
+ @runtime = @root_section.total_ms + @root_section.gc_time_ms
44
+ @runtime += @queue_section.total_ms if @queue_section
47
45
  end
48
46
 
49
47
  def as_json(options = nil)
@@ -66,25 +64,6 @@ module RorVsWild
66
64
 
67
65
  private
68
66
 
69
- def start_gc_timing
70
- section = Section.new
71
- section.calls = GC.count
72
- section.file, section.line = "ruby/gc.c", 0
73
- section.add_command("GC.start")
74
- section.kind = "gc"
75
- section
76
- end
77
-
78
- if GC.respond_to?(:total_time)
79
- def gc_total_ms
80
- GC.total_time / 1_000_000.0 # nanosecond -> millisecond
81
- end
82
- else
83
- def gc_total_ms
84
- GC::Profiler.total_time * 1000 # second -> millisecond
85
- end
86
- end
87
-
88
67
  class Job < Execution
89
68
  def add_exception(exception)
90
69
  super(exception)
@@ -22,44 +22,59 @@ development:
22
22
  # widget: top-left, top-right, bottom-left, bottom-right or hidden
23
23
 
24
24
  # Open files in your text editor by clicking from the local widget.
25
- # VSCode: vscode://file${path}:${line}
26
- # Sublime: subl://${path}:${line}
27
- # It should be set with an env variable when developers are not using the same editor.
28
- editor_url: <%= ENV.fetch("RORVSWILD_EDITOR_URL", "vscode://file${path}:${line}") %>
25
+ # Leave commented to auto-detect from RAILS_EDITOR or EDITOR env vars (Rails 8.1+).
26
+ # Or set explicitly:
27
+ # editor_url: <%= ENV.fetch("RORVSWILD_EDITOR_URL", "vscode://file${path}:${line}") %>
28
+ # for VSCode: "vscode://file${path}:${line}"
29
+ # for Sublime: "subl://${path}:${line}"
29
30
 
30
31
  production:
31
- api_key: #{api_key}
32
- # ignore_requests: # Do not monitor the following actions
33
- # - SecretController#index
34
- # ignore_jobs: # Do not monitor the following jobs
35
- # - SecretJob
36
- # ignore_exceptions: # Do not record the following exceptions
37
- # - ActionController::RoutingError # By default to ignore 404
38
- # ignore_plugins:
39
- # - ActionController
40
- # - ActionMailer
41
- # - ActionView
42
- # - ActiveJob
43
- # - ActiveRecord
44
- # - DelayedJob
45
- # - Elasticsearch
46
- # - Mongo
47
- # - NetHttp
48
- # - Redis
49
- # - Resque
50
- # - Sidekiq
51
- #
32
+ api_key: <%= ENV["RORVSWILD_API_KEY"] || "#{api_key}" %>
33
+
34
+ # Do not monitor the following actions.
35
+ ignore_requests:
36
+ - SecretController#index
37
+
38
+ # Do not monitor the following jobs.
39
+ ignore_jobs:
40
+ - SecretJob
41
+
42
+ # Do not monitor the following exceptions.
43
+ ignore_exceptions:
44
+ # Noisy exceptions such as ActionNotFound, UnknownHttpMethod, etc are ignored by default.
45
+ - <%= ActionDispatch::ExceptionWrapper.rescue_responses.keys.join("\\n - ") %>
46
+ - AnotherNoisyError
47
+
48
+ # In case you want less details.
49
+ ignore_plugins:
50
+ # - ActionController
51
+ # - ActionMailer
52
+ # - ActionView
53
+ # - ActiveJob
54
+ # - ActiveRecord
55
+ # - DelayedJob
56
+ # - Elasticsearch
57
+ # - Faktory
58
+ # - Mongo
59
+ # - NetHttp
60
+ # - Rack
61
+ # - RailsCache
62
+ # - RailsError
63
+ # - Redis
64
+ # - Resque
65
+ # - Sidekiq
66
+
52
67
  # logger: log/rorvswild.log # By default it uses Rails.logger or Logger.new(STDOUT)
53
- #
54
- # # Deployment tracking is working without any actions from your part if the Rails app
55
- # # is inside a Git repository, is deployed via Capistrano.
56
- # # In the other cases, you can provide the following details.
68
+
69
+ # Deployment tracking is working without any actions from your part if the Rails app
70
+ # is inside a Git repository, or deployed with Capistrano, Kamal, Heroku or Scalingo.
71
+ # In the other cases, you can provide the following details.
57
72
  # deployment:
58
73
  # revision: <%= "Anything that will return the deployment version" %> # Mandatory
59
74
  # description: <%= "Eventually if you have a description such as a Git message" %>
60
75
  # author: <%= "Author's name of the deployment" %>
61
76
  # email: <%= "emailOf@theAuthor.example" %>
62
- #
77
+
63
78
  # Sampling allows to send a fraction of jobs and requests.
64
79
  # If your app is sending hundred of millions of requests per month,
65
80
  # you will probably get the same precision if you send only a fraction of it.
@@ -74,6 +74,18 @@ RorVsWild.Local.prototype.toggleCommand = function(event) {
74
74
  document.querySelector(event.currentTarget.dataset.target).classList.toggle("is-open")
75
75
  }
76
76
 
77
+ RorVsWild.Local.prototype.toggleLowImpactSections = function(event) {
78
+ var button = event.currentTarget
79
+ var sections = document.querySelectorAll(".rorvswild-local-panel__details__section[data-low-impact]")
80
+ var isHidden = sections[0].classList.contains("is-hidden")
81
+
82
+ sections.forEach(function(section) {
83
+ section.classList.toggle("is-hidden")
84
+ })
85
+
86
+ button.textContent = isHidden ? "↑ Hide low impact sections" : "↓ View low impact sections"
87
+ }
88
+
77
89
  RorVsWild.Local.prototype.expand = function() {
78
90
  this.active = true
79
91
  this.goToRequestDetail(event)
@@ -107,6 +119,7 @@ RorVsWild.Local.formatImpact = function(impact) {
107
119
  }
108
120
 
109
121
  RorVsWild.Local.formatDateTime = function(date) {
122
+ if (!date || isNaN(date.getTime())) return ""
110
123
  var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
111
124
  var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()
112
125
  var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours()
@@ -189,9 +202,11 @@ RorVsWild.Local.Request.prototype.runtime = function() {
189
202
  RorVsWild.Local.Request.prototype.sections = function() {
190
203
  return this.data.sections.map(function(section) {
191
204
  var runtime = (section.total_runtime - section.children_runtime)
205
+ var impactValue = runtime * 100 / this.data.runtime
192
206
  return {
193
207
  id: RorVsWild.Local.nextId(),
194
- impact: RorVsWild.Local.formatImpact(runtime * 100 / this.data.runtime),
208
+ impact: RorVsWild.Local.formatImpact(impactValue),
209
+ isLowImpact: impactValue < 1,
195
210
  language: RorVsWild.Local.kindToLanguage(section.kind),
196
211
  totalRuntime: RorVsWild.Local.relevantRounding(section.total_runtime),
197
212
  asyncRuntime: RorVsWild.Local.relevantRounding(section.async_runtime),
@@ -212,6 +227,10 @@ RorVsWild.Local.Request.prototype.sections = function() {
212
227
  }.bind(this)).sort(function(a, b) { return b.selfRuntime - a.selfRuntime })
213
228
  }
214
229
 
230
+ RorVsWild.Local.Request.prototype.hasLowImpactSections = function() {
231
+ return this.sections().some(function(section) { return section.isLowImpact })
232
+ }
233
+
215
234
  RorVsWild.Local.Request.prototype.sectionsImpactPerKind = function() {
216
235
  var total = 0
217
236
  var perKind = this.sections().reduce(function(object, section) {
@@ -148,6 +148,11 @@
148
148
  {{#sections}}
149
149
  {{> RorVsWild.Local.Section}}
150
150
  {{/sections}}
151
+ {{#hasLowImpactSections}}
152
+ <div class="rorvswild-local-panel__toggle-low-impact">
153
+ <button data-events="click->toggleLowImpactSections" class="rorvswild-local-panel__toggle-low-impact__button">↓ view low impact sections</button>
154
+ </div>
155
+ {{/hasLowImpactSections}}
151
156
  </div>
152
157
  </script>
153
158
 
@@ -162,7 +167,7 @@
162
167
  </script>
163
168
 
164
169
  <script type="x-tmpl-mustache" data-partial="RorVsWild.Local.Section">
165
- <div class="rorvswild-local-panel__details__section" id="section-{{id}}">
170
+ <div class="rorvswild-local-panel__details__section {{#isLowImpact}}is-hidden{{/isLowImpact}}" {{#isLowImpact}}data-low-impact{{/isLowImpact}} id="section-{{id}}">
166
171
  <div class="rorvswild-local-panel__details__section__main">
167
172
  <span class="rorvswild-local-panel__details__section__file">
168
173
  <button data-events="click->toggleCommand" data-target="#section-{{id}}" class="rorvswild-local-panel__details__section__kind"><span>{{kind}}</button>
@@ -181,32 +186,32 @@
181
186
 
182
187
  <div class="rorvswild-local-panel__details__section__code">
183
188
  <dl>
184
- {{#isAsync}}
185
- <div>
186
- <dt title="Time spent as non blocking IO">Async runtime</dt>
187
- <dd>{{asyncRuntime}}<small>ms</small></dd>
188
- </div>
189
- {{/isAsync}}
190
189
  <div>
191
- <dt title="self + children">Total runtime</dt>
192
- <dd title="{{selfRuntime}} + {{childrenRuntime}}">{{totalRuntime}}<small>ms</small></dd>
190
+ <dt title="self runtime / calls">Average runtime</dt>
191
+ <dd title="{{selfRuntime}} / {{calls}}">{{averageRuntime}}<small>ms</small></dd>
193
192
  </div>
194
193
  <div>
195
- <dt title="total - self">Children runtime</dt>
196
- <dd title="{{totalRuntime}} - {{selfRuntime}}">{{childrenRuntime}}<small>ms</small></dd>
194
+ <dt>Calls</dt>
195
+ <dd class="dd--calls">{{calls}}<small>x</small></dd>
197
196
  </div>
198
197
  <div>
199
198
  <dt title="total - children">Self runtime</dt>
200
199
  <dd title="{{totalRuntime}} - {{childrenRuntime}}">{{selfRuntime}}<small>ms</small></dd>
201
200
  </div>
202
201
  <div>
203
- <dt>Calls</dt>
204
- <dd class="dd--calls">{{calls}}<small>x</small></dd>
202
+ <dt title="total - self">Children runtime</dt>
203
+ <dd title="{{totalRuntime}} - {{selfRuntime}}">{{childrenRuntime}}<small>ms</small></dd>
205
204
  </div>
206
205
  <div>
207
- <dt title="self runtime / calls">Average runtime</dt>
208
- <dd title="{{selfRuntime}} / {{calls}}">{{averageRuntime}}<small>ms</small></dd>
206
+ <dt title="self + children">Total runtime</dt>
207
+ <dd title="{{selfRuntime}} + {{childrenRuntime}}">{{totalRuntime}}<small>ms</small></dd>
209
208
  </div>
209
+ {{#isAsync}}
210
+ <div>
211
+ <dt title="Time spent as non blocking IO">Async runtime</dt>
212
+ <dd>{{asyncRuntime}}<small>ms</small></dd>
213
+ </div>
214
+ {{/isAsync}}
210
215
  </dl>
211
216
  {{#command}}
212
217
  <pre><code class="{{language}}">{{command}}</code></pre>
@@ -335,5 +340,5 @@
335
340
  {{/currentError}}
336
341
  </script>
337
342
 
338
- <link rel="stylesheet" media="all" href="/rorvswild.css"/>
343
+ <link rel="stylesheet" media="all" href="/rorvswild.css">
339
344
  <script src="/rorvswild.js"></script>
@@ -79,7 +79,13 @@ module RorVsWild
79
79
  end
80
80
 
81
81
  def editor_url
82
- RorVsWild.agent.config[:editor_url]
82
+ config[:editor_url] || rails_editor_url
83
+ end
84
+
85
+ def rails_editor_url
86
+ if editor = defined?(ActiveSupport::Editor) && ActiveSupport::Editor.current
87
+ editor.url_for("${path}", 999999.999999).gsub("999999.999999", "${line}").gsub("999999", "${line}")
88
+ end
83
89
  end
84
90
 
85
91
  def inject_into(html)
@@ -39,7 +39,7 @@ module RorVsWild
39
39
  private
40
40
 
41
41
  def push_to(data, name)
42
- data[:queued_at] = Time.now
42
+ data[:queued_at] = Time.now.iso8601(3)
43
43
  data[:uuid] = SecureRandom.uuid
44
44
  File.open(File.join(@directoy, "#{name}.ndjson"), "a") { |file| file.write(JSON.dump(data) + "\n") }
45
45
  end
@@ -126,6 +126,7 @@
126
126
  color: rgb(146 149 159) !important;
127
127
  cursor: pointer !important;
128
128
  line-height: 48px !important;
129
+ margin: 0 !important;
129
130
  }
130
131
 
131
132
  .rorvswild-local-panel__header nav li:hover {
@@ -350,6 +351,10 @@ h2.rorvswild-local-panel__title {
350
351
  display: block !important;
351
352
  }
352
353
 
354
+ .rorvswild-local-panel__details__section.is-hidden {
355
+ display: none !important;
356
+ }
357
+
353
358
  .rorvswild-local-panel__details__section:hover {
354
359
  background: rgb(26 29 36) !important;
355
360
  transition: all .3s !important;
@@ -433,30 +438,31 @@ a.rorvswild-local-panel__file__name:hover::after { opacity: 1; }
433
438
  .rorvswild-local-panel__details__section__code dl {
434
439
  display: flex;
435
440
  align-items: baseline;
436
- justify-content: flex-end;
437
441
  flex-wrap: wrap;
438
442
  margin: 0;
439
443
  padding: 0;
440
- text-align: right;
444
+ text-align: center;
441
445
  gap: 12px 24px;
442
446
  }
443
447
 
448
+ .rorvswild-local-panel__details__section__code dl > * {
449
+ flex: 1;
450
+ }
451
+
444
452
  .rorvswild-local-panel__details__section__code dt {
445
453
  white-space: nowrap;
446
- margin: 0;
454
+ margin: 4px 0 0;
447
455
  text-transform: uppercase;
448
456
  letter-spacing: 1px;
449
457
  font-size: 9px !important;
458
+ line-height: 12px;
459
+ font-weight: 400;
450
460
  }
451
461
 
452
462
  .rorvswild-local-panel__details__section__code dd {
453
463
  margin: 0;
454
- color: rgb(182 202 255) !important;
455
- font-weight: 700;
456
- }
457
-
458
- .rorvswild-local-panel__details__section__code dd.dd--calls {
459
464
  color: rgb(199 203 214) !important;
465
+ font-weight: 700;
460
466
  }
461
467
 
462
468
  .rorvswild-local-panel__details__section__code dl + pre[class*="language-"] {
@@ -593,6 +599,31 @@ span.rorvswild-local-panel__details__section__kind {
593
599
  overflow-wrap: break-word !important;
594
600
  }
595
601
 
602
+ .rorvswild-local-panel__toggle-low-impact {
603
+ padding: 12px !important;
604
+ }
605
+
606
+ .rorvswild-local-panel__toggle-low-impact__button {
607
+ background: transparent !important;
608
+ color: rgb(199 203 214) !important;
609
+ border: 1px solid rgb(72 75 80) !important;
610
+ box-shadow: none !important;
611
+ padding: 0px 8px !important;
612
+ font-size: 10px !important;
613
+ line-height: 24px !important;
614
+ text-transform: uppercase !important;
615
+ letter-spacing: 1px !important;
616
+ cursor: pointer !important;
617
+ font-weight: 700 !important;
618
+ transition: all 0.2s !important;
619
+ }
620
+
621
+ .rorvswild-local-panel__toggle-low-impact__button:hover {
622
+ background: transparent !important;
623
+ color: rgb(223 228 242) !important;
624
+ border-color: rgb(147 149 156) !important;
625
+ }
626
+
596
627
  /* Panel Footer */
597
628
  .rorvswild-local-panel__footer {
598
629
  width: 100% !important;
@@ -6,7 +6,7 @@ module RorVsWild
6
6
  def self.start(config = {})
7
7
  queue = RorVsWild::Local::Queue.new(config[:queue] || {})
8
8
  RorVsWild.start(config.merge(queue: queue))
9
- Rails.application.config.middleware.unshift(RorVsWild::Local::Middleware, nil)
9
+ Rails.application.config.middleware.unshift(RorVsWild::Local::Middleware, config)
10
10
  end
11
11
  end
12
12
  end
@@ -50,6 +50,9 @@ module RorVsWild
50
50
  SQL_ONE_LINE_COMMENT_REGEX =/--.*$/
51
51
  SQL_MULTI_LINE_COMMENT_REGEX = /\/\*.*?\*\//m
52
52
 
53
+ # Catch Regexp::TimeoutError without breaking before Ruby 3.2
54
+ REGEXP_TIMEOUT_ERROR = defined?(Regexp::TimeoutError) ? Regexp::TimeoutError : RegexpError
55
+
53
56
  def normalize_sql_query(sql)
54
57
  sql = sql.to_s.gsub(SQL_STRING_REGEX, "?")
55
58
  sql.gsub!(SQL_PARAMETER_REGEX, "?")
@@ -59,6 +62,8 @@ module RorVsWild
59
62
  sql.gsub!(SQL_MULTI_LINE_COMMENT_REGEX, "")
60
63
  sql.strip!
61
64
  sql
65
+ rescue REGEXP_TIMEOUT_ERROR
66
+ nil
62
67
  end
63
68
  end
64
69
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RorVsWild
4
+ module Plugin
5
+ class Rack
6
+ @installed = false
7
+
8
+ def self.setup(agent)
9
+ return if @installed
10
+ return if !defined?(ActiveSupport::Notifications.subscribe)
11
+ ActiveSupport::Notifications.subscribe("process_middleware.action_dispatch", new)
12
+ @installed = true
13
+ end
14
+
15
+ def start(name, id, payload)
16
+ section = RorVsWild::Section.start
17
+ section.kind = "rack"
18
+ if location = source_location(payload[:middleware])
19
+ section.file = location[0]
20
+ section.line = location[1]
21
+ section.commands << payload[:middleware]
22
+ else
23
+ section.file = payload[:middleware]
24
+ section.line = 0
25
+ end
26
+ end
27
+
28
+ def finish(name, id, payload)
29
+ RorVsWild::Section.stop
30
+ end
31
+
32
+ private
33
+
34
+ def source_location(middleware_name)
35
+ middleware = Kernel.const_get(middleware_name)
36
+ middleware.instance_method(:call).source_location
37
+ rescue NameError
38
+ end
39
+ end
40
+ end
41
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RorVsWild
4
4
  module Plugin
5
- class Middleware
5
+ module RailsEngine
6
6
  module RequestQueueTime
7
7
 
8
8
  ACCEPTABLE_HEADERS = [
@@ -49,26 +49,20 @@ module RorVsWild
49
49
  @installed = false
50
50
 
51
51
  def self.setup(agent)
52
- return if @installed
53
- Rails.application.config.middleware.unshift(RorVsWild::Plugin::Middleware, nil) if defined?(Rails)
52
+ return if @installed || !defined?(Rails::Engine)
53
+ Rails::Engine.prepend(self)
54
54
  @installed = true
55
55
  end
56
56
 
57
- def initialize(app, config)
58
- @app, @config = app, config
59
- end
60
-
61
57
  def call(env)
62
- execution = RorVsWild::Execution::Request.new(env["ORIGINAL_FULLPATH"])
58
+ execution = RorVsWild::Execution::Request.new(env["REQUEST_URI"])
63
59
  execution.add_queue_time(calculate_queue_time(env))
64
60
  RorVsWild.agent.start_execution(execution)
65
- section = RorVsWild::Section.start
66
- section.file, section.line = rails_engine_location
61
+ section = execution.root_section
62
+ section.file, section.line = method(:call).super_method.source_location
67
63
  section.commands << "Rails::Engine#call"
68
- code, headers, body = @app.call(env)
69
- [code, headers, body]
64
+ super
70
65
  ensure
71
- RorVsWild::Section.stop
72
66
  RorVsWild.agent.stop_execution
73
67
  end
74
68
 
@@ -79,10 +73,6 @@ module RorVsWild
79
73
 
80
74
  ((Time.now.to_f - queue_time_from_header) * 1000).round if queue_time_from_header
81
75
  end
82
-
83
- def rails_engine_location
84
- @rails_engine_location = ::Rails::Engine.instance_method(:call).source_location
85
- end
86
76
  end
87
77
  end
88
78
  end
@@ -28,21 +28,6 @@ module RorVsWild
28
28
  (sections = stack) && sections.last
29
29
  end
30
30
 
31
- def self.start_gc_timing
32
- section = Section.new
33
- section.calls = GC.count
34
- section.file, section.line = "ruby/gc.c", 0
35
- section.add_command("GC.start")
36
- section.kind = "gc"
37
- section
38
- end
39
-
40
- def self.stop_gc_timing(section)
41
- section.total_ms = gc_total_ms - section.gc_start_ms
42
- section.calls = GC.count - section.calls
43
- section
44
- end
45
-
46
31
  if GC.respond_to?(:total_time)
47
32
  def self.gc_total_ms
48
33
  GC.total_time / 1_000_000.0 # nanosecond -> millisecond
@@ -111,5 +96,46 @@ module RorVsWild
111
96
  string = @commands.to_a.join("\n")
112
97
  string.size > COMMAND_MAX_SIZE ? string[0, COMMAND_MAX_SIZE] + " [TRUNCATED]" : string
113
98
  end
99
+
100
+ class Root < Section
101
+ attr_reader :gc_calls
102
+
103
+ def initialize
104
+ super
105
+ @gc_count = GC.count
106
+ end
107
+
108
+ def stop
109
+ super
110
+ @gc_calls = GC.count - @gc_count
111
+ end
112
+ end
113
+
114
+ class GarbageCollection < Section
115
+ def initialize(total_ms, calls)
116
+ @total_ms = total_ms
117
+ @calls = calls > 0 ? calls : 1
118
+ @kind = "gc"
119
+ @file, @line = "ruby/gc.c", 0
120
+ @commands = Set.new
121
+ @children_ms = 0
122
+ @async_ms = 0
123
+ add_command("GC.start")
124
+ end
125
+ end
126
+
127
+ class Queue < Section
128
+ def initialize(total_ms)
129
+ @total_ms = total_ms
130
+ @gc_time_ms = 0
131
+ @file = "queue"
132
+ @line = 0
133
+ @kind = "queue"
134
+ @calls = 1
135
+ @children_ms = 0
136
+ @gc_time_ms = 0
137
+ @async_ms = 0
138
+ end
139
+ end
114
140
  end
115
141
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RorVsWild
4
- VERSION = "1.10.1"
4
+ VERSION = "1.11.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rorvswild
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.1
4
+ version: 1.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Bernard
@@ -55,10 +55,11 @@ files:
55
55
  - lib/rorvswild/plugin/delayed_job.rb
56
56
  - lib/rorvswild/plugin/elasticsearch.rb
57
57
  - lib/rorvswild/plugin/faktory.rb
58
- - lib/rorvswild/plugin/middleware.rb
59
58
  - lib/rorvswild/plugin/mongo.rb
60
59
  - lib/rorvswild/plugin/net_http.rb
60
+ - lib/rorvswild/plugin/rack.rb
61
61
  - lib/rorvswild/plugin/rails_cache.rb
62
+ - lib/rorvswild/plugin/rails_engine.rb
62
63
  - lib/rorvswild/plugin/rails_error.rb
63
64
  - lib/rorvswild/plugin/redis.rb
64
65
  - lib/rorvswild/plugin/resque.rb
@@ -88,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
89
  - !ruby/object:Gem::Version
89
90
  version: '0'
90
91
  requirements: []
91
- rubygems_version: 3.6.9
92
+ rubygems_version: 4.0.3
92
93
  specification_version: 4
93
94
  summary: Ruby on Rails applications monitoring
94
95
  test_files: []