debugbar 0.3.2 → 0.4.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 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)