dial 0.3.0 → 0.3.2
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/dial/configuration.rb +1 -0
- data/lib/dial/constants.rb +2 -0
- data/lib/dial/engine/routes.rb +1 -1
- data/lib/dial/middleware/panel.rb +10 -2
- data/lib/dial/middleware.rb +6 -7
- data/lib/dial/prosopite.rb +21 -0
- data/lib/dial/railtie.rb +9 -18
- data/lib/dial/version.rb +1 -1
- data/lib/dial.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1cf2ace9f735e0ffd4c290b742bbf7f89f322babb96636e942fe1faaf92a8e45
|
4
|
+
data.tar.gz: '06279e4a580959080df277abe1a8488bca20f50276d9221ca399366f570e1426'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bde524497181a25dbeddc54f8f04ff128714844a5f2b6d8c84ae569bf4d45cb39942dddbe0d687e63c7b3c23f590410128de4abfffa69e5ee41923be4a73b6c1
|
7
|
+
data.tar.gz: e2bd5aef0ee0975a8e9c227a77296f40811d558251035771177c11f2128b599d905644186237280b3718f64ff65b31b834e01cfd222e5baba0077a53ee631451
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.2] - 2025-05-14
|
4
|
+
|
5
|
+
- Consolidate railtie initializers
|
6
|
+
|
7
|
+
## [0.3.1] - 2025-05-03
|
8
|
+
|
9
|
+
- Only show truncated summary + one full query in N+1 logs
|
10
|
+
- Fix parsing of multiline N+1 query logs
|
11
|
+
- Truncate summary N+1 queries to 100 chars
|
12
|
+
|
3
13
|
## [0.3.0] - 2025-05-02
|
4
14
|
|
5
15
|
- Require rails 7.1.0 or later
|
data/lib/dial/configuration.rb
CHANGED
data/lib/dial/constants.rb
CHANGED
@@ -11,6 +11,7 @@ module Dial
|
|
11
11
|
|
12
12
|
HTTP_ACCEPT = "HTTP_ACCEPT"
|
13
13
|
CONTENT_TYPE = ::Rack::CONTENT_TYPE
|
14
|
+
CONTENT_TYPE_HTML = "text/html"
|
14
15
|
CONTENT_LENGTH = ::Rack::CONTENT_LENGTH
|
15
16
|
NONCE = ::ActionDispatch::ContentSecurityPolicy::Request::NONCE
|
16
17
|
REQUEST_TIMING = "dial_request_timing"
|
@@ -20,6 +21,7 @@ module Dial
|
|
20
21
|
VERNIER_INTERVAL = 200
|
21
22
|
VERNIER_ALLOCATION_INTERVAL = 2_000
|
22
23
|
VERNIER_PROFILE_OUT_RELATIVE_DIRNAME = "tmp/dial/profiles"
|
24
|
+
VERNIER_PROFILE_OUT_FILE_EXTENSION = ".json.gz"
|
23
25
|
VERNIER_VIEWER_URL = "https://vernier.prof"
|
24
26
|
|
25
27
|
PROSOPITE_IGNORE_QUERIES = [/schema_migrations/i].freeze
|
data/lib/dial/engine/routes.rb
CHANGED
@@ -4,7 +4,7 @@ Dial::Engine.routes.draw do
|
|
4
4
|
scope path: "/dial", as: "dial" do
|
5
5
|
get "profile", to: lambda { |env|
|
6
6
|
uuid = env[::Rack::QUERY_STRING].sub "uuid=", ""
|
7
|
-
path = String ::Rails.root.join Dial::VERNIER_PROFILE_OUT_RELATIVE_DIRNAME,
|
7
|
+
path = String ::Rails.root.join Dial::VERNIER_PROFILE_OUT_RELATIVE_DIRNAME, (uuid + VERNIER_PROFILE_OUT_FILE_EXTENSION)
|
8
8
|
|
9
9
|
if File.exist? path
|
10
10
|
[
|
@@ -4,6 +4,8 @@ require "uri"
|
|
4
4
|
|
5
5
|
module Dial
|
6
6
|
class Panel
|
7
|
+
QUERY_CHARS_TRUNCATION_THRESHOLD = 100
|
8
|
+
|
7
9
|
class << self
|
8
10
|
def html env, headers, profile_out_filename, query_logs, ruby_vm_stat, gc_stat, gc_stat_heap, server_timing
|
9
11
|
<<~HTML
|
@@ -190,7 +192,7 @@ module Dial
|
|
190
192
|
def formatted_profile_output env, profile_out_filename
|
191
193
|
url_base = ::Rails.application.routes.url_helpers.dial_url host: env[::Rack::HTTP_HOST]
|
192
194
|
prefix = "/" unless url_base.end_with? "/"
|
193
|
-
uuid = profile_out_filename.delete_suffix
|
195
|
+
uuid = profile_out_filename.delete_suffix VERNIER_PROFILE_OUT_FILE_EXTENSION
|
194
196
|
profile_out_url = URI.encode_www_form_component url_base + "#{prefix}dial/profile?uuid=#{uuid}"
|
195
197
|
|
196
198
|
"<a href='https://vernier.prof/from-url/#{profile_out_url}' target='_blank'>View profile</a>"
|
@@ -223,7 +225,7 @@ module Dial
|
|
223
225
|
query_logs.map do |(queries, stack_lines)|
|
224
226
|
<<~HTML
|
225
227
|
<details>
|
226
|
-
<summary>#{queries.
|
228
|
+
<summary>#{truncated_query queries.first}</summary>
|
227
229
|
<div class="section query-logs">
|
228
230
|
#{queries.map { |query| "<span>#{query}</span>" }.join}
|
229
231
|
#{stack_lines.map { |stack_line| "<span>#{stack_line}</span>" }.join}
|
@@ -236,6 +238,12 @@ module Dial
|
|
236
238
|
end
|
237
239
|
end
|
238
240
|
|
241
|
+
def truncated_query query
|
242
|
+
return query if query.length <= QUERY_CHARS_TRUNCATION_THRESHOLD
|
243
|
+
|
244
|
+
query[0...QUERY_CHARS_TRUNCATION_THRESHOLD] + "..."
|
245
|
+
end
|
246
|
+
|
239
247
|
def formatted_ruby_vm_stat ruby_vm_stat
|
240
248
|
ruby_vm_stat.map { |key, value| "<span><b>#{key}:</b> #{value}</span>" }.join
|
241
249
|
end
|
data/lib/dial/middleware.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "vernier"
|
4
4
|
require "prosopite"
|
5
5
|
|
6
|
+
require_relative "prosopite"
|
6
7
|
require_relative "middleware/panel"
|
7
8
|
require_relative "middleware/ruby_stat"
|
8
9
|
require_relative "middleware/rails_stat"
|
@@ -12,20 +13,18 @@ module Dial
|
|
12
13
|
include RubyStat
|
13
14
|
include RailsStat
|
14
15
|
|
15
|
-
HTML_CONTENT_TYPE = "text/html"
|
16
|
-
|
17
16
|
def initialize app
|
18
17
|
@app = app
|
19
18
|
end
|
20
19
|
|
21
20
|
def call env
|
22
|
-
unless env[HTTP_ACCEPT]&.include?
|
21
|
+
unless env[HTTP_ACCEPT]&.include? CONTENT_TYPE_HTML
|
23
22
|
return @app.call env
|
24
23
|
end
|
25
24
|
|
26
25
|
start_time = Process.clock_gettime Process::CLOCK_MONOTONIC
|
27
26
|
|
28
|
-
profile_out_filename = "#{Util.uuid}_vernier
|
27
|
+
profile_out_filename = "#{Util.uuid}_vernier" + VERNIER_PROFILE_OUT_FILE_EXTENSION
|
29
28
|
profile_out_pathname = "#{profile_out_dir_pathname}/#{profile_out_filename}"
|
30
29
|
|
31
30
|
status, headers, rack_body, ruby_vm_stat, gc_stat, gc_stat_heap, vernier_result = nil
|
@@ -40,7 +39,7 @@ module Dial
|
|
40
39
|
end
|
41
40
|
server_timing = server_timing headers
|
42
41
|
|
43
|
-
unless headers[CONTENT_TYPE]&.include?
|
42
|
+
unless headers[CONTENT_TYPE]&.include? CONTENT_TYPE_HTML
|
44
43
|
return [status, headers, rack_body]
|
45
44
|
end
|
46
45
|
|
@@ -106,13 +105,13 @@ module Dial
|
|
106
105
|
when /N\+1 queries detected/
|
107
106
|
[[[],[]], :queries, 0]
|
108
107
|
when /Call stack/
|
109
|
-
entry.first << "+ #{count -
|
108
|
+
entry.first << "+ #{count - 1} more queries" if count > 1
|
110
109
|
[entry, :call_stack, count]
|
111
110
|
else
|
112
111
|
case section
|
113
112
|
when :queries
|
114
113
|
count += 1
|
115
|
-
entry.first << line.strip if count
|
114
|
+
entry.first << line.strip if count == 1
|
116
115
|
[entry, :queries, count]
|
117
116
|
when :call_stack
|
118
117
|
if line.strip.empty?
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
4
|
+
|
5
|
+
module Dial
|
6
|
+
module Prosopite
|
7
|
+
def send_notifications
|
8
|
+
tc[:prosopite_notifications] = tc[:prosopite_notifications].to_h do |queries, kaller|
|
9
|
+
[queries.map { |query| query.squish }, kaller]
|
10
|
+
end
|
11
|
+
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ::Prosopite
|
18
|
+
class << self
|
19
|
+
prepend Dial::Prosopite
|
20
|
+
end
|
21
|
+
end
|
data/lib/dial/railtie.rb
CHANGED
@@ -9,36 +9,27 @@ require_relative "prosopite_logger"
|
|
9
9
|
|
10
10
|
module Dial
|
11
11
|
class Railtie < ::Rails::Railtie
|
12
|
-
initializer "dial.
|
12
|
+
initializer "dial.setup", after: :load_config_initializers do |app|
|
13
|
+
# use middleware
|
13
14
|
app.middleware.insert_before 0, Middleware
|
14
|
-
end
|
15
|
-
|
16
|
-
initializer "dial.set_up_vernier", after: :load_config_initializers do |app|
|
17
|
-
app.config.after_initialize do
|
18
|
-
FileUtils.mkdir_p ::Rails.root.join VERNIER_PROFILE_OUT_RELATIVE_DIRNAME
|
19
|
-
end
|
20
|
-
end
|
21
15
|
|
22
|
-
|
23
|
-
stale_files("#{profile_out_dir_pathname}
|
16
|
+
# clean up stale vernier profile output files
|
17
|
+
stale_files("#{profile_out_dir_pathname}/*" + VERNIER_PROFILE_OUT_FILE_EXTENSION).each do |profile_out_file|
|
24
18
|
File.delete profile_out_file rescue nil
|
25
19
|
end
|
26
|
-
end
|
27
20
|
|
28
|
-
initializer "dial.set_up_prosopite", after: :load_config_initializers do |app|
|
29
21
|
app.config.after_initialize do
|
22
|
+
# set up vernier
|
23
|
+
FileUtils.mkdir_p ::Rails.root.join VERNIER_PROFILE_OUT_RELATIVE_DIRNAME
|
24
|
+
|
25
|
+
# set up prosopite
|
30
26
|
if ::ActiveRecord::Base.configurations.configurations.any? { |config| config.adapter == "postgresql" }
|
31
27
|
require "pg_query"
|
32
28
|
end
|
33
|
-
|
34
29
|
::Prosopite.custom_logger = ProsopiteLogger.new PROSOPITE_LOG_IO
|
35
|
-
end
|
36
|
-
end
|
37
30
|
|
38
|
-
|
39
|
-
app.config.after_initialize do
|
31
|
+
# finalize configuration
|
40
32
|
Dial._configuration.freeze
|
41
|
-
|
42
33
|
::Prosopite.ignore_queries = Dial._configuration.prosopite_ignore_queries
|
43
34
|
end
|
44
35
|
end
|
data/lib/dial/version.rb
CHANGED
data/lib/dial.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dial
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Young
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- lib/dial/middleware/panel.rb
|
114
114
|
- lib/dial/middleware/rails_stat.rb
|
115
115
|
- lib/dial/middleware/ruby_stat.rb
|
116
|
+
- lib/dial/prosopite.rb
|
116
117
|
- lib/dial/prosopite_logger.rb
|
117
118
|
- lib/dial/railtie.rb
|
118
119
|
- lib/dial/util.rb
|
@@ -137,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
138
|
- !ruby/object:Gem::Version
|
138
139
|
version: '0'
|
139
140
|
requirements: []
|
140
|
-
rubygems_version: 3.6.
|
141
|
+
rubygems_version: 3.6.9
|
141
142
|
specification_version: 4
|
142
143
|
summary: A modern profiler for your Rails application
|
143
144
|
test_files: []
|