rorvswild 1.8.1 → 1.9.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: d947482c8e791eebcf34ce670f5b35f4f6aa4bf547fec308119ed18839cfae4a
4
- data.tar.gz: 87f8a8ed97ea66cdc405209c92d03a637471b4507a22179ca2dde273f183ecda
3
+ metadata.gz: e4752356e6d6e1b7b9883b55c5f9f29223dedd2c31cc4abdac85065215b1b05b
4
+ data.tar.gz: 254eaff9c5412c5d1e121644d0993f0a8557748ac25370a9266ce1e61fe9f031
5
5
  SHA512:
6
- metadata.gz: 909542f1359138d44c8b8cec6ac8599e5b8e3eafb5d1eb27de0099be040a06e9753ff4068ba0b7f706c4b080aae18b7d340054679e9a6235fc82f8db05a0e0ea
7
- data.tar.gz: '080bdc4b330af84eb7aad989588a93a46b672c22725d8d6c534befddce6178765961b716915808bd7e4fe08d5d44f47c747402133573fce9135a9958fab92d25'
6
+ metadata.gz: b4b7acb9cc0fe303310648985dda337a3ab11c941af8c0e4d06199404064eee3c9a5cd93993f94e701fbc121f7f633123395fd89e5ddd3e28b52cd9cfa2e65e5
7
+ data.tar.gz: 404254bcfd4f9bb5c165da7955d712f1435514c061f70b019c84fbc1973626b71935a87ee8b1eae9b9b2741d79e74d2cfaef8378472f65b2a22ca78e03a99590
@@ -114,8 +114,8 @@ module RorVsWild
114
114
  end
115
115
  end
116
116
 
117
- def start_request
118
- current_data || initialize_data
117
+ def start_request(queue_time_ms = 0)
118
+ current_data || initialize_data(queue_time_ms)
119
119
  end
120
120
 
121
121
  def stop_request
@@ -195,9 +195,9 @@ module RorVsWild
195
195
 
196
196
  private
197
197
 
198
- def initialize_data
198
+ def initialize_data(queue_time_ms = 0)
199
199
  Thread.current[:rorvswild_data] = {
200
- started_at: RorVsWild.clock_milliseconds,
200
+ started_at: RorVsWild.clock_milliseconds - queue_time_ms,
201
201
  gc_section: Section.start_gc_timing,
202
202
  environment: Host.to_h,
203
203
  section_stack: [],
@@ -91,7 +91,7 @@ module RorVsWild
91
91
  end
92
92
 
93
93
  def self.shell(command)
94
- stdout, stderr, process = Open3.capture3(command) rescue nil
94
+ stdout, _, process = Open3.capture3(command) rescue nil
95
95
  stdout if process && process.success?
96
96
  end
97
97
  end
@@ -8,7 +8,7 @@ module RorVsWild
8
8
 
9
9
  def self.os
10
10
  @os_description ||= `uname -sr`.strip
11
- rescue Exception => ex
11
+ rescue Exception
12
12
  @os_description = RbConfig::CONFIG["host_os"]
13
13
  end
14
14
 
@@ -189,11 +189,12 @@ RorVsWild.Local.Request.prototype.runtime = function() {
189
189
  RorVsWild.Local.Request.prototype.sections = function() {
190
190
  return this.data.sections.map(function(section) {
191
191
  var runtime = (section.total_runtime - section.children_runtime)
192
- var object = {
192
+ return {
193
193
  id: RorVsWild.Local.nextId(),
194
194
  impact: RorVsWild.Local.formatImpact(runtime * 100 / this.data.runtime),
195
195
  language: RorVsWild.Local.kindToLanguage(section.kind),
196
196
  totalRuntime: RorVsWild.Local.relevantRounding(section.total_runtime),
197
+ asyncRuntime: RorVsWild.Local.relevantRounding(section.async_runtime),
197
198
  childrenRuntime: RorVsWild.Local.relevantRounding(section.children_runtime),
198
199
  selfRuntime: RorVsWild.Local.relevantRounding(runtime),
199
200
  runtime: RorVsWild.Local.relevantRounding(runtime),
@@ -204,9 +205,9 @@ RorVsWild.Local.Request.prototype.sections = function() {
204
205
  file: section.file,
205
206
  line: section.line,
206
207
  location: section.kind == "view" ? section.file : section.file + ":" + section.line,
207
- locationUrl: RorVsWild.Local.pathToUrl(this.data.environment.cwd, section.file, section.line)
208
+ locationUrl: RorVsWild.Local.pathToUrl(this.data.environment.cwd, section.file, section.line),
209
+ isAsync: RorVsWild.Local.relevantRounding(section.async_runtime) > 0,
208
210
  }
209
- return object
210
211
  }.bind(this)).sort(function(a, b) { return b.selfRuntime - a.selfRuntime })
211
212
  }
212
213
 
@@ -179,6 +179,12 @@
179
179
 
180
180
  <div class="rorvswild-local-panel__details__section__code">
181
181
  <dl>
182
+ {{#isAsync}}
183
+ <div>
184
+ <dt title="Time spent as non blocking IO">Async runtime</dt>
185
+ <dd>{{asyncRuntime}}<small>ms</small></dd>
186
+ </div>
187
+ {{/isAsync}}
182
188
  <div>
183
189
  <dt title="self + children">Total runtime</dt>
184
190
  <dd title="{{selfRuntime}} + {{childrenRuntime}}">{{totalRuntime}}<small>ms</small></dd>
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RorVsWild
2
4
  module Plugin
3
5
  class ActionView
4
6
  def self.setup
5
7
  return if @installed
6
8
  return unless defined?(::ActiveSupport::Notifications.subscribe)
7
- ActiveSupport::Notifications.subscribe("render_partial.action_view", new)
8
- ActiveSupport::Notifications.subscribe("render_template.action_view", new)
9
+ ActiveSupport::Notifications.subscribe("render_partial.action_view", plugin = new)
10
+ ActiveSupport::Notifications.subscribe("render_template.action_view", plugin)
11
+ ActiveSupport::Notifications.subscribe("render_collection.action_view", plugin)
9
12
  @installed = true
10
13
  end
11
14
 
@@ -15,10 +18,11 @@ module RorVsWild
15
18
 
16
19
  def finish(name, id, payload)
17
20
  RorVsWild::Section.stop do |section|
18
- section.kind = "view".freeze
21
+ section.kind = "view"
19
22
  section.commands << RorVsWild.agent.locator.relative_path(payload[:identifier])
20
23
  section.file = section.command
21
- section.line = 1
24
+ section.line = 0
25
+ section.calls = payload[:count] if payload[:count] # render collection
22
26
  end
23
27
  end
24
28
  end
@@ -29,6 +29,18 @@ module RorVsWild
29
29
  RorVsWild::Section.stop
30
30
  end
31
31
 
32
+ # Async queries
33
+ def publish_event(event)
34
+ section = Section.new
35
+ section.total_ms = event.payload[:lock_wait]
36
+ section.async_ms = event.duration - event.payload[:lock_wait]
37
+ section.gc_time_ms = event.respond_to?(:gc_time) ? event.gc_time : 0 # gc_time since Rails 7.2.0
38
+ section.commands << normalize_sql_query(event.payload[:sql])
39
+ section.kind = "sql"
40
+ (parent = Section.current) && parent.children_ms += section.total_ms
41
+ RorVsWild.agent.add_section(section)
42
+ end
43
+
32
44
  SQL_STRING_REGEX = /'((?:''|\\'|[^'])*)'/
33
45
  SQL_NUMERIC_REGEX = /(?<!\w)\d+(\.\d+)?(?!\w)/
34
46
  SQL_PARAMETER_REGEX = /\$\d+/
@@ -3,6 +3,49 @@
3
3
  module RorVsWild
4
4
  module Plugin
5
5
  class Middleware
6
+ module RequestQueueTime
7
+
8
+ ACCEPTABLE_HEADERS = [
9
+ 'HTTP_X_REQUEST_START',
10
+ 'HTTP_X_QUEUE_START',
11
+ 'HTTP_X_MIDDLEWARE_START'
12
+ ].freeze
13
+
14
+ MINIMUM_TIMESTAMP = 1577836800.freeze # 2020/01/01 UTC
15
+ DIVISORS = [1_000_000, 1_000, 1].freeze
16
+
17
+ def parse_queue_time_header(env)
18
+ return unless env
19
+
20
+ earliest = nil
21
+
22
+ ACCEPTABLE_HEADERS.each do |header|
23
+ if (header_value = env[header])
24
+ timestamp = parse_timestamp(header_value.delete_prefix("t="))
25
+ if timestamp && (!earliest || timestamp < earliest)
26
+ earliest = timestamp
27
+ end
28
+ end
29
+ end
30
+
31
+ [earliest, Time.now.to_f].min if earliest
32
+ end
33
+
34
+ private
35
+
36
+ def parse_timestamp(timestamp)
37
+ timestamp = timestamp.to_f
38
+ return unless timestamp.finite?
39
+
40
+ DIVISORS.each do |divisor|
41
+ t = timestamp / divisor
42
+ return t if t > MINIMUM_TIMESTAMP
43
+ end
44
+ end
45
+ end
46
+
47
+ include RequestQueueTime
48
+
6
49
  def self.setup
7
50
  return if @installed
8
51
  Rails.application.config.middleware.unshift(RorVsWild::Plugin::Middleware, nil) if defined?(Rails)
@@ -14,8 +57,10 @@ module RorVsWild
14
57
  end
15
58
 
16
59
  def call(env)
17
- RorVsWild.agent.start_request
60
+ queue_time_ms = calculate_queue_time(env)
61
+ RorVsWild.agent.start_request(queue_time_ms || 0)
18
62
  RorVsWild.agent.current_data[:path] = env["ORIGINAL_FULLPATH"]
63
+ add_queue_time_section(queue_time_ms)
19
64
  section = RorVsWild::Section.start
20
65
  section.file, section.line = rails_engine_location
21
66
  section.commands << "Rails::Engine#call"
@@ -28,6 +73,25 @@ module RorVsWild
28
73
 
29
74
  private
30
75
 
76
+ def add_queue_time_section(queue_time_ms)
77
+ return unless queue_time_ms
78
+
79
+ section = Section.new
80
+ section.stop
81
+ section.total_ms = queue_time_ms
82
+ section.gc_time_ms = 0
83
+ section.file = "request-queue"
84
+ section.line = 0
85
+ section.kind = "queue"
86
+ RorVsWild.agent.add_section(section)
87
+ end
88
+
89
+ def calculate_queue_time(env)
90
+ queue_time_from_header = parse_queue_time_header(env)
91
+
92
+ ((Time.now.to_f - queue_time_from_header) * 1000).round if queue_time_from_header
93
+ end
94
+
31
95
  def rails_engine_location
32
96
  @rails_engine_location = ::Rails::Engine.instance_method(:call).source_location
33
97
  end
@@ -3,7 +3,7 @@
3
3
  module RorVsWild
4
4
  class Section
5
5
  attr_reader :start_ms, :commands, :gc_start_ms
6
- attr_accessor :kind, :file, :line, :calls, :children_ms, :total_ms, :gc_time_ms
6
+ attr_accessor :kind, :file, :line, :calls, :children_ms, :total_ms, :gc_time_ms, :async_ms
7
7
 
8
8
  def self.start(&block)
9
9
  section = Section.new
@@ -31,7 +31,7 @@ module RorVsWild
31
31
  def self.start_gc_timing
32
32
  section = Section.new
33
33
  section.calls = GC.count
34
- section.file, section.line = "ruby/gc.c", 42
34
+ section.file, section.line = "ruby/gc.c", 0
35
35
  section.add_command("GC.start")
36
36
  section.kind = "gc"
37
37
  section
@@ -62,6 +62,7 @@ module RorVsWild
62
62
  @calls = 1
63
63
  @total_ms = 0
64
64
  @children_ms = 0
65
+ @async_ms = 0
65
66
  @kind = "code"
66
67
  location = RorVsWild.agent.locator.find_most_relevant_location(caller_locations)
67
68
  @file = RorVsWild.agent.locator.relative_path(location.path)
@@ -93,7 +94,7 @@ module RorVsWild
93
94
  end
94
95
 
95
96
  def as_json(options = nil)
96
- {calls: calls, total_runtime: total_ms, children_runtime: children_ms, kind: kind, started_at: start_ms, file: file, line: line, command: command}
97
+ {calls: calls, total_runtime: total_ms, children_runtime: children_ms, async_runtime: async_ms, kind: kind, started_at: start_ms, file: file, line: line, command: command}
97
98
  end
98
99
 
99
100
  def to_json(options = {})
@@ -1,3 +1,3 @@
1
1
  module RorVsWild
2
- VERSION = "1.8.1".freeze
2
+ VERSION = "1.9.0".freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rorvswild
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.1
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Bernard
8
8
  - Antoine Marguerie
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-10-17 00:00:00.000000000 Z
12
+ date: 2025-01-30 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Performances and errors insights for rails developers.
15
15
  email:
@@ -71,7 +71,7 @@ licenses:
71
71
  metadata:
72
72
  source_code_uri: https://github.com/BaseSecrete/rorvswild
73
73
  changelog_uri: https://github.com/BaseSecrete/rorvswild/blob/master/CHANGELOG.md
74
- post_install_message:
74
+ post_install_message:
75
75
  rdoc_options: []
76
76
  require_paths:
77
77
  - lib
@@ -86,8 +86,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  - !ruby/object:Gem::Version
87
87
  version: '0'
88
88
  requirements: []
89
- rubygems_version: 3.5.9
90
- signing_key:
89
+ rubygems_version: 3.5.22
90
+ signing_key:
91
91
  specification_version: 4
92
92
  summary: Ruby on Rails applications monitoring
93
93
  test_files: []