debugbar 0.0.1 → 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5380fa3760a1309aacd1404c077e3e6c78ed94d21bde373eb0f96f90da96abe6
4
+ data.tar.gz: 7e9f86653a25e202c9ebf4c64b8d74f5816aaadbee1f2e2d75b46423fa17a8dd
5
+ SHA512:
6
+ metadata.gz: '08f084f414d3520d4f7f287b3ffcde2a1a322b7bc04492bcf80ca0087b7fe1a853db6dac041bafceaa352944d8c09ed8d12ecf47bfa30cbc03643f29e2573179'
7
+ data.tar.gz: 2836203deb352f9d344d8784423b801d6ca6ff26e7e48adaec4ea58b142434999ef145f32207de68ba80cf499dba137e8f41d507d97bb9f025d84084a205ac65
data/.prettierrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "trailingComma": "es5",
3
+ "tabWidth": 2,
4
+ "printWidth": 120,
5
+ "semi": false
6
+ }
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Julien Bourdeau
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Debugbar for Rails
2
+
3
+ Find all documentation on [debugbar.dev](https://debugbar.dev/)
4
+
5
+ [![debugbar screesnhot](https://debugbar.dev/assets/screenshots/home.png)](https://debugbar.dev/docs/)
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,26 @@
1
+ module Debugbar
2
+ class DebugbarChannel < ActionCable::Channel::Base
3
+ def subscribed
4
+ stream_from "debugbar_channel"
5
+ end
6
+
7
+ def receive(data)
8
+ if data["clear"]
9
+ RequestBuffer.clear!
10
+ end
11
+
12
+ if data["ids"].present?
13
+ RequestBuffer.remove(data["ids"])
14
+ end
15
+
16
+ Debugbar.connect!
17
+
18
+ data = RequestBuffer.all.map(&:to_h)
19
+ ActionCable.server.broadcast("debugbar_channel", data)
20
+ end
21
+
22
+ def unsubscribed
23
+ Debugbar.disconnect!
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,4 @@
1
+ module Debugbar
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,15 @@
1
+ module Debugbar
2
+ class AssetsController < ApplicationController
3
+ def js
4
+ render file: File.join(Gem.loaded_specs['debugbar'].full_gem_path, 'public', 'debugbar.js'),
5
+ layout: false,
6
+ content_type: 'text/javascript'
7
+ end
8
+
9
+ private
10
+
11
+ def verify_same_origin_request
12
+ true # YOLO
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Debugbar::TagHelpers
2
+ def debugbar_javascript(opt = {})
3
+ html = <<-HTML
4
+ <div id="__debugbar"></div>
5
+ HTML
6
+
7
+ html += <<-HTML
8
+ <script type="text/javascript">
9
+ window._debugbarConfigOptions = #{opt.to_json}
10
+ </script>
11
+ HTML
12
+
13
+ html += <<-HTML
14
+ <script defer src="#{Debugbar.config.prefix}/assets/script"></script>
15
+ HTML
16
+
17
+ raw html
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module Debugbar
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
data/build.sh ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ rm -rf client/dist
4
+ (cd client && npm run build)
5
+ cp client/dist/assets/debugbar-*.js public/debugbar.js
6
+
7
+ gem build debugbar.gemspec
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Debugbar::Engine.routes.draw do
2
+ mount ActionCable.server => '/cable'
3
+
4
+ # TODO: Silence logs for these routes if `::Rails.application.config.assets.quiet` is true
5
+ get 'assets/style' => "assets#css", format: :css
6
+ get 'assets/script' => "assets#js"
7
+ end
data/debugbar.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/debugbar/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "debugbar"
7
+ spec.version = Debugbar::VERSION
8
+ spec.authors = ["Julien Bourdeau"]
9
+ spec.email = ["julien@debugbar.dev"]
10
+
11
+ spec.summary = "Powerful devtools for Ruby on Rails"
12
+ spec.description = "Get a better understanding of your application performance and behavior (SQL queries, jobs, cache, routes, logs, etc)"
13
+ spec.homepage = "https://debugbar.dev"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/julienbourdeau/debugbar"
19
+ spec.metadata["changelog_uri"] = "https://debugbar.dev/changelog/"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ files_to_remove = %w[
24
+ bin/ test/ spec/ features/ fixtures/
25
+ client/
26
+ .git .circleci appveyor Gemfile package.json package-lock.json
27
+ ]
28
+ spec.files = Dir.chdir(__dir__) do
29
+ `git ls-files -z`.split("\x0").reject do |f|
30
+ (File.expand_path(f) == __FILE__) ||
31
+ f.start_with?(*files_to_remove)
32
+ end
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ spec.add_dependency "rails" #, ">= 7.1.1"
39
+ spec.add_dependency "actioncable" #, ">= 7.1.1"
40
+
41
+ # For more information and examples about making a new gem, check out our
42
+ # guide at: https://bundler.io/guides/creating_gem.html
43
+ end
@@ -0,0 +1,34 @@
1
+ module Debugbar
2
+ class MemoryBuffer
3
+ def initialize
4
+ @collection = {}
5
+ end
6
+
7
+ def push(request)
8
+ @collection[request.id] = request
9
+ nil
10
+ end
11
+
12
+ def remove(ids)
13
+ ids = Array.wrap(ids)
14
+ ids.each do |id|
15
+ @collection.delete(id)
16
+ end
17
+ :self
18
+ end
19
+
20
+ def all
21
+ @collection.values
22
+ end
23
+
24
+ def each(&block)
25
+ @collection.each(&block)
26
+ :self
27
+ end
28
+
29
+ def clear!
30
+ @collection = {}
31
+ :self
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ module Debugbar
2
+ class NullBuffer
3
+ def push(_request)
4
+ end
5
+
6
+ def remove(_ids)
7
+ end
8
+
9
+ def all
10
+ end
11
+
12
+ def each
13
+ end
14
+
15
+ def clear!
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ module Debugbar
2
+ class RequestBuffer
3
+ class << self
4
+ def init(adapter)
5
+ @enabled = false
6
+ @adapter = adapter
7
+ end
8
+
9
+ def enable!
10
+ @enabled = true
11
+ send_all
12
+ end
13
+
14
+ def disable!
15
+ @enabled = false
16
+ end
17
+
18
+ def send_all
19
+ data = @collection.values.map(&:to_h)
20
+ ActionCable.server.broadcast("debugbar_channel", data)
21
+ end
22
+
23
+ %w(push each all remove clear!).each do |name|
24
+ define_method(name) do |*args, &block|
25
+ ret = @adapter.send(name, *args, &block)
26
+ ret == :self ? self : ret
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ module Debugbar
2
+ class Config
3
+ attr_accessor :enabled, :prefix, :buffer_adapter, :ignore_request,
4
+ :active_record, :action_controller, :active_job,
5
+ :min_log_level
6
+
7
+ alias_method :enabled?, :enabled
8
+
9
+ def initialize(options = {})
10
+ opt = defaults.merge options
11
+ @enabled = opt[:enabled] && !defined?(Rails::Console)
12
+ @prefix = opt[:prefix]
13
+ @active_record = opt[:active_record]
14
+ @action_controller = opt[:action_controller]
15
+ @active_job = opt[:active_job]
16
+ @min_log_level = opt[:min_log_level]
17
+ @buffer_adapter = opt[:buffer_adapter]
18
+ end
19
+
20
+ def defaults
21
+ {
22
+ enabled: Rails.env.development?,
23
+ prefix: "/_debugbar",
24
+ active_record: true,
25
+ action_controller: true,
26
+ active_job: true,
27
+ min_log_level: -1,
28
+ buffer_adapter: :memory,
29
+ }
30
+ end
31
+
32
+ def ignore_request?(env)
33
+ if ignore_request.is_a? Proc
34
+ ignore_request.call(env)
35
+ else
36
+ [Debugbar.config.prefix, "/assets"].any? { |s| env['PATH_INFO'].start_with? s }
37
+ end
38
+ end
39
+
40
+ def use_logger?
41
+ @enabled && @min_log_level >= 0
42
+ end
43
+
44
+ %w(active_record action_controller active_job).each do |name|
45
+ define_method("#{name}?") do
46
+ @enabled && instance_variable_get("@#{name}")
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debugbar
4
+ class Current < ActiveSupport::CurrentAttributes
5
+ attribute :request
6
+ attribute :ignore
7
+
8
+ alias_method :ignore?, :ignore
9
+
10
+ def new_request!(request_id)
11
+ self.request = Request.new(request_id)
12
+ end
13
+
14
+ def pop_request!
15
+ request.tap { self.request = nil }
16
+ end
17
+ end
18
+ end
@@ -1,10 +1,93 @@
1
+ require_relative 'config'
2
+ require_relative 'middlewares/track_current_request'
3
+
1
4
  module Debugbar
2
- class Engine < Rails::Engine
3
- # This should fix rails3 rc, and also work with beta
4
- config.autoload_paths << File.expand_path(File.join(File.dirname(__FILE__), "..", "..")) if config.respond_to? :autoload_paths
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Debugbar
7
+
8
+ initializer 'debugbar.config' do |app|
9
+ app.config.debugbar = ::Debugbar.config
10
+ end
11
+
12
+ initializer 'debugbar.init' do |app|
13
+ adapter = case(app.config.debugbar.buffer_adapter)
14
+ when :memory
15
+ require_relative 'buffers/memory_buffer'
16
+ MemoryBuffer.new
17
+ when :null
18
+ require_relative 'buffers/null_buffer'
19
+ NullBuffer.new
20
+ else
21
+ throw "Invalid RequestBuffer adapter"
22
+ end
23
+
24
+ Debugbar::RequestBuffer.init(adapter)
25
+ end
26
+
27
+ initializer 'debugbar.helper' do
28
+ ActiveSupport.on_load(:action_controller) do
29
+ ActionController::Base.helper(Debugbar::TagHelpers)
30
+ end
31
+
32
+ ActiveSupport.on_load(:action_view) do
33
+ include Debugbar::TagHelpers
34
+ end
35
+ end
36
+
37
+ initializer 'debugbar.inject_middlewares' do |app|
38
+ next unless Debugbar.config.enabled?
39
+ app.middleware.insert_after ActionDispatch::RequestId, Debugbar::TrackCurrentRequest
40
+ end
41
+
42
+ initializer 'debugbar.subscribe' do
43
+ if Debugbar.config.active_record?
44
+ require_relative 'subscribers/active_record'
45
+ subscribe "Debugbar::ActiveRecordEventSubscriber" => "sql.active_record"
46
+ end
47
+
48
+ if Debugbar.config.action_controller?
49
+ require_relative 'subscribers/action_controller'
50
+ subscribe "Debugbar::ActionControllerEventSubscriber" => %w[
51
+ start_processing.action_controller process_action.action_controller
52
+ ]
53
+ end
54
+
55
+ if Debugbar.config.active_job?
56
+ require_relative 'subscribers/active_job'
57
+ subscribe "Debugbar::ActiveJobEventSubscriber" => ["enqueue.active_job", "enqueue_at.active_job"]
58
+ end
59
+
60
+ require_relative 'subscribers/active_support'
61
+ subscribe "Debugbar::ActiveSupportEventSubscriber.cache" => %w(
62
+ cache_read.active_support
63
+ cache_generate.active_support
64
+ cache_fetch_hit.active_support
65
+ cache_write.active_support
66
+ cache_delete.active_support
67
+ cache_exist?.active_support
68
+ )
69
+ end
70
+
71
+ initializer 'debugbar.track_models' do
72
+ next unless Debugbar.config.active_record?
73
+ ActiveSupport.on_load(:active_record) do
74
+ after_initialize do |model|
75
+ Debugbar::Tracker.inc_model(model.class.name)
76
+ end
77
+ end
78
+ end
5
79
 
6
- rake_tasks do
7
- load "debugbar/railties/tasks.rake"
80
+ def subscribe(config)
81
+ config.each do |subscriber, event_names|
82
+ event_names = Array.wrap(event_names)
83
+ class_name, class_method_name = subscriber.split('.')
84
+ event_names.each do |name|
85
+ method_name = class_method_name || name.split('.').first
86
+ ActiveSupport::Notifications.subscribe name do |event|
87
+ class_name.constantize.send method_name, event
88
+ end
89
+ end
90
+ end
8
91
  end
9
92
  end
10
93
  end
@@ -0,0 +1,34 @@
1
+ module Debugbar
2
+ class SimpleLogger < ::Logger
3
+ def initialize(min_level= 2)
4
+ @min_level = min_level
5
+ end
6
+
7
+ def add(severity, message = nil, progname = nil)
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
+ return if severity < @min_level
15
+
16
+ if message.nil?
17
+ if block_given?
18
+ message = yield
19
+ else
20
+ message = progname
21
+ progname = nil
22
+ end
23
+ end
24
+
25
+ Debugbar::Tracker.add_log({
26
+ time: Time.now.strftime(Debugbar::TIME_FORMAT),
27
+ severity: severity,
28
+ severity_label: SEV_LABEL[severity],
29
+ message: message,
30
+ progname: progname,
31
+ })
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ module Debugbar
2
+ class TrackCurrentRequest
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ Debugbar::Current.ignore = Debugbar.config.ignore_request?(env)
9
+
10
+ return @app.call(env) if Debugbar::Current.ignore?
11
+
12
+ Debugbar::Current.new_request!(env['action_dispatch.request_id'])
13
+
14
+ res = @app.call(env)
15
+
16
+ # TODO: Remove this if statement?
17
+ if Debugbar::Current.request&.id
18
+ # filename = "#{Time.now.to_i}--#{Debugbar::Current.request.meta.dig(:params, :controller)}_#{Debugbar::Current.request.meta.dig(:params, :action).gsub('/', '_')}.json"
19
+ # File.open(Rails.root.join('_requests', filename), "w") do |f|
20
+ # f.write(Debugbar::Current.request.to_json)
21
+ # end
22
+
23
+ RequestBuffer.push(Debugbar::Current.pop_request!)
24
+
25
+ if Debugbar.connected?
26
+ data = RequestBuffer.all.map(&:to_h)
27
+ ActionCable.server.broadcast("debugbar_channel", data)
28
+ end
29
+ end
30
+
31
+ res
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,98 @@
1
+ module Debugbar
2
+ class Request
3
+ attr_reader :request_id, :meta,
4
+ :models, :queries, :jobs,
5
+ :messages, :cache, :logs
6
+ attr_accessor :request, :response, :headers
7
+
8
+ def initialize(request_id)
9
+ @request_id = request_id
10
+ @models = {}
11
+ @queries = []
12
+ @jobs = []
13
+ @messages = []
14
+ @cache = []
15
+ @logs = []
16
+ end
17
+
18
+ alias_method :id, :request_id
19
+
20
+ def meta=(meta)
21
+ meta.delete(:headers)
22
+ meta.delete(:request)
23
+ meta.delete(:response)
24
+ @meta = meta
25
+ end
26
+
27
+ def inc_model(name)
28
+ if @models[name]
29
+ @models[name] += 1
30
+ else
31
+ @models[name] = 1
32
+ end
33
+ end
34
+
35
+ def add_query(query)
36
+ @queries << query
37
+ end
38
+
39
+ def add_job(job)
40
+ @jobs << job
41
+ end
42
+
43
+ def add_msg(msg, extra)
44
+ @messages << {msg: msg, extra: extra}
45
+ end
46
+
47
+ def add_cache(c)
48
+ @cache << c
49
+ end
50
+
51
+ def add_log(l)
52
+ @logs << l
53
+ end
54
+
55
+ def to_h
56
+ {
57
+ id: request_id,
58
+ meta: meta,
59
+ request: request_hash,
60
+ response: response_hash,
61
+ models: models,
62
+ queries: queries,
63
+ jobs: jobs,
64
+ messages: messages,
65
+ cache: cache,
66
+ logs: logs,
67
+ }
68
+ end
69
+
70
+ def to_json
71
+ JSON.pretty_generate(to_h)
72
+ 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
+ end
98
+ end
@@ -0,0 +1,33 @@
1
+ module Debugbar
2
+ class ActionControllerEventSubscriber
3
+ class << self
4
+ def start_processing(event)
5
+ return if Debugbar::Current.ignore?
6
+
7
+ Debugbar::Tracker.request = event.payload[:request]
8
+ end
9
+
10
+ def process_action(event)
11
+ return if Debugbar::Current.ignore?
12
+
13
+ meta = event.payload
14
+
15
+ meta.delete :headers
16
+ request = meta.delete :request
17
+ response = meta.delete :response
18
+
19
+ meta.merge!({
20
+ duration: event.duration,
21
+ cpu_time: event.cpu_time,
22
+ idle_time: event.idle_time,
23
+ allocations: event.allocations,
24
+ })
25
+
26
+ Debugbar::Tracker.request = request
27
+ Debugbar::Tracker.response = response
28
+ Debugbar::Tracker.meta = meta
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debugbar
4
+ class ActiveJobEventSubscriber
5
+ class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
6
+
7
+ class << self
8
+ def enqueue(event)
9
+ return if Debugbar::Current.ignore?
10
+
11
+ job = event.payload[:job]
12
+ ex = event.payload[:exception_object] || job.enqueue_error
13
+
14
+ logs = []
15
+ logs << if ex
16
+ "Failed enqueuing #{job.class.name} to #{queue_name(event)}: #{ex.class} (#{ex.message})"
17
+ elsif event.payload[:aborted]
18
+ "Failed enqueuing #{job.class.name} to #{queue_name(event)}, a before_enqueue callback halted the enqueuing execution."
19
+ else
20
+ "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)}" + args_info(job)
21
+ end
22
+ logs << log_enqueue_source
23
+
24
+ Debugbar::Tracker.add_job({
25
+ id: job.job_id,
26
+ class: job.class.name,
27
+ queue: queue_name(event),
28
+ args: job.arguments.map { |arg| format(arg) },
29
+ successfully_enqueued: job.successfully_enqueued?,
30
+ scheduled_at: scheduled_at(job),
31
+ logs: logs,
32
+ })
33
+ end
34
+ # subscribe_log_level :enqueue, :info # ???
35
+
36
+ def enqueue_at(event)
37
+ return if Debugbar::Current.ignore?
38
+
39
+ job = event.payload[:job]
40
+ ex = event.payload[:exception_object] || job.enqueue_error
41
+
42
+ logs = []
43
+ logs << if ex
44
+ "Failed enqueuing #{job.class.name} to #{queue_name(event)}: #{ex.class} (#{ex.message})"
45
+ elsif event.payload[:aborted]
46
+ "Failed enqueuing #{job.class.name} to #{queue_name(event)}, a before_enqueue callback halted the enqueuing execution."
47
+ else
48
+ "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(job)}" + args_info(job)
49
+ end
50
+ logs << log_enqueue_source
51
+
52
+ Debugbar::Tracker.add_job({
53
+ id: job.job_id,
54
+ class: job.class.name,
55
+ queue: queue_name(event),
56
+ args: job.arguments.map { |arg| format(arg) },
57
+ successfully_enqueued: job.successfully_enqueued?,
58
+ scheduled_at: scheduled_at(job),
59
+ logs: logs,
60
+ })
61
+ end
62
+ # subscribe_log_level :enqueue_at, :info
63
+
64
+ # def enqueue_all(event)
65
+ # info do
66
+ # jobs = event.payload[:jobs]
67
+ # adapter = event.payload[:adapter]
68
+ # enqueued_count = event.payload[:enqueued_count]
69
+ #
70
+ # if enqueued_count == jobs.size
71
+ # enqueued_jobs_message(adapter, jobs)
72
+ # elsif jobs.any?(&:successfully_enqueued?)
73
+ # enqueued_jobs = jobs.select(&:successfully_enqueued?)
74
+ #
75
+ # failed_enqueue_count = jobs.size - enqueued_count
76
+ # if failed_enqueue_count == 0
77
+ # enqueued_jobs_message(adapter, enqueued_jobs)
78
+ # else
79
+ # "#{enqueued_jobs_message(adapter, enqueued_jobs)}. "\
80
+ # "Failed enqueuing #{failed_enqueue_count} #{'job'.pluralize(failed_enqueue_count)}"
81
+ # end
82
+ # else
83
+ # failed_enqueue_count = jobs.size - enqueued_count
84
+ # "Failed enqueuing #{failed_enqueue_count} #{'job'.pluralize(failed_enqueue_count)} "\
85
+ # "to #{ActiveJob.adapter_name(adapter)}"
86
+ # end
87
+ # end
88
+ # end
89
+ # # subscribe_log_level :enqueue_all, :info
90
+
91
+ private
92
+ def queue_name(event)
93
+ ActiveJob.adapter_name(event.payload[:adapter]) + "(#{event.payload[:job].queue_name})"
94
+ end
95
+
96
+ def args_info(job)
97
+ if job.class.log_arguments? && job.arguments.any?
98
+ " with arguments: " +
99
+ job.arguments.map { |arg| format(arg).inspect }.join(", ")
100
+ else
101
+ ""
102
+ end
103
+ end
104
+
105
+ def format(arg)
106
+ case arg
107
+ when Hash
108
+ arg.transform_values { |value| format(value) }
109
+ when Array
110
+ arg.map { |value| format(value) }
111
+ when GlobalID::Identification
112
+ arg.to_global_id rescue arg
113
+ else
114
+ arg
115
+ end
116
+ end
117
+
118
+ def scheduled_at(job)
119
+ Time.at(job.scheduled_at).utc if job.scheduled_at
120
+ end
121
+
122
+ def logger
123
+ ActiveJob::Base.logger
124
+ end
125
+
126
+ def info(progname = nil, &block)
127
+ return unless super
128
+
129
+ if ActiveJob.verbose_enqueue_logs
130
+ log_enqueue_source
131
+ end
132
+ end
133
+
134
+ def error(progname = nil, &block)
135
+ return unless super
136
+
137
+ if ActiveJob.verbose_enqueue_logs
138
+ log_enqueue_source
139
+ end
140
+ end
141
+
142
+ def log_enqueue_source
143
+ extract_enqueue_source_location(caller)
144
+ end
145
+
146
+ def extract_enqueue_source_location(locations)
147
+ backtrace_cleaner.clean(locations.lazy).first
148
+ end
149
+
150
+ def enqueued_jobs_message(adapter, enqueued_jobs)
151
+ enqueued_count = enqueued_jobs.size
152
+ job_classes_counts = enqueued_jobs.map(&:class).tally.sort_by { |_k, v| -v }
153
+ "Enqueued #{enqueued_count} #{'job'.pluralize(enqueued_count)} to #{ActiveJob.adapter_name(adapter)}"\
154
+ " (#{job_classes_counts.map { |klass, count| "#{count} #{klass}" }.join(', ')})"
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debugbar
4
+ class ActiveRecordEventSubscriber
5
+ class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
6
+
7
+ class << self
8
+ def sql(event)
9
+ return if Debugbar::Current.ignore?
10
+
11
+ payload = event.payload
12
+
13
+ return if payload[:name]&.starts_with? "SCHEMA"
14
+
15
+ title = if payload[:async]
16
+ "ASYNC #{payload[:name]} (#{payload[:lock_wait].round(1)}ms) (db time #{event.duration.round(1)}ms)"
17
+ else
18
+ "#{payload[:name] || "Unnamed"} (#{event.duration.round(1)}ms)"
19
+ end
20
+ title = "CACHE #{title}" if payload[:cached]
21
+
22
+ sql = payload[:sql]&.gsub(/\/\*.*\*\//, "") # remove comments
23
+
24
+ binds = nil
25
+ if payload[:binds]&.any?
26
+ casted_params = type_casted_binds(payload[:type_casted_binds])
27
+
28
+ binds = []
29
+ payload[:binds].each_with_index do |attr, i|
30
+ attribute_name = if attr.respond_to?(:name)
31
+ attr.name
32
+ elsif attr.respond_to?(:[]) && attr[i].respond_to?(:name)
33
+ attr[i].name
34
+ else
35
+ nil
36
+ end
37
+
38
+ filtered_params = filter(attribute_name, casted_params[i])
39
+
40
+ binds << render_bind(attr, filtered_params)
41
+ end
42
+ binds = binds.inspect
43
+ binds.prepend(" ")
44
+ end
45
+
46
+ Debugbar::Tracker.add_query({
47
+ id: event.transaction_id,
48
+ title: title,
49
+ name: payload[:name],
50
+ sql: sql,
51
+ cached: payload[:cached].present?,
52
+ async: payload[:async],
53
+ duration: event.duration.round(1),
54
+ lock_wait: payload[:lock_wait]&.round(1),
55
+ binds: binds,
56
+ source: query_source_location&.split(":in"),
57
+ })
58
+ end
59
+
60
+ def query_source_location
61
+ backtrace_cleaner.clean(caller(3))[0]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,30 @@
1
+ module Debugbar
2
+ class ActiveSupportEventSubscriber
3
+ class << self
4
+ def cache(event)
5
+ evt = event.payload
6
+ evt[:name] = event.name
7
+ evt[:transaction_id] = event.transaction_id
8
+ evt[:time] = Time.at(event.time).strftime(Debugbar::TIME_FORMAT)
9
+ evt[:label] = case event.name
10
+ when "cache_read.active_support"
11
+ "read"
12
+ when "cache_generate.active_support"
13
+ "generate"
14
+ when "cache_fetch_hit.active_support"
15
+ "fetch hit"
16
+ when "cache_write.active_support"
17
+ "write"
18
+ when "cache_delete.active_support"
19
+ "delete"
20
+ when "cache_exist?.active_support"
21
+ "exist?"
22
+ else
23
+ "unknown"
24
+ end
25
+
26
+ Debugbar::Tracker.add_cache evt
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debugbar
4
+ VERSION = "0.1.1"
5
+ end
data/lib/debugbar.rb CHANGED
@@ -1,8 +1,82 @@
1
- require 'pathname'
2
- require 'debugbar/engine' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
1
+ # frozen_string_literal: true
3
2
 
4
- module DebugBar
5
- def self.root
6
- Pathname.new(File.join(File.dirname(__FILE__), ".."))
3
+ module Debugbar
4
+ autoload :VERSION, "debugbar/version"
5
+ autoload :Current, "debugbar/current"
6
+ autoload :Request, "debugbar/request"
7
+ autoload :RequestBuffer, "debugbar/buffers/request_buffer"
8
+ autoload :SimpleLogger, "debugbar/loggers/simple_logger"
9
+
10
+ TIME_FORMAT = "%H:%M:%S.%L"
11
+
12
+ module Tracker
13
+ class << self
14
+ SETTERS = %i[request response headers meta].freeze
15
+ METHODS = %i[inc_model add_query add_job add_cache add_log].freeze
16
+
17
+ SETTERS.each do |m|
18
+ define_method("#{m}=") do |val|
19
+ if Current.request.nil?
20
+ # TODO: Much, much better logging needed
21
+ puts "The current request is not set yet. Was trying to set #{m}=[#{val.class.name}]."
22
+ else
23
+ Current.request.send("#{m}=", val)
24
+ end
25
+ end
26
+ end
27
+
28
+ METHODS.each do |m|
29
+ define_method(m) do |*args, &block|
30
+ if Current.request
31
+ return Current.request.send(m, *args, &block)
32
+ end
33
+
34
+ if Current.request.nil? && ENV["DEBUGBAR_VERBOSE_MODE"] == "true"
35
+ puts "The current request is not set yet. Was trying to call #{m}(#{args.map{ _1.class.name}.join(',')})."
36
+ pp args
37
+ nil
38
+ end
39
+ end
40
+ end
41
+
42
+ def msg(msg, *extra)
43
+ if Current.request.nil?
44
+ puts "The current request is not set yet. Printing to STDOUT instead."
45
+ puts msg, extra
46
+ else
47
+ Current.request.add_msg(msg, extra)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ include Tracker
54
+
55
+ class << self
56
+ def config
57
+ @config ||= Config.new(enabled: true)
58
+ end
59
+
60
+ def configure
61
+ yield config
62
+ end
63
+
64
+ def connect!
65
+ @connected = true
66
+ end
67
+
68
+ def disconnect!
69
+ @connected = false
70
+ end
71
+
72
+ def connected?
73
+ @connected
74
+ end
75
+
76
+ def msg(msg, *extra)
77
+ Tracker.msg(msg, *extra)
78
+ end
7
79
  end
8
80
  end
81
+
82
+ require 'debugbar/engine'
data/sig/debugbar.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Debugbar
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata CHANGED
@@ -1,59 +1,103 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: debugbar
3
- version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
6
5
  platform: ruby
7
- authors:
8
- - Andy Jeffries
9
- autorequire:
10
- bindir: bin
6
+ authors:
7
+ - Julien Bourdeau
8
+ autorequire:
9
+ bindir: exe
11
10
  cert_chain: []
12
-
13
- date: 2011-03-17 00:00:00 +00:00
14
- default_executable:
15
- dependencies: []
16
-
17
- description: Debugbar gem for Rails 3
18
- email: andy@andyjeffries.co.uk
11
+ date: 2024-02-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: actioncable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Get a better understanding of your application performance and behavior
42
+ (SQL queries, jobs, cache, routes, logs, etc)
43
+ email:
44
+ - julien@debugbar.dev
19
45
  executables: []
20
-
21
46
  extensions: []
22
-
23
- extra_rdoc_files:
24
- - README.textile
25
- files:
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".prettierrc"
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - app/channels/debugbar/debugbar_channel.rb
54
+ - app/controllers/debugbar/application_controller.rb
55
+ - app/controllers/debugbar/assets_controller.rb
56
+ - app/helpers/debugbar/tag_helpers.rb
57
+ - app/models/debugbar/application_record.rb
58
+ - build.sh
59
+ - config/routes.rb
60
+ - debugbar.gemspec
26
61
  - lib/debugbar.rb
62
+ - lib/debugbar/buffers/memory_buffer.rb
63
+ - lib/debugbar/buffers/null_buffer.rb
64
+ - lib/debugbar/buffers/request_buffer.rb
65
+ - lib/debugbar/config.rb
66
+ - lib/debugbar/current.rb
27
67
  - lib/debugbar/engine.rb
28
- - lib/debugbar/railties/tasks.rake
29
- - README.textile
30
- has_rdoc: true
31
- homepage: http://andyjeffries.co.uk/
32
- licenses: []
33
-
34
- post_install_message:
35
- rdoc_options:
36
- - --charset=UTF-8
37
- require_paths:
68
+ - lib/debugbar/loggers/simple_logger.rb
69
+ - lib/debugbar/middlewares/track_current_request.rb
70
+ - lib/debugbar/request.rb
71
+ - lib/debugbar/subscribers/action_controller.rb
72
+ - lib/debugbar/subscribers/active_job.rb
73
+ - lib/debugbar/subscribers/active_record.rb
74
+ - lib/debugbar/subscribers/active_support.rb
75
+ - lib/debugbar/version.rb
76
+ - sig/debugbar.rbs
77
+ homepage: https://debugbar.dev
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ homepage_uri: https://debugbar.dev
82
+ source_code_uri: https://github.com/julienbourdeau/debugbar
83
+ changelog_uri: https://debugbar.dev/changelog/
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
38
87
  - lib
39
- required_ruby_version: !ruby/object:Gem::Requirement
40
- none: false
41
- requirements:
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
42
90
  - - ">="
43
- - !ruby/object:Gem::Version
44
- version: "0"
45
- required_rubygems_version: !ruby/object:Gem::Requirement
46
- none: false
47
- requirements:
91
+ - !ruby/object:Gem::Version
92
+ version: 2.6.0
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
48
95
  - - ">="
49
- - !ruby/object:Gem::Version
50
- version: "0"
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
51
98
  requirements: []
52
-
53
- rubyforge_project:
54
- rubygems_version: 1.6.1
55
- signing_key:
56
- specification_version: 3
57
- summary: Debugbar gem for Rails 3
99
+ rubygems_version: 3.4.20
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Powerful devtools for Ruby on Rails
58
103
  test_files: []
59
-
data/README.textile DELETED
@@ -1,18 +0,0 @@
1
- h1. Debug Bar Gem v0.0.1
2
-
3
- h2. Installation
4
-
5
- Add the following to your Gemfile:
6
-
7
- <pre><code>gem 'debugbar'</code></pre>
8
-
9
- And restart your application with:
10
-
11
- <pre><code>bundle install
12
- touch tmp/restart.txt</code></pre>
13
-
14
- h2. Introduction
15
-
16
- DebugBar provides a Django/Symfony like Debug Bar for Ruby on Rails 3.
17
-
18
- This project is only in it's infancy...
@@ -1 +0,0 @@
1
- # Put your custom rake tasks here