rorvswild 1.7.0 → 1.8.0

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.
@@ -18,7 +18,7 @@ module RorVsWild
18
18
  RorVsWild.agent.current_data[:path] = env["ORIGINAL_FULLPATH"]
19
19
  section = RorVsWild::Section.start
20
20
  section.file, section.line = rails_engine_location
21
- section.command = "Rails::Engine#call"
21
+ section.commands << "Rails::Engine#call"
22
22
  code, headers, body = @app.call(env)
23
23
  [code, headers, body]
24
24
  ensure
@@ -26,23 +26,53 @@ module RorVsWild
26
26
  inject_server_timing(RorVsWild.agent.stop_request, headers)
27
27
  end
28
28
 
29
+ private
30
+
29
31
  def rails_engine_location
30
32
  @rails_engine_location = ::Rails::Engine.instance_method(:call).source_location
31
33
  end
32
34
 
33
- def format_server_timing(sections)
34
- sections.sort_by(&:self_runtime).reverse.map do |section|
35
+ def format_server_timing_header(sections)
36
+ sections.map do |section|
35
37
  if section.kind == "view"
36
- "#{section.kind};dur=#{section.self_runtime};desc=\"#{section.file}\""
38
+ "#{section.kind};dur=#{section.self_ms.round};desc=\"#{section.file}\""
37
39
  else
38
- "#{section.kind};dur=#{section.self_runtime};desc=\"#{section.file}:#{section.line}\""
40
+ "#{section.kind};dur=#{section.self_ms.round};desc=\"#{section.file}:#{section.line}\""
39
41
  end
40
42
  end.join(", ")
41
43
  end
42
44
 
45
+ def format_server_timing_ascii(sections, total_width = 80)
46
+ max_time = sections.map(&:self_ms).max
47
+ chart_width = (total_width * 0.25).to_i
48
+ rows = sections.map { |section|
49
+ [
50
+ section.kind == "view" ? section.file : "#{section.file}:#{section.line}",
51
+ "█" * (section.self_ms * (chart_width-1) / max_time),
52
+ "%.1fms" % section.self_ms,
53
+ ]
54
+ }
55
+ time_width = rows.map { |cols| cols[2].size }.max + 1
56
+ label_width = total_width - chart_width - time_width
57
+ rows.each { |cols| cols[0] = truncate_backwards(cols[0], label_width) }
58
+ template = "%-#{label_width}s%#{chart_width}s%#{time_width}s"
59
+ rows.map { |cols| format(template, *cols) }.join("\n")
60
+ end
61
+
62
+ def truncate_backwards(string, width)
63
+ string.size > width ? "…" + string[-(width - 1)..-1] : string
64
+ end
65
+
43
66
  def inject_server_timing(data, headers)
44
67
  return if !data || !data[:send_server_timing] || !(sections = data[:sections])
45
- headers["Server-Timing"] = format_server_timing(sections)
68
+ sections = sections.sort_by(&:self_ms).reverse[0,10]
69
+ headers["Server-Timing"] = format_server_timing_header(sections)
70
+ if data[:name] && RorVsWild.logger.level <= Logger::Severity::DEBUG
71
+ RorVsWild.logger.debug(["┤ #{data[:name]} ├".center(80, "─"),
72
+ format_server_timing_ascii(sections),
73
+ "─" * 80, nil].join("\n")
74
+ )
75
+ end
46
76
  end
47
77
  end
48
78
  end
@@ -15,8 +15,7 @@ module RorVsWild
15
15
  module V4
16
16
  def process(commands, &block)
17
17
  string = commands.map(&:first).join("\n")
18
- appendable = APPENDABLE_COMMANDS.include?(commands[0][0])
19
- RorVsWild.agent.measure_section(string, appendable_command: appendable, kind: "redis") do
18
+ RorVsWild.agent.measure_section(string, kind: "redis") do
20
19
  super(commands, &block)
21
20
  end
22
21
  end
@@ -24,8 +23,7 @@ module RorVsWild
24
23
 
25
24
  module V5
26
25
  def send_command(command, &block)
27
- appendable = APPENDABLE_COMMANDS.include?(command)
28
- RorVsWild.agent.measure_section(command[0], appendable_command: appendable, kind: "redis") do
26
+ RorVsWild.agent.measure_section(command[0], kind: "redis") do
29
27
  super(command, &block)
30
28
  end
31
29
  end
@@ -38,8 +36,6 @@ module RorVsWild
38
36
  RorVsWild.agent.measure_section("multi", kind: "redis") { super }
39
37
  end
40
38
  end
41
-
42
- APPENDABLE_COMMANDS = [:auth, :select]
43
39
  end
44
40
  end
45
41
  end
@@ -27,6 +27,10 @@ module RorVsWild
27
27
  push_to(requests, data) if !@request_sampling_rate || rand <= @request_sampling_rate
28
28
  end
29
29
 
30
+ def push_error(data)
31
+ client.post_async("/errors", error: data)
32
+ end
33
+
30
34
  def push_to(array, data)
31
35
  mutex.synchronize do
32
36
  wakeup_thread if array.push(data).size >= FLUSH_TRESHOLD || !thread
@@ -2,8 +2,8 @@
2
2
 
3
3
  module RorVsWild
4
4
  class Section
5
- attr_reader :started_at
6
- attr_accessor :kind, :file, :line, :calls, :command, :children_runtime, :total_runtime, :appendable_command
5
+ attr_reader :start_ms, :commands, :gc_start_ms
6
+ attr_accessor :kind, :file, :line, :calls, :children_ms, :total_ms, :gc_time_ms
7
7
 
8
8
  def self.start(&block)
9
9
  section = Section.new
@@ -15,8 +15,8 @@ module RorVsWild
15
15
  def self.stop(&block)
16
16
  return unless stack && section = stack.pop
17
17
  block.call(section) if block_given?
18
- section.total_runtime = RorVsWild.clock_milliseconds - section.started_at
19
- current.children_runtime += section.total_runtime if current
18
+ section.stop
19
+ current.children_ms += section.total_ms if current
20
20
  RorVsWild.agent.add_section(section)
21
21
  end
22
22
 
@@ -28,17 +28,52 @@ 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", 42
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
+ if GC.respond_to?(:total_time)
47
+ def self.gc_total_ms
48
+ GC.total_time / 1_000_000.0 # nanosecond -> millisecond
49
+ end
50
+ else
51
+ def self.gc_total_ms
52
+ GC::Profiler.total_time * 1000 # second -> millisecond
53
+ end
54
+ end
55
+
31
56
  def initialize
57
+ @start_ms = RorVsWild.clock_milliseconds
58
+ @end_ms = nil
59
+ @gc_start_ms = Section.gc_total_ms
60
+ @gc_end_ms = nil
61
+ @gc_time_ms = 0
32
62
  @calls = 1
33
- @total_runtime = 0
34
- @children_runtime = 0
63
+ @total_ms = 0
64
+ @children_ms = 0
35
65
  @kind = "code"
36
- @started_at = RorVsWild.clock_milliseconds
37
66
  location = RorVsWild.agent.locator.find_most_relevant_location(caller_locations)
38
67
  @file = RorVsWild.agent.locator.relative_path(location.path)
39
68
  @line = location.lineno
40
- @command = nil
41
- @appendable_command = false
69
+ @commands = Set.new
70
+ end
71
+
72
+ def stop
73
+ @gc_end_ms = self.class.gc_total_ms
74
+ @gc_time_ms = @gc_end_ms - @gc_start_ms
75
+ @end_ms = RorVsWild.clock_milliseconds
76
+ @total_ms = @end_ms - @start_ms - gc_time_ms
42
77
  end
43
78
 
44
79
  def sibling?(section)
@@ -47,35 +82,33 @@ module RorVsWild
47
82
 
48
83
  def merge(section)
49
84
  self.calls += section.calls
50
- self.total_runtime += section.total_runtime
51
- self.children_runtime += section.children_runtime
52
- if section
53
- if appendable_command
54
- self.command = self.command.dup if self.command.frozen?
55
- self.command << "\n" + section.command
56
- end
57
- else
58
- self.command = section.command
59
- end
60
- self.appendable_command = appendable_command && section.appendable_command
85
+ self.total_ms += section.total_ms
86
+ self.children_ms += section.children_ms
87
+ self.gc_time_ms += section.gc_time_ms
88
+ commands.merge(section.commands)
61
89
  end
62
90
 
63
- def self_runtime
64
- total_runtime - children_runtime
91
+ def self_ms
92
+ total_ms - children_ms
65
93
  end
66
94
 
67
- COMMAND_MAX_SIZE = 5_000
95
+ 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
+ end
68
98
 
69
- def command=(value)
70
- @command = value && value.size > COMMAND_MAX_SIZE ? value[0, COMMAND_MAX_SIZE] + " [TRUNCATED]" : value
99
+ def to_json(options = {})
100
+ as_json.to_json(options)
71
101
  end
72
102
 
73
- def to_h
74
- {calls: calls, total_runtime: total_runtime, children_runtime: children_runtime, kind: kind, started_at: started_at, file: file, line: line, command: command}
103
+ def add_command(command)
104
+ commands << command
75
105
  end
76
106
 
77
- def to_json(options = {})
78
- to_h.to_json(options)
107
+ COMMAND_MAX_SIZE = 5_000
108
+
109
+ def command
110
+ string = @commands.join("\n")
111
+ string.size > COMMAND_MAX_SIZE ? string[0, COMMAND_MAX_SIZE] + " [TRUNCATED]" : string
79
112
  end
80
113
  end
81
114
  end
@@ -1,3 +1,3 @@
1
1
  module RorVsWild
2
- VERSION = "1.7.0".freeze
2
+ VERSION = "1.8.0".freeze
3
3
  end
data/lib/rorvswild.rb CHANGED
@@ -26,8 +26,14 @@ module RorVsWild
26
26
  @logger ||= initialize_logger
27
27
  end
28
28
 
29
- def self.measure(code_or_name = nil, &block)
30
- block ? measure_block(code_or_name, &block) : measure_code(code_or_name)
29
+ def self.measure(method_or_code = nil, &block)
30
+ if block
31
+ measure_block(method_or_code, &block)
32
+ elsif method_or_code.is_a?(Method) || method_or_code.is_a?(UnboundMethod)
33
+ measure_method(method_or_code)
34
+ else
35
+ measure_code(method_or_code)
36
+ end
31
37
  end
32
38
 
33
39
  def self.measure_code(code)
@@ -38,6 +44,10 @@ module RorVsWild
38
44
  agent ? agent.measure_block(name , &block) : block.call
39
45
  end
40
46
 
47
+ def self.measure_method(method)
48
+ agent.measure_method(method) if agent
49
+ end
50
+
41
51
  def self.catch_error(context = nil, &block)
42
52
  agent ? agent.catch_error(context, &block) : block.call
43
53
  end
@@ -67,7 +77,7 @@ module RorVsWild
67
77
  end
68
78
 
69
79
  def self.clock_milliseconds
70
- Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
80
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
71
81
  end
72
82
 
73
83
  def self.check
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.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Bernard
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-04-22 00:00:00.000000000 Z
12
+ date: 2024-06-14 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Performances and errors insights for rails developers.
15
15
  email: