rack-mini-profiler 3.1.0 → 3.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -1
- data/README.md +2 -1
- data/lib/html/includes.js +5 -7
- data/lib/html/includes.tmpl +1 -1
- data/lib/html/speedscope/{import.cf0fa83f.js → import.8ae8aa3d.js} +26 -18
- data/lib/html/speedscope/index.html +1 -1
- data/lib/html/speedscope/release.txt +3 -3
- data/lib/html/speedscope/{speedscope.44364064.js → speedscope.f27db165.js} +99 -106
- data/lib/html/vendor.js +1 -1
- data/lib/mini_profiler/actions.rb +152 -0
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/config.rb +2 -1
- data/lib/mini_profiler/profiling_methods.rb +4 -0
- data/lib/mini_profiler/storage/memcache_store.rb +9 -0
- data/lib/mini_profiler/storage/memory_store.rb +2 -2
- data/lib/mini_profiler/timer_struct/request.rb +6 -0
- data/lib/mini_profiler/timer_struct/sql.rb +3 -1
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/mini_profiler/views.rb +193 -0
- data/lib/mini_profiler.rb +65 -349
- data/lib/mini_profiler_rails/railtie.rb +12 -1
- data/lib/mini_profiler_rails/railtie_methods.rb +1 -1
- data/lib/patches/sql_patches.rb +2 -2
- data/rack-mini-profiler.gemspec +1 -1
- metadata +8 -6
data/lib/mini_profiler.rb
CHANGED
@@ -12,11 +12,15 @@ require 'mini_profiler/context'
|
|
12
12
|
require 'mini_profiler/client_settings'
|
13
13
|
require 'mini_profiler/gc_profiler'
|
14
14
|
require 'mini_profiler/snapshots_transporter'
|
15
|
+
require 'mini_profiler/views'
|
16
|
+
require 'mini_profiler/actions'
|
15
17
|
|
16
18
|
module Rack
|
17
19
|
class MiniProfiler
|
18
|
-
|
20
|
+
include Actions
|
21
|
+
include Views
|
19
22
|
|
23
|
+
class << self
|
20
24
|
include Rack::MiniProfiler::ProfilingMethods
|
21
25
|
attr_accessor :subscribe_sql_active_record
|
22
26
|
|
@@ -37,14 +41,6 @@ module Rack
|
|
37
41
|
@config ||= Config.default
|
38
42
|
end
|
39
43
|
|
40
|
-
def resources_root
|
41
|
-
@resources_root ||= ::File.expand_path("../html", __FILE__)
|
42
|
-
end
|
43
|
-
|
44
|
-
def share_template
|
45
|
-
@share_template ||= ERB.new(::File.read(::File.expand_path("html/share.html", ::File.dirname(__FILE__))))
|
46
|
-
end
|
47
|
-
|
48
44
|
def current
|
49
45
|
Thread.current[:mini_profiler_private]
|
50
46
|
end
|
@@ -138,70 +134,6 @@ module Rack
|
|
138
134
|
@config.user_provider.call(env)
|
139
135
|
end
|
140
136
|
|
141
|
-
def serve_results(env)
|
142
|
-
request = Rack::Request.new(env)
|
143
|
-
id = request.params['id']
|
144
|
-
group_name = request.params['group']
|
145
|
-
is_snapshot = group_name && group_name.size > 0
|
146
|
-
if is_snapshot
|
147
|
-
page_struct = @storage.load_snapshot(id, group_name)
|
148
|
-
else
|
149
|
-
page_struct = @storage.load(id)
|
150
|
-
end
|
151
|
-
if !page_struct && is_snapshot
|
152
|
-
id = ERB::Util.html_escape(id)
|
153
|
-
return [404, {}, ["Snapshot with id '#{id}' not found"]]
|
154
|
-
elsif !page_struct
|
155
|
-
@storage.set_viewed(user(env), id)
|
156
|
-
id = ERB::Util.html_escape(id)
|
157
|
-
user_info = ERB::Util.html_escape(user(env))
|
158
|
-
return [404, {}, ["Request not found: #{id} - user #{user_info}"]]
|
159
|
-
end
|
160
|
-
if !page_struct[:has_user_viewed] && !is_snapshot
|
161
|
-
page_struct[:client_timings] = TimerStruct::Client.init_from_form_data(env, page_struct)
|
162
|
-
page_struct[:has_user_viewed] = true
|
163
|
-
@storage.save(page_struct)
|
164
|
-
@storage.set_viewed(user(env), id)
|
165
|
-
end
|
166
|
-
|
167
|
-
# If we're an XMLHttpRequest, serve up the contents as JSON
|
168
|
-
if request.xhr?
|
169
|
-
result_json = page_struct.to_json
|
170
|
-
[200, { 'Content-Type' => 'application/json' }, [result_json]]
|
171
|
-
else
|
172
|
-
# Otherwise give the HTML back
|
173
|
-
html = generate_html(page_struct, env)
|
174
|
-
[200, { 'Content-Type' => 'text/html' }, [html]]
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def generate_html(page_struct, env, result_json = page_struct.to_json)
|
179
|
-
# double-assigning to suppress "assigned but unused variable" warnings
|
180
|
-
path = path = "#{env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME']}#{@config.base_url_path}"
|
181
|
-
version = version = MiniProfiler::ASSET_VERSION
|
182
|
-
json = json = result_json
|
183
|
-
includes = includes = get_profile_script(env)
|
184
|
-
name = name = page_struct[:name]
|
185
|
-
duration = duration = page_struct.duration_ms.round(1).to_s
|
186
|
-
|
187
|
-
MiniProfiler.share_template.result(binding)
|
188
|
-
end
|
189
|
-
|
190
|
-
def serve_html(env)
|
191
|
-
path = env['PATH_INFO'].sub('//', '/')
|
192
|
-
file_name = path.sub(@config.base_url_path, '')
|
193
|
-
|
194
|
-
return serve_results(env) if file_name.eql?('results')
|
195
|
-
return handle_snapshots_request(env) if file_name.eql?('snapshots')
|
196
|
-
return serve_flamegraph(env) if file_name.eql?('flamegraph')
|
197
|
-
|
198
|
-
resources_env = env.dup
|
199
|
-
resources_env['PATH_INFO'] = file_name
|
200
|
-
|
201
|
-
rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' => "max-age=#{cache_control_value}")
|
202
|
-
rack_file.call(resources_env)
|
203
|
-
end
|
204
|
-
|
205
137
|
def current
|
206
138
|
MiniProfiler.current
|
207
139
|
end
|
@@ -228,13 +160,12 @@ module Rack
|
|
228
160
|
MiniProfiler.deauthorize_request if @config.authorization_mode == :allow_authorized
|
229
161
|
|
230
162
|
status = headers = body = nil
|
231
|
-
query_string = env['QUERY_STRING']
|
232
163
|
path = env['PATH_INFO'].sub('//', '/')
|
233
164
|
|
234
165
|
# Someone (e.g. Rails engine) could change the SCRIPT_NAME so we save it
|
235
166
|
env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME'] = ENV['PASSENGER_BASE_URI'] || env['SCRIPT_NAME']
|
236
167
|
|
237
|
-
skip_it =
|
168
|
+
skip_it = matches_action?('skip', env) || (
|
238
169
|
@config.skip_paths &&
|
239
170
|
@config.skip_paths.any? do |p|
|
240
171
|
if p.instance_of?(String)
|
@@ -262,15 +193,29 @@ module Rack
|
|
262
193
|
end
|
263
194
|
|
264
195
|
# handle all /mini-profiler requests here
|
265
|
-
|
196
|
+
if path.start_with? @config.base_url_path
|
197
|
+
file_name = path.sub(@config.base_url_path, '')
|
198
|
+
|
199
|
+
case file_name
|
200
|
+
when 'results'
|
201
|
+
return serve_results(env)
|
202
|
+
when 'snapshots'
|
203
|
+
self.current = nil
|
204
|
+
return serve_snapshot(env)
|
205
|
+
when 'flamegraph'
|
206
|
+
return serve_flamegraph(env)
|
207
|
+
end
|
208
|
+
|
209
|
+
return client_settings.handle_cookie(serve_file(env, file_name: file_name))
|
210
|
+
end
|
266
211
|
|
267
212
|
has_disable_cookie = client_settings.disable_profiling?
|
268
213
|
# manual session disable / enable
|
269
|
-
if
|
214
|
+
if matches_action?('disable', env) || has_disable_cookie
|
270
215
|
skip_it = true
|
271
216
|
end
|
272
217
|
|
273
|
-
if
|
218
|
+
if matches_action?('enable', env)
|
274
219
|
skip_it = false
|
275
220
|
config.enabled = true
|
276
221
|
end
|
@@ -279,54 +224,32 @@ module Rack
|
|
279
224
|
status, headers, body = @app.call(env)
|
280
225
|
client_settings.disable_profiling = true
|
281
226
|
return client_settings.handle_cookie([status, headers, body])
|
282
|
-
else
|
283
|
-
client_settings.disable_profiling = false
|
284
227
|
end
|
285
228
|
|
229
|
+
# remember that profiling is not disabled (ie enabled)
|
230
|
+
client_settings.disable_profiling = false
|
231
|
+
|
286
232
|
# profile gc
|
287
|
-
if
|
288
|
-
return tool_disabled_message(client_settings) if !advanced_debugging_enabled?
|
233
|
+
if matches_action?('profile-gc', env)
|
289
234
|
current.measure = false if current
|
290
|
-
return
|
235
|
+
return serve_profile_gc(env, client_settings)
|
291
236
|
end
|
292
237
|
|
293
238
|
# profile memory
|
294
|
-
if
|
295
|
-
return
|
296
|
-
|
297
|
-
unless defined?(MemoryProfiler) && MemoryProfiler.respond_to?(:report)
|
298
|
-
message = "Please install the memory_profiler gem and require it: add gem 'memory_profiler' to your Gemfile"
|
299
|
-
status, headers, body = @app.call(env)
|
300
|
-
body.close if body.respond_to? :close
|
301
|
-
|
302
|
-
return client_settings.handle_cookie(
|
303
|
-
text_result(message, status: 500, headers: headers)
|
304
|
-
)
|
305
|
-
end
|
306
|
-
|
307
|
-
query_params = Rack::Utils.parse_nested_query(query_string)
|
308
|
-
options = {
|
309
|
-
ignore_files: query_params['memory_profiler_ignore_files'],
|
310
|
-
allow_files: query_params['memory_profiler_allow_files'],
|
311
|
-
}
|
312
|
-
options[:top] = Integer(query_params['memory_profiler_top']) if query_params.key?('memory_profiler_top')
|
313
|
-
result = StringIO.new
|
314
|
-
report = MemoryProfiler.report(options) do
|
315
|
-
_, _, body = @app.call(env)
|
316
|
-
body.close if body.respond_to? :close
|
317
|
-
end
|
318
|
-
report.pretty_print(result)
|
319
|
-
return client_settings.handle_cookie(text_result(result.string))
|
239
|
+
if matches_action?('profile-memory', env)
|
240
|
+
return serve_profile_memory(env, client_settings)
|
320
241
|
end
|
321
242
|
|
243
|
+
# any other requests past this point are going to the app to be profiled
|
244
|
+
|
322
245
|
MiniProfiler.create_current(env, @config)
|
323
246
|
|
324
|
-
if
|
247
|
+
if matches_action?('normal-backtrace', env)
|
325
248
|
client_settings.backtrace_level = ClientSettings::BACKTRACE_DEFAULT
|
326
|
-
elsif
|
249
|
+
elsif matches_action?('no-backtrace', env)
|
327
250
|
current.skip_backtrace = true
|
328
251
|
client_settings.backtrace_level = ClientSettings::BACKTRACE_NONE
|
329
|
-
elsif
|
252
|
+
elsif matches_action?('full-backtrace', env) || client_settings.backtrace_full?
|
330
253
|
current.full_backtrace = true
|
331
254
|
client_settings.backtrace_level = ClientSettings::BACKTRACE_FULL
|
332
255
|
elsif client_settings.backtrace_none?
|
@@ -335,7 +258,7 @@ module Rack
|
|
335
258
|
|
336
259
|
flamegraph = nil
|
337
260
|
|
338
|
-
trace_exceptions =
|
261
|
+
trace_exceptions = matches_action?('trace-exceptions', env) && defined? TracePoint
|
339
262
|
status, headers, body, exceptions, trace = nil
|
340
263
|
|
341
264
|
if trace_exceptions
|
@@ -359,11 +282,11 @@ module Rack
|
|
359
282
|
# Prevent response body from being compressed
|
360
283
|
env['HTTP_ACCEPT_ENCODING'] = 'identity' if config.suppress_encoding
|
361
284
|
|
362
|
-
if
|
285
|
+
if matches_action?('flamegraph', env) || matches_action?('async-flamegraph', env) || env['HTTP_REFERER'] =~ /pp=async-flamegraph/
|
363
286
|
if defined?(StackProf) && StackProf.respond_to?(:run)
|
364
287
|
# do not sully our profile with mini profiler timings
|
365
288
|
current.measure = false
|
366
|
-
match_data =
|
289
|
+
match_data = action_parameters(env)['flamegraph_sample_rate']
|
367
290
|
|
368
291
|
if match_data && !match_data[1].to_f.zero?
|
369
292
|
sample_rate = match_data[1].to_f
|
@@ -371,7 +294,7 @@ module Rack
|
|
371
294
|
sample_rate = config.flamegraph_sample_rate
|
372
295
|
end
|
373
296
|
|
374
|
-
mode_match_data =
|
297
|
+
mode_match_data = action_parameters(env)['flamegraph_mode']
|
375
298
|
|
376
299
|
if mode_match_data && [:cpu, :wall, :object, :custom].include?(mode_match_data[1].to_sym)
|
377
300
|
mode = mode_match_data[1].to_sym
|
@@ -379,11 +302,20 @@ module Rack
|
|
379
302
|
mode = config.flamegraph_mode
|
380
303
|
end
|
381
304
|
|
305
|
+
ignore_gc_match_data = action_parameters(env)['flamegraph_ignore_gc']
|
306
|
+
|
307
|
+
if ignore_gc_match_data
|
308
|
+
ignore_gc = ignore_gc_match_data == 'true'
|
309
|
+
else
|
310
|
+
ignore_gc = config.flamegraph_ignore_gc
|
311
|
+
end
|
312
|
+
|
382
313
|
flamegraph = StackProf.run(
|
383
314
|
mode: mode,
|
384
315
|
raw: true,
|
385
316
|
aggregate: false,
|
386
|
-
interval: (sample_rate * 1000).to_i
|
317
|
+
interval: (sample_rate * 1000).to_i,
|
318
|
+
ignore_gc: ignore_gc
|
387
319
|
) do
|
388
320
|
status, headers, body = @app.call(env)
|
389
321
|
end
|
@@ -397,18 +329,7 @@ module Rack
|
|
397
329
|
)
|
398
330
|
end
|
399
331
|
elsif path == '/rack-mini-profiler/requests'
|
400
|
-
|
401
|
-
<!DOCTYPE html>
|
402
|
-
<html>
|
403
|
-
<head>
|
404
|
-
<title>Rack::MiniProfiler Requests</title>
|
405
|
-
</head>
|
406
|
-
<body>
|
407
|
-
</body>
|
408
|
-
</html>
|
409
|
-
HTML
|
410
|
-
|
411
|
-
status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html.dup]]
|
332
|
+
status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html]]
|
412
333
|
else
|
413
334
|
status, headers, body = @app.call(env)
|
414
335
|
end
|
@@ -429,7 +350,7 @@ module Rack
|
|
429
350
|
if trace_exceptions
|
430
351
|
body.close if body.respond_to? :close
|
431
352
|
|
432
|
-
query_params =
|
353
|
+
query_params = action_parameters(env)
|
433
354
|
trace_exceptions_filter = query_params['trace_exceptions_filter']
|
434
355
|
if trace_exceptions_filter
|
435
356
|
trace_exceptions_regex = Regexp.new(trace_exceptions_filter)
|
@@ -439,19 +360,19 @@ module Rack
|
|
439
360
|
return client_settings.handle_cookie(dump_exceptions exceptions)
|
440
361
|
end
|
441
362
|
|
442
|
-
if
|
363
|
+
if matches_action?("env", env)
|
443
364
|
return tool_disabled_message(client_settings) if !advanced_debugging_enabled?
|
444
365
|
body.close if body.respond_to? :close
|
445
366
|
return client_settings.handle_cookie(dump_env env)
|
446
367
|
end
|
447
368
|
|
448
|
-
if
|
369
|
+
if matches_action?("analyze-memory", env)
|
449
370
|
return tool_disabled_message(client_settings) if !advanced_debugging_enabled?
|
450
371
|
body.close if body.respond_to? :close
|
451
372
|
return client_settings.handle_cookie(analyze_memory)
|
452
373
|
end
|
453
374
|
|
454
|
-
if
|
375
|
+
if matches_action?("help", env)
|
455
376
|
body.close if body.respond_to? :close
|
456
377
|
return client_settings.handle_cookie(help(client_settings, env))
|
457
378
|
end
|
@@ -460,9 +381,9 @@ module Rack
|
|
460
381
|
page_struct[:user] = user(env)
|
461
382
|
page_struct[:root].record_time((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000)
|
462
383
|
|
463
|
-
if flamegraph &&
|
384
|
+
if flamegraph && matches_action?("flamegraph", env)
|
464
385
|
body.close if body.respond_to? :close
|
465
|
-
return client_settings.handle_cookie(self.flamegraph(flamegraph, path))
|
386
|
+
return client_settings.handle_cookie(self.flamegraph(flamegraph, path, env))
|
466
387
|
elsif flamegraph # async-flamegraph
|
467
388
|
page_struct[:has_flamegraph] = true
|
468
389
|
page_struct[:flamegraph] = flamegraph
|
@@ -485,12 +406,20 @@ module Rack
|
|
485
406
|
end
|
486
407
|
|
487
408
|
client_settings.handle_cookie([status, headers, body])
|
488
|
-
|
489
409
|
ensure
|
490
410
|
# Make sure this always happens
|
491
411
|
self.current = nil
|
492
412
|
end
|
493
413
|
|
414
|
+
def matches_action?(action, env)
|
415
|
+
env['QUERY_STRING'] =~ /#{@config.profile_parameter}=#{action}/ ||
|
416
|
+
env['HTTP_X_RACK_MINI_PROFILER'] == action
|
417
|
+
end
|
418
|
+
|
419
|
+
def action_parameters(env)
|
420
|
+
query_params = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
421
|
+
end
|
422
|
+
|
494
423
|
def inject_profiler(env, status, headers, body)
|
495
424
|
# mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
|
496
425
|
# Rack::ETag has already inserted some nonesense in the chain
|
@@ -663,78 +592,6 @@ module Rack
|
|
663
592
|
[status, headers, [body]]
|
664
593
|
end
|
665
594
|
|
666
|
-
def make_link(postfix, env)
|
667
|
-
link = env["PATH_INFO"] + "?" + env["QUERY_STRING"].sub("#{@config.profile_parameter}=help", "#{@config.profile_parameter}=#{postfix}")
|
668
|
-
"#{@config.profile_parameter}=<a href='#{ERB::Util.html_escape(link)}'>#{postfix}</a>"
|
669
|
-
end
|
670
|
-
|
671
|
-
def help(client_settings, env)
|
672
|
-
headers = { 'Content-Type' => 'text/html' }
|
673
|
-
html = <<~HTML
|
674
|
-
<!DOCTYPE html>
|
675
|
-
<html>
|
676
|
-
<head>
|
677
|
-
<title>Rack::MiniProfiler Help</title>
|
678
|
-
</head>
|
679
|
-
<body>
|
680
|
-
<pre style='line-height: 30px; font-size: 16px'>
|
681
|
-
This is the help menu of the <a href='#{Rack::MiniProfiler::SOURCE_CODE_URI}'>rack-mini-profiler</a> gem, append the following to your query string for more options:
|
682
|
-
|
683
|
-
#{make_link "help", env} : display this screen
|
684
|
-
#{make_link "env", env} : display the rack environment
|
685
|
-
#{make_link "skip", env} : skip mini profiler for this request
|
686
|
-
#{make_link "no-backtrace", env} #{"(*) " if client_settings.backtrace_none?}: don't collect stack traces from all the SQL executed (sticky, use #{@config.profile_parameter}=normal-backtrace to enable)
|
687
|
-
#{make_link "normal-backtrace", env} #{"(*) " if client_settings.backtrace_default?}: collect stack traces from all the SQL executed and filter normally
|
688
|
-
#{make_link "full-backtrace", env} #{"(*) " if client_settings.backtrace_full?}: enable full backtraces for SQL executed (use #{@config.profile_parameter}=normal-backtrace to disable)
|
689
|
-
#{make_link "disable", env} : disable profiling for this session
|
690
|
-
#{make_link "enable", env} : enable profiling for this session (if previously disabled)
|
691
|
-
#{make_link "profile-gc", env} : perform gc profiling on this request, analyzes ObjectSpace generated by request
|
692
|
-
#{make_link "profile-memory", env} : requires the memory_profiler gem, new location based report
|
693
|
-
#{make_link "flamegraph", env} : a graph representing sampled activity (requires the stackprof gem).
|
694
|
-
#{make_link "async-flamegraph", env} : store flamegraph data for this page and all its AJAX requests. Flamegraph links will be available in the mini-profiler UI (requires the stackprof gem).
|
695
|
-
#{make_link "flamegraph&flamegraph_sample_rate=1", env}: creates a flamegraph with the specified sample rate (in ms). Overrides value set in config
|
696
|
-
#{make_link "flamegraph&flamegraph_mode=cpu", env}: creates a flamegraph with the specified mode (one of cpu, wall, object, or custom). Overrides value set in config
|
697
|
-
#{make_link "flamegraph_embed", env} : a graph representing sampled activity (requires the stackprof gem), embedded resources for use on an intranet.
|
698
|
-
#{make_link "trace-exceptions", env} : will return all the spots where your application raises exceptions
|
699
|
-
#{make_link "analyze-memory", env} : will perform basic memory analysis of heap
|
700
|
-
</pre>
|
701
|
-
</body>
|
702
|
-
</html>
|
703
|
-
HTML
|
704
|
-
|
705
|
-
[200, headers, [html]]
|
706
|
-
end
|
707
|
-
|
708
|
-
def flamegraph(graph, path)
|
709
|
-
headers = { 'Content-Type' => 'text/html' }
|
710
|
-
html = <<~HTML
|
711
|
-
<!DOCTYPE html>
|
712
|
-
<html>
|
713
|
-
<head>
|
714
|
-
<title>Rack::MiniProfiler Flamegraph</title>
|
715
|
-
<style>
|
716
|
-
body { margin: 0; height: 100vh; }
|
717
|
-
#speedscope-iframe { width: 100%; height: 100%; border: none; }
|
718
|
-
</style>
|
719
|
-
</head>
|
720
|
-
<body>
|
721
|
-
<script type="text/javascript">
|
722
|
-
var graph = #{JSON.generate(graph)};
|
723
|
-
var json = JSON.stringify(graph);
|
724
|
-
var blob = new Blob([json], { type: 'text/plain' });
|
725
|
-
var objUrl = encodeURIComponent(URL.createObjectURL(blob));
|
726
|
-
var iframe = document.createElement('IFRAME');
|
727
|
-
iframe.setAttribute('id', 'speedscope-iframe');
|
728
|
-
document.body.appendChild(iframe);
|
729
|
-
var iframeUrl = '#{@config.base_url_path}speedscope/index.html#profileURL=' + objUrl + '&title=' + 'Flamegraph for #{CGI.escape(path)}';
|
730
|
-
iframe.setAttribute('src', iframeUrl);
|
731
|
-
</script>
|
732
|
-
</body>
|
733
|
-
</html>
|
734
|
-
HTML
|
735
|
-
[200, headers, [html]]
|
736
|
-
end
|
737
|
-
|
738
595
|
def ids(env)
|
739
596
|
all = ([current.page_struct[:id]] + (@storage.get_unviewed_ids(user(env)) || [])).uniq
|
740
597
|
if all.size > @config.max_traces_to_show
|
@@ -748,69 +605,6 @@ module Rack
|
|
748
605
|
ids(env).join(",")
|
749
606
|
end
|
750
607
|
|
751
|
-
# get_profile_script returns script to be injected inside current html page
|
752
|
-
# By default, profile_script is appended to the end of all html requests automatically.
|
753
|
-
# Calling get_profile_script cancels automatic append for the current page
|
754
|
-
# Use it when:
|
755
|
-
# * you have disabled auto append behaviour throught :auto_inject => false flag
|
756
|
-
# * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
|
757
|
-
def get_profile_script(env)
|
758
|
-
path = "#{env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME']}#{@config.base_url_path}"
|
759
|
-
version = MiniProfiler::ASSET_VERSION
|
760
|
-
if @config.assets_url
|
761
|
-
url = @config.assets_url.call('rack-mini-profiler.js', version, env)
|
762
|
-
css_url = @config.assets_url.call('rack-mini-profiler.css', version, env)
|
763
|
-
end
|
764
|
-
|
765
|
-
url = "#{path}includes.js?v=#{version}" if !url
|
766
|
-
css_url = "#{path}includes.css?v=#{version}" if !css_url
|
767
|
-
|
768
|
-
content_security_policy_nonce = @config.content_security_policy_nonce ||
|
769
|
-
env["action_dispatch.content_security_policy_nonce"] ||
|
770
|
-
env["secure_headers_content_security_policy_nonce"]
|
771
|
-
|
772
|
-
settings = {
|
773
|
-
path: path,
|
774
|
-
url: url,
|
775
|
-
cssUrl: css_url,
|
776
|
-
version: version,
|
777
|
-
verticalPosition: @config.vertical_position,
|
778
|
-
horizontalPosition: @config.horizontal_position,
|
779
|
-
showTrivial: @config.show_trivial,
|
780
|
-
showChildren: @config.show_children,
|
781
|
-
maxTracesToShow: @config.max_traces_to_show,
|
782
|
-
showControls: @config.show_controls,
|
783
|
-
showTotalSqlCount: @config.show_total_sql_count,
|
784
|
-
authorized: true,
|
785
|
-
toggleShortcut: @config.toggle_shortcut,
|
786
|
-
startHidden: @config.start_hidden,
|
787
|
-
collapseResults: @config.collapse_results,
|
788
|
-
htmlContainer: @config.html_container,
|
789
|
-
hiddenCustomFields: @config.snapshot_hidden_custom_fields.join(','),
|
790
|
-
cspNonce: content_security_policy_nonce,
|
791
|
-
hotwireTurboDriveSupport: @config.enable_hotwire_turbo_drive_support,
|
792
|
-
}
|
793
|
-
|
794
|
-
if current && current.page_struct
|
795
|
-
settings[:ids] = ids_comma_separated(env)
|
796
|
-
settings[:currentId] = current.page_struct[:id]
|
797
|
-
else
|
798
|
-
settings[:ids] = []
|
799
|
-
settings[:currentId] = ""
|
800
|
-
end
|
801
|
-
|
802
|
-
# TODO : cache this snippet
|
803
|
-
script = ::File.read(::File.expand_path('html/profile_handler.js', ::File.dirname(__FILE__)))
|
804
|
-
# replace the variables
|
805
|
-
settings.each do |k, v|
|
806
|
-
regex = Regexp.new("\\{#{k.to_s}\\}")
|
807
|
-
script.gsub!(regex, v.to_s)
|
808
|
-
end
|
809
|
-
|
810
|
-
current.inject_js = false if current
|
811
|
-
script
|
812
|
-
end
|
813
|
-
|
814
608
|
# cancels automatic injection of profile script for the current page
|
815
609
|
def cancel_auto_inject(env)
|
816
610
|
current.inject_js = false
|
@@ -822,74 +616,6 @@ module Rack
|
|
822
616
|
|
823
617
|
private
|
824
618
|
|
825
|
-
def handle_snapshots_request(env)
|
826
|
-
self.current = nil
|
827
|
-
MiniProfiler.authorize_request
|
828
|
-
status = 200
|
829
|
-
headers = { 'Content-Type' => 'text/html' }
|
830
|
-
qp = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
831
|
-
if group_name = qp["group_name"]
|
832
|
-
list = @storage.snapshots_group(group_name)
|
833
|
-
list.each do |snapshot|
|
834
|
-
snapshot[:url] = url_for_snapshot(snapshot[:id], group_name)
|
835
|
-
end
|
836
|
-
data = {
|
837
|
-
group_name: group_name,
|
838
|
-
list: list
|
839
|
-
}
|
840
|
-
else
|
841
|
-
list = @storage.snapshots_overview
|
842
|
-
list.each do |group|
|
843
|
-
group[:url] = url_for_snapshots_group(group[:name])
|
844
|
-
end
|
845
|
-
data = {
|
846
|
-
page: "overview",
|
847
|
-
list: list
|
848
|
-
}
|
849
|
-
end
|
850
|
-
data_html = <<~HTML
|
851
|
-
<div style="display: none;" id="snapshots-data">
|
852
|
-
#{data.to_json}
|
853
|
-
</div>
|
854
|
-
HTML
|
855
|
-
response = Rack::Response.new([], status, headers)
|
856
|
-
|
857
|
-
response.write <<~HTML
|
858
|
-
<!DOCTYPE html>
|
859
|
-
<html>
|
860
|
-
<head>
|
861
|
-
<title>Rack::MiniProfiler Snapshots</title>
|
862
|
-
</head>
|
863
|
-
<body class="mp-snapshots">
|
864
|
-
HTML
|
865
|
-
response.write(data_html)
|
866
|
-
script = self.get_profile_script(env)
|
867
|
-
response.write(script)
|
868
|
-
response.write <<~HTML
|
869
|
-
</body>
|
870
|
-
</html>
|
871
|
-
HTML
|
872
|
-
response.finish
|
873
|
-
end
|
874
|
-
|
875
|
-
def serve_flamegraph(env)
|
876
|
-
request = Rack::Request.new(env)
|
877
|
-
id = request.params['id']
|
878
|
-
page_struct = @storage.load(id)
|
879
|
-
|
880
|
-
if !page_struct
|
881
|
-
id = ERB::Util.html_escape(id)
|
882
|
-
user_info = ERB::Util.html_escape(user(env))
|
883
|
-
return [404, {}, ["Request not found: #{id} - user #{user_info}"]]
|
884
|
-
end
|
885
|
-
|
886
|
-
if !page_struct[:flamegraph]
|
887
|
-
return [404, {}, ["No flamegraph available for #{ERB::Util.html_escape(id)}"]]
|
888
|
-
end
|
889
|
-
|
890
|
-
self.flamegraph(page_struct[:flamegraph], page_struct[:request_path])
|
891
|
-
end
|
892
|
-
|
893
619
|
def rails_route_from_path(path, method)
|
894
620
|
if defined?(Rails) && defined?(ActionController::RoutingError)
|
895
621
|
hash = Rails.application.routes.recognize_path(path, method: method)
|
@@ -901,16 +627,6 @@ module Rack
|
|
901
627
|
nil
|
902
628
|
end
|
903
629
|
|
904
|
-
def url_for_snapshots_group(group_name)
|
905
|
-
qs = Rack::Utils.build_query({ group_name: group_name })
|
906
|
-
"/#{@config.base_url_path.gsub('/', '')}/snapshots?#{qs}"
|
907
|
-
end
|
908
|
-
|
909
|
-
def url_for_snapshot(id, group_name)
|
910
|
-
qs = Rack::Utils.build_query({ id: id, group: group_name })
|
911
|
-
"/#{@config.base_url_path.gsub('/', '')}/results?#{qs}"
|
912
|
-
end
|
913
|
-
|
914
630
|
def take_snapshot?(path)
|
915
631
|
@config.snapshot_every_n_requests > 0 &&
|
916
632
|
!path.start_with?(@config.base_url_path) &&
|
@@ -75,7 +75,8 @@ module Rack::MiniProfilerRails
|
|
75
75
|
next if !should_measure?
|
76
76
|
|
77
77
|
current = Rack::MiniProfiler.current
|
78
|
-
|
78
|
+
controller_name = payload[:controller].sub(/Controller\z/, '').downcase
|
79
|
+
description = "Executing: #{controller_name}##{payload[:action]}"
|
79
80
|
Thread.current[get_key(payload)] = current.current_timer
|
80
81
|
Rack::MiniProfiler.current.current_timer = current.current_timer.add_child(description)
|
81
82
|
end
|
@@ -113,6 +114,16 @@ module Rack::MiniProfilerRails
|
|
113
114
|
Rack::MiniProfiler.binds_to_params(payload[:binds])
|
114
115
|
)
|
115
116
|
end
|
117
|
+
|
118
|
+
subscribe("instantiation.active_record") do |name, start, finish, id, payload|
|
119
|
+
next if !should_measure?
|
120
|
+
|
121
|
+
Rack::MiniProfiler.report_reader_duration(
|
122
|
+
(finish - start) * 1000,
|
123
|
+
payload[:record_count],
|
124
|
+
payload[:class_name]
|
125
|
+
)
|
126
|
+
end
|
116
127
|
end
|
117
128
|
end
|
118
129
|
@already_initialized = true
|
@@ -52,7 +52,7 @@ module Rack::MiniProfilerRailsMethods
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def get_webpacker_assets_path
|
55
|
-
if defined?(Webpacker) && Webpacker.config
|
55
|
+
if defined?(Webpacker) && Webpacker.try(:config)&.config_path&.exist?
|
56
56
|
Webpacker.config.public_output_path.to_s.gsub(Webpacker.config.public_path.to_s, "")
|
57
57
|
end
|
58
58
|
end
|
data/lib/patches/sql_patches.rb
CHANGED
@@ -30,8 +30,8 @@ class SqlPatches
|
|
30
30
|
def self.sql_patches
|
31
31
|
patches = []
|
32
32
|
|
33
|
-
patches << 'mysql2' if defined?(Mysql2::Client) && Mysql2::Client.class == Class
|
34
|
-
patches << 'pg' if defined?(PG::Result) && PG::Result.class == Class
|
33
|
+
patches << 'mysql2' if defined?(Mysql2::Client) && Mysql2::Client.class == Class && patch_rails?
|
34
|
+
patches << 'pg' if defined?(PG::Result) && PG::Result.class == Class && patch_rails?
|
35
35
|
patches << 'oracle_enhanced' if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) && ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class == Class &&
|
36
36
|
SqlPatches.correct_version?('~> 1.5.0', ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) &&
|
37
37
|
patch_rails?
|
data/rack-mini-profiler.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
"CHANGELOG.md"
|
22
22
|
]
|
23
23
|
s.add_runtime_dependency 'rack', '>= 1.2.0'
|
24
|
-
s.required_ruby_version = '>= 2.
|
24
|
+
s.required_ruby_version = '>= 2.7.0'
|
25
25
|
|
26
26
|
s.metadata = {
|
27
27
|
'source_code_uri' => Rack::MiniProfiler::SOURCE_CODE_URI,
|