debugbar 0.3.2 → 0.4.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: 5fb345ca3b935b2a6baee536310271a5cda8e15132e7a79413fb661e44c0dbef
4
- data.tar.gz: 292867bd66a2a1e7348fca7451ef85e2bd756d187cefff23065c349fd90186f7
3
+ metadata.gz: 6aba562e1aed30badc070345aa43832a4446332e7106ec29fd8cb3b8ceffdcba
4
+ data.tar.gz: 5bdc9a1ffe211db11e665f5fa07eb0a526da894b529ba091b78195e2dc6bafdf
5
5
  SHA512:
6
- metadata.gz: 6f0b963c955ca99ff48e9a281a43d3de900b552ed4b16de947529e106c00b5f65ff4cca955b62a521be56a9e1668ce9e6081ce364e57a8137a4dd466dfe087d2
7
- data.tar.gz: 609c0a2137bf2f0517e362cbe8771ac4c7f4fef211b200c51621b7af2555a67b71e76967b1c45bf59c3c6c8639b6f255d13cfff341cbb048e83d885d7a8cecca
6
+ metadata.gz: bce8023b7873a7d6975e7da731dbcdc67b29b1333d5995988c50b209ae0616e23dafa2201d6854fd5298fa738b85e34a89a4f7ee2439f25863dcb24a95061c02
7
+ data.tar.gz: 47cf338f0bad77e1fa45e6d85f26cc25456adf592d10ee9ea8affe427d7c9a5fb0b74cd39b3f1e367070f43736ed14cfa7afd3ea24b36355255be258d5140327
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## UNRELEASED
4
+
5
+ * Use the debugbar in the browser dev tools 😎
6
+
7
+
8
+ ## v0.4.0 - 2025-01-07
9
+
10
+ * Track HTTP calls made by the app - See [#48](https://github.com/julienbourdeau/debugbar/pull/48)
11
+ * Learn more: https://debugbar.dev/docs/http
12
+ * Handle multi-processes service (cluster mode in Puma) - See [#45](https://github.com/julienbourdeau/debugbar/pull/45)
13
+ * Learn more: https://debugbar.dev/docs/puma-configuration
14
+ * Ignore OPTIONS requests - See [08e7ee06](https://github.com/julienbourdeau/debugbar/commit/08e7ee0665f18f419a9a9bbcea00c414d05c2084)
15
+ * ⚠️ Automatically set `action_cable.disable_request_forgery_protection` to true - See [449fc496](https://github.com/julienbourdeau/debugbar/commit/449fc49691182ecedf7cbbe1e5ca276894a70fee)
16
+ * Some design tweak (like the select input to choose the request)
17
+
18
+ ## v0.3.3 - 2024-06-15
19
+
20
+ * Allow nonce to be set for content security policies - See [#38](https://github.com/julienbourdeau/debugbar/pull/38)
21
+
3
22
  ## v0.3.2 - 2024-05-04
4
23
 
5
24
  * Improved Debug panel (to be in par with Queries panel) - See [#36](https://github.com/julienbourdeau/debugbar/pull/36)
@@ -13,14 +13,11 @@ module Debugbar
13
13
  RequestBuffer.remove(data["ids"])
14
14
  end
15
15
 
16
- Debugbar.connect!
17
-
18
- data = RequestBuffer.all.map(&:to_h)
19
- ActionCable.server.broadcast("debugbar_channel", data)
16
+ ActionCable.server.broadcast("debugbar_channel", RequestBuffer.to_h)
20
17
  end
21
18
 
22
19
  def unsubscribed
23
- Debugbar.disconnect!
20
+ # Nothing to do
24
21
  end
25
22
  end
26
23
  end
@@ -4,7 +4,7 @@ module Debugbar
4
4
  before_action :cors_set_access_control_headers
5
5
 
6
6
  def poll
7
- render json: RequestBuffer.all.map(&:to_h)
7
+ render json: RequestBuffer.to_h
8
8
  end
9
9
 
10
10
  def confirm
@@ -22,7 +22,7 @@ module Debugbar::TagHelpers
22
22
  HTML
23
23
 
24
24
  html += <<-HTML
25
- <script type="text/javascript" data-turbo-permanent>
25
+ <script type="text/javascript" data-turbo-permanent nonce="#{opt.delete(:nonce)}">
26
26
  window._debugbarConfigOptions = #{opt.to_json}
27
27
  </script>
28
28
  HTML
data/build_client.sh CHANGED
@@ -2,4 +2,7 @@
2
2
 
3
3
  rm -rf client/dist
4
4
  (cd client && npm run build)
5
+
6
+ echo
7
+ echo " -> Copying new assets version to public/debugbar.js"
5
8
  cp client/dist/assets/debugbar-*.js public/debugbar.js
data/build_demo.sh CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/bin/bash
2
2
 
3
3
  ruby ./build_fixtures.rb
4
+
4
5
  rm -rf client/dist-demo
5
- (cd client/ && VITE_DEMO_MODE=true npm run build)
6
+ (cd client/ && npm run build:demo)
6
7
 
7
8
  if [ -d "../debugbar.dev/source/assets/debugbar" ]; then
8
9
  echo
@@ -10,4 +11,8 @@ if [ -d "../debugbar.dev/source/assets/debugbar" ]; then
10
11
  echo " -> Copying assets"
11
12
  rm -f ../debugbar.dev/source/assets/debugbar/*
12
13
  cp ./client/dist-demo/assets/* ../debugbar.dev/source/assets/debugbar/
14
+ else
15
+ echo "####################################################"
16
+ echo "## debugbar.dev was NOT FOUND in parent directory ##"
17
+ echo "####################################################"
13
18
  fi
data/build_fixtures.rb CHANGED
@@ -8,7 +8,7 @@ Dir.glob("#{fixtures_dir}/*").each { |p| File.delete(p) }
8
8
 
9
9
  Net::HTTP.start('127.0.0.1', 3000) do |http|
10
10
  [
11
- '/post-list',
11
+ '/post-list?with-external-data=true',
12
12
  '/slow-page',
13
13
  '/random',
14
14
  '/post/240',
@@ -29,4 +29,3 @@ Net::HTTP.start('127.0.0.1', 3000) do |http|
29
29
  File.write("#{fixtures_dir}/#{name}.json", JSON.pretty_generate(item))
30
30
  end
31
31
  end
32
-
@@ -0,0 +1,42 @@
1
+ module Debugbar
2
+ class CacheBuffer
3
+ def push(request)
4
+ collection = get_collection
5
+ collection[request.id] = request.to_h
6
+ set_collection(collection)
7
+ end
8
+
9
+ def remove(ids)
10
+ ids = Array.wrap(ids)
11
+
12
+ collection = get_collection
13
+ ids.each do |id|
14
+ collection.delete(id)
15
+ end
16
+ set_collection(collection)
17
+ end
18
+
19
+ def to_h
20
+ get_collection.values.map(&:to_h)
21
+ end
22
+
23
+ def each(&block)
24
+ get_collection.each(&block)
25
+
26
+ end
27
+
28
+ def clear!
29
+ Rails.cache.delete("debugbar-requests")
30
+ end
31
+
32
+ private
33
+
34
+ def get_collection
35
+ Rails.cache.fetch("debugbar-requests") { {} }
36
+ end
37
+
38
+ def set_collection(collection)
39
+ Rails.cache.write("debugbar-requests", collection, expires_in: 10.minutes)
40
+ end
41
+ end
42
+ end
@@ -6,7 +6,6 @@ module Debugbar
6
6
 
7
7
  def push(request)
8
8
  @collection[request.id] = request
9
- nil
10
9
  end
11
10
 
12
11
  def remove(ids)
@@ -14,21 +13,18 @@ module Debugbar
14
13
  ids.each do |id|
15
14
  @collection.delete(id)
16
15
  end
17
- :self
18
16
  end
19
17
 
20
- def all
21
- @collection.values
18
+ def to_h
19
+ @collection.values.map(&:to_h)
22
20
  end
23
21
 
24
22
  def each(&block)
25
23
  @collection.each(&block)
26
- :self
27
24
  end
28
25
 
29
26
  def clear!
30
27
  @collection = {}
31
- :self
32
28
  end
33
29
  end
34
30
  end
@@ -6,7 +6,8 @@ module Debugbar
6
6
  def remove(_ids)
7
7
  end
8
8
 
9
- def all
9
+ def to_h
10
+ {}
10
11
  end
11
12
 
12
13
  def each
@@ -5,11 +5,28 @@ module Debugbar
5
5
  @adapter = adapter
6
6
  end
7
7
 
8
- %w(push each all remove clear!).each do |name|
9
- define_method(name) do |*args, &block|
10
- ret = @adapter.send(name, *args, &block)
11
- ret == :self ? self : ret
12
- end
8
+ def push(request)
9
+ @adapter.push(request)
10
+ nil # Why not return self?
11
+ end
12
+
13
+ def remove(ids)
14
+ @adapter.remove(ids)
15
+ self
16
+ end
17
+
18
+ def to_h
19
+ @adapter.to_h
20
+ end
21
+
22
+ def each(&block)
23
+ @adapter.each(&block)
24
+ self
25
+ end
26
+
27
+ def clear!
28
+ @adapter.clear!
29
+ self
13
30
  end
14
31
  end
15
32
  end
@@ -33,7 +33,9 @@ module Debugbar
33
33
  if ignore_request.is_a? Proc
34
34
  ignore_request.call(env)
35
35
  else
36
- [Debugbar.config.prefix, "/assets"].any? { |s| env['PATH_INFO'].start_with? s }
36
+ [Debugbar.config.prefix, "/assets"].any? do |s|
37
+ env['PATH_INFO'].start_with? s
38
+ end || env['REQUEST_METHOD'] == "OPTIONS"
37
39
  end
38
40
  end
39
41
 
@@ -7,12 +7,70 @@ module Debugbar
7
7
  class Engine < ::Rails::Engine
8
8
  isolate_namespace Debugbar
9
9
 
10
+ def log(msg)
11
+ @logger ||= Logger.new(STDOUT)
12
+ Array.wrap(msg).each { |m| @logger.warn(m) }
13
+ end
14
+
10
15
  initializer 'debugbar.config' do |app|
11
16
  app.config.debugbar = ::Debugbar.config
12
17
  end
13
18
 
19
+ initializer 'debugbar.override_config' do |app|
20
+ unless app.config.action_cable.disable_request_forgery_protection
21
+ app.config.action_cable.disable_request_forgery_protection = true
22
+ log "Debugbar: Action Cable request forgery protection is enabled. This can cause issues with Debugbar. Overriding setting config.action_cable.disable_request_forgery_protection = true now. Update your configuration to get rid of this message."
23
+ end
24
+
25
+ if defined? HttpLog
26
+ HttpLog.configure do |config|
27
+ config.enabled = true
28
+
29
+ config.json_log = true
30
+ config.prefix = ''
31
+
32
+ config.logger = Debugbar::HttpLogger.new
33
+ config.logger_method = :log
34
+
35
+ config.log_connect = true
36
+ config.log_request = true
37
+ config.log_headers = true
38
+ config.log_data = true
39
+ config.log_status = true
40
+ config.log_response = true
41
+ config.log_benchmark = true
42
+
43
+ end
44
+ end
45
+ end
46
+
14
47
  initializer 'debugbar.init' do |app|
48
+ # Display error message if running in multi-process mode without proper configuration
49
+ if ENV["WEB_CONCURRENCY"].to_i > 1
50
+ cache_nok = %i[null_store memory_store].include?(Rails.configuration.cache_store.first.to_sym)
51
+ action_cable_nok = ActionCable.server.config.cable[:adapter].to_s == "async"
52
+ adapter_nok = app.config.debugbar.buffer_adapter != :cache
53
+
54
+ if cache_nok || action_cable_nok || adapter_nok
55
+ msg = [
56
+ "############################################################################################################",
57
+ "# Debugbar: You are using a multi-process server configuration (like Puma in cluster mode)",
58
+ "# Debugbar: You can use puma in single mode by setting the environment variable WEB_CONCURRENCY to 0",
59
+ "# Debugbar: If you want to use multiple processes, you must ensure that the following conditions are met:",
60
+ "# Debugbar: \t Use a persistent cache store (like files or database) in your Application config",
61
+ "# Debugbar: \t Use a persistent adapter for ActionCable (like Redis or SolidCable)",
62
+ "# Debugbar: \t Use the :cache buffer_adapter in config/initializers/debugbar.rb",
63
+ "############################################################################################################",
64
+ ]
65
+
66
+ log msg
67
+ end
68
+ end
69
+
15
70
  adapter = case(app.config.debugbar.buffer_adapter)
71
+ when :cache
72
+ require_relative 'buffers/cache_buffer'
73
+ CacheBuffer.new
16
74
  when :memory
17
75
  require_relative 'buffers/memory_buffer'
18
76
  MemoryBuffer.new
@@ -0,0 +1,44 @@
1
+ module Debugbar
2
+ class HttpRequest
3
+ attr_reader :method, :url, :headers, :body
4
+
5
+ def initialize(method, url, headers, body)
6
+ @method = method
7
+ @url = url
8
+ @headers = headers
9
+ @body = body
10
+ end
11
+
12
+ def self.from_rack(rack_req)
13
+ headers = rack_req.env.select { |k,v| k.start_with? 'HTTP_'} # https://stackoverflow.com/a/55406700/1001125
14
+ .transform_keys { |k| k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') }
15
+ .sort.to_h
16
+
17
+ new(rack_req.method, rack_req.original_url, headers, rack_req.body)
18
+ end
19
+
20
+ def to_h
21
+ { method:, url:, headers:, body: }
22
+ end
23
+ end
24
+
25
+ class HttpResponse
26
+ attr_reader :status, :headers, :body
27
+
28
+ def initialize(status, headers, body)
29
+ @status = status
30
+ @headers = headers
31
+ @body = body
32
+ end
33
+
34
+ def self.from_rack(rack_res)
35
+ headers = rack_res.headers.to_h.transform_keys { |s| s.split('-').map(&:capitalize).join('-') }
36
+
37
+ new(rack_res.status, headers, rack_res.body)
38
+ end
39
+
40
+ def to_h
41
+ { status:, headers:, body: }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ module Debugbar
2
+ class HttpLogger < ::Logger
3
+ def initialize(min_level= 2)
4
+ @min_level = min_level
5
+ end
6
+
7
+ def log(_level, json_str)
8
+ # We normally rely on the Tracker to know if the current request is set or nil and log an error to STDOUT,
9
+ # but in this case, it happens so often that the logs are annoying.
10
+ # In ActiveCable, only the first call goes through the Rake middleware, so every following communication
11
+ # doesn't go through TrackCurrentRequest, which initializes the request.
12
+ return if ::Debugbar::Current.request.nil?
13
+
14
+ log = JSON.parse(json_str)
15
+ req = HttpRequest.new(log["method"], log["url"], log["request_headers"], log["request_body"])
16
+ res = HttpResponse.new(log["response_code"], log["response_headers"], log["response_body"])
17
+ Debugbar::Tracker.add_http_call(SecureRandom.hex(4), req, res, benchmark: log['benchmark'])
18
+ end
19
+ end
20
+ end
@@ -5,34 +5,33 @@ module Debugbar
5
5
  end
6
6
 
7
7
  def call(env)
8
+ # we don't check if debugbar is enabled because this middleware is only added when it is
9
+
10
+ # Set `ignore` attribute to reuse it anywhere
8
11
  Debugbar::Current.ignore = Debugbar.config.ignore_request?(env)
9
12
 
10
13
  return @app.call(env) if Debugbar::Current.ignore?
11
14
 
12
15
  Debugbar::Current.new_request!(SecureRandom.uuid)
13
16
 
14
- res = @app.call(env)
17
+ status, headers, body = @app.call(env)
15
18
 
16
19
  # TODO: Remove this if statement?
17
20
  # We check meta because the frontend doesn't support request without meta yet.
18
21
  # It might happen with ActionController::Live where the following code
19
22
  # will run BEFORE ActionControllerEventSubscriber.process_action is called
20
23
  if Debugbar::Current.request&.meta
21
- # filename = "#{Time.now.to_i}--#{Debugbar::Current.request.meta.dig(:params, :controller)}_#{Debugbar::Current.request.meta.dig(:params, :action).gsub('/', '_')}.json"
22
- # File.open(Rails.root.join('_requests', filename), "w") do |f|
23
- # f.write(Debugbar::Current.request.to_json)
24
- # end
25
-
26
24
  RequestBuffer.push(Debugbar::Current.pop_request!)
27
25
 
28
- # TODO: Refactor has not having ActionCable might more common than I thought
29
- if Debugbar.connected? && defined?(ActionCable)
30
- data = RequestBuffer.all.map(&:to_h)
31
- ActionCable.server.broadcast("debugbar_channel", data)
26
+ # TODO: Refactor since not having ActionCable might be more common than I thought
27
+ if defined?(ActionCable)
28
+ ActionCable.server.broadcast("debugbar_channel", RequestBuffer.to_h)
32
29
  end
33
30
  end
34
31
 
35
- res
32
+ headers["X-Debugbar-On"] = "yes" # Used for the browser extension
33
+
34
+ [status, headers, body]
36
35
  end
37
36
  end
38
37
 
@@ -1,9 +1,9 @@
1
1
  module Debugbar
2
2
  class Request
3
3
  attr_reader :request_id, :meta,
4
+ :request, :response,
4
5
  :models, :queries, :jobs,
5
- :messages, :cache, :logs
6
- attr_accessor :request, :response, :headers
6
+ :messages, :cache, :logs, :http_calls
7
7
 
8
8
  def initialize(request_id)
9
9
  @request_id = request_id
@@ -13,6 +13,7 @@ module Debugbar
13
13
  @messages = []
14
14
  @cache = []
15
15
  @logs = []
16
+ @http_calls = []
16
17
  end
17
18
 
18
19
  alias_method :id, :request_id
@@ -24,6 +25,14 @@ module Debugbar
24
25
  @meta = meta
25
26
  end
26
27
 
28
+ def request=(rack_request)
29
+ @request = HttpRequest.from_rack(rack_request)
30
+ end
31
+
32
+ def response=(rack_response)
33
+ @response = HttpResponse.from_rack(rack_response)
34
+ end
35
+
27
36
  def inc_model(name)
28
37
  if @models[name]
29
38
  @models[name] += 1
@@ -52,47 +61,32 @@ module Debugbar
52
61
  @logs << l
53
62
  end
54
63
 
64
+ def add_http_call(id, request, response, extra)
65
+ @http_calls << {
66
+ id: id,
67
+ request: request.to_h,
68
+ response: response.to_h
69
+ }.merge(extra)
70
+ end
71
+
55
72
  def to_h
56
73
  {
57
74
  id: request_id,
58
75
  meta: meta,
59
- request: request_hash,
60
- response: response_hash,
76
+ request: request.to_h,
77
+ response: response&.to_h,
61
78
  models: models,
62
79
  queries: queries,
63
80
  jobs: jobs,
64
81
  messages: messages,
65
82
  cache: cache,
66
83
  logs: logs,
84
+ http_calls: http_calls,
67
85
  }
68
86
  end
69
87
 
70
88
  def to_json
71
89
  JSON.pretty_generate(to_h)
72
90
  end
73
-
74
- private
75
-
76
- def request_hash
77
- {
78
- method: request.method,
79
- path: request.path,
80
- format: meta.dig(:format),
81
- params: meta.dig(:params),
82
- headers: request.env.select { |k,v| k.start_with? 'HTTP_'} # https://stackoverflow.com/a/55406700/1001125
83
- .transform_keys { |k| k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') }
84
- .sort.to_h
85
- }
86
- end
87
-
88
- def response_hash
89
- return nil if response.nil?
90
-
91
- {
92
- status: response.status,
93
- headers: response.headers.to_h.transform_keys { |s| s.split('-').map(&:capitalize).join('-') },
94
- body: response.body,
95
- }
96
- end
97
91
  end
98
92
  end
@@ -26,7 +26,6 @@ module Debugbar
26
26
  Debugbar::Tracker.request = request
27
27
  Debugbar::Tracker.response = response
28
28
  Debugbar::Tracker.meta = meta
29
-
30
29
  end
31
30
  end
32
31
  end
@@ -12,7 +12,7 @@ module Debugbar
12
12
  when "cache_generate.active_support"
13
13
  "generate"
14
14
  when "cache_fetch_hit.active_support"
15
- "fetch hit"
15
+ "fetchhit"
16
16
  when "cache_write.active_support"
17
17
  "write"
18
18
  when "cache_delete.active_support"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Debugbar
4
- VERSION = "0.3.2"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/debugbar.rb CHANGED
@@ -6,13 +6,16 @@ module Debugbar
6
6
  autoload :Request, "debugbar/request"
7
7
  autoload :RequestBuffer, "debugbar/buffers/request_buffer"
8
8
  autoload :SimpleLogger, "debugbar/loggers/simple_logger"
9
+ autoload :HttpLogger, "debugbar/loggers/http_logger"
10
+ autoload :HttpRequest, "debugbar/http/http"
11
+ autoload :HttpResponse, "debugbar/http/http"
9
12
 
10
13
  TIME_FORMAT = "%H:%M:%S.%L"
11
14
 
12
15
  module Tracker
13
16
  class << self
14
17
  SETTERS = %i[request response headers meta].freeze
15
- METHODS = %i[inc_model add_query add_job add_cache add_log].freeze
18
+ METHODS = %i[inc_model add_query add_job add_cache add_log add_http_call].freeze
16
19
 
17
20
  SETTERS.each do |m|
18
21
  define_method("#{m}=") do |val|
@@ -69,18 +72,6 @@ module Debugbar
69
72
  yield config
70
73
  end
71
74
 
72
- def connect!
73
- @connected = true
74
- end
75
-
76
- def disconnect!
77
- @connected = false
78
- end
79
-
80
- def connected?
81
- @connected
82
- end
83
-
84
75
  def msg(msg, *extra)
85
76
  source = caller.first&.split(":in")&.map { |s| s.delete_prefix("#{Rails.root}/").strip.tr("`'", '' ) }
86
77
  Tracker.msg(msg, extra, source)