callstacking-rails 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +52 -0
- data/Rakefile +8 -0
- data/app/assets/config/checkpoint_rails_manifest.js +1 -0
- data/app/assets/stylesheets/checkpoint/rails/application.css +15 -0
- data/app/controllers/checkpoint/rails/application_controller.rb +6 -0
- data/app/controllers/checkpoint/rails/traces_controller.rb +6 -0
- data/app/helpers/checkpoint/rails/application_helper.rb +6 -0
- data/app/jobs/checkpoint/rails/application_job.rb +6 -0
- data/app/mailers/checkpoint/rails/application_mailer.rb +8 -0
- data/app/models/checkpoint/rails/application_record.rb +7 -0
- data/config/routes.rb +4 -0
- data/lib/checkpoint/rails/client/authenticate.rb +22 -0
- data/lib/checkpoint/rails/client/base.rb +56 -0
- data/lib/checkpoint/rails/client/trace.rb +33 -0
- data/lib/checkpoint/rails/engine.rb +34 -0
- data/lib/checkpoint/rails/env.rb +15 -0
- data/lib/checkpoint/rails/settings.rb +46 -0
- data/lib/checkpoint/rails/setup.rb +93 -0
- data/lib/checkpoint/rails/traceable.rb +210 -0
- data/lib/checkpoint/rails/traces_helper.rb +39 -0
- data/lib/checkpoint/rails/version.rb +5 -0
- data/lib/checkpoint/rails.rb +7 -0
- data/lib/tasks/checkpoint/rails_tasks.rake +4 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f4bc78186fe09232889a12dea985d128878de745f00a7f6761fd2d6bdc1bc6e0
|
4
|
+
data.tar.gz: 113fe92740e5260af527603445de8c0ec5ce49262a332dff1620382bb584dfa3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db7b809625176d70d726b202d11d26d55c73809281af0e569431f9c884ad81ce7868e29fa685a0527a48ffc0ba8fd52cf4e35b4ae0e83bd6cf64684663bc5e31
|
7
|
+
data.tar.gz: 74cf19f86b7ad4da4c67eb1a83af54f51e843d324e067b6d05cf81eede3a1bc14a05903e252ed73359c01d0cf58a35e98940e13688fa2423abd61616ca302c6a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 Jim Jones
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Checkpoint::Rails
|
2
|
+
|
3
|
+
Callstacking is a rolling, checkpoint debugger for Rails. It records all of the critical method calls within your app, along with their important context (param/argument/return/local variable values).
|
4
|
+
|
5
|
+
You no longer need to debug with `binding.pry` or `puts` statements, as the entire callstack for a given request is captured.
|
6
|
+
|
7
|
+
Demo video:
|
8
|
+
[](https://www.youtube.com/watch?v=NGqnwcNWv_k)
|
9
|
+
|
10
|
+
Class method calls are labeled. Return values for those calls are denoted with ↳
|
11
|
+
|
12
|
+
Arguments for a method will be listed along with their calling values.
|
13
|
+
|
14
|
+
For method returns ↳, the final values of the local variables will be listed when you hover over the entry.
|
15
|
+
|
16
|
+
<img width="1695" alt="CleanShot 2022-09-17 at 21 10 32@2x" src="https://user-images.githubusercontent.com/4600/190882603-a99e9358-9754-4cbf-ac68-a41d53afe747.png">
|
17
|
+
|
18
|
+
Subsequent calls within a method are visibly nested.
|
19
|
+
|
20
|
+
Checkpoint is a Rails engine that you mount within your Rails app.
|
21
|
+
|
22
|
+
Here's a sample debugging sessions recorded from a Jumpstart Rails based app I've been working on. This is a request for the main page ( https://smartk.id/ ).
|
23
|
+
|
24
|
+

|
25
|
+
|
26
|
+
Checkpoint Rails records all of the critical method calls within your app, along with their important context (param/argument/return/local variable values).
|
27
|
+
|
28
|
+
All in a rolling panel, so that you can debug your call chains from any point in the stack.
|
29
|
+
|
30
|
+
You'll never have to debug with `puts` statements ever again.
|
31
|
+
|
32
|
+
Calls are visibly nested so that it's easy to see which calls are issued from which parent methods.
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
Add this line to your application's Gemfile:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
gem "callstacking-rails"
|
39
|
+
```
|
40
|
+
|
41
|
+
And then execute:
|
42
|
+
```bash
|
43
|
+
$ bundle
|
44
|
+
```
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
When you open a page for your app, once the page has rendered, you will see an arrow on the right hand side.
|
48
|
+
|
49
|
+
Click the arrow, and observe the full callstack context.
|
50
|
+
|
51
|
+
## License
|
52
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
//= link_directory ../stylesheets/checkpoint/rails .css
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
data/config/routes.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Checkpoint
|
4
|
+
module Rails
|
5
|
+
module Client
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
|
+
class Authenticate < Base
|
9
|
+
URL = "/api/v1/auth.json"
|
10
|
+
|
11
|
+
def login(email, password)
|
12
|
+
resp = post(URL, email: email, password: password)
|
13
|
+
|
14
|
+
raise Faraday::UnauthorizedError if resp&.body.nil?
|
15
|
+
|
16
|
+
body = resp&.body || {}
|
17
|
+
body["token"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday/follow_redirects'
|
3
|
+
require "checkpoint/rails/settings"
|
4
|
+
|
5
|
+
module Checkpoint
|
6
|
+
module Rails
|
7
|
+
module Client
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
class Base
|
11
|
+
include Checkpoint::Rails::Settings
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
read_settings
|
15
|
+
end
|
16
|
+
|
17
|
+
def connection
|
18
|
+
# https://github.com/lostisland/awesome-faraday
|
19
|
+
@connection ||= Faraday.new(url) do |c|
|
20
|
+
c.response :json
|
21
|
+
c.use Faraday::Response::Logger, Logger.new('/tmp/callstacking-rails.log')
|
22
|
+
# c.use Faraday::Response::Logger, nil, { headers: false, bodies: false }
|
23
|
+
c.response :follow_redirects
|
24
|
+
c.use Faraday::Response::RaiseError # raise exceptions on 40x, 50x responses
|
25
|
+
c.request :json # This will set the "Content-Type" header to application/json and call .to_json on the body
|
26
|
+
c.adapter Faraday.default_adapter
|
27
|
+
|
28
|
+
if auth_token?
|
29
|
+
c.request :authorization, :Bearer, auth_token
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get(url, params = {})
|
35
|
+
connection.get(url, params, headers)
|
36
|
+
end
|
37
|
+
|
38
|
+
def post(url, params = {}, body = {}, headers_ext = {})
|
39
|
+
r(:post, url, params, body, headers_ext)
|
40
|
+
end
|
41
|
+
|
42
|
+
def patch(url, params = {}, body = {}, headers_ext = {})
|
43
|
+
r(:patch, url, params, body, headers_ext)
|
44
|
+
end
|
45
|
+
|
46
|
+
def r(action, url, params = {}, body = {}, _headers_ext = {})
|
47
|
+
connection.send(action, url) do |req|
|
48
|
+
req.params.merge!(params)
|
49
|
+
req.body = body
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Checkpoint
|
2
|
+
module Rails
|
3
|
+
module Client
|
4
|
+
class Trace < Base
|
5
|
+
CREATE_URL = "/api/v1/traces.json"
|
6
|
+
UPDATE_URL = "/api/v1/traces/:id.json"
|
7
|
+
|
8
|
+
def create(method_name, klass, action_name, format_name, root_path, url, request_id, headers, params)
|
9
|
+
resp = post(CREATE_URL,
|
10
|
+
{},
|
11
|
+
{ method_name: method_name,
|
12
|
+
klass: klass,
|
13
|
+
action_name: action_name,
|
14
|
+
format_name: format_name,
|
15
|
+
root_path: root_path,
|
16
|
+
url: url,
|
17
|
+
request_id: request_id,
|
18
|
+
h: headers.to_h,
|
19
|
+
p: params.to_h,
|
20
|
+
})
|
21
|
+
|
22
|
+
return resp.body["trace_id"], resp.body["pulse_interval"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def upsert(trace_id, traces)
|
26
|
+
resp = patch(UPDATE_URL.gsub(':id', trace_id), {}, traces)
|
27
|
+
|
28
|
+
resp.status
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "checkpoint/rails/traceable"
|
2
|
+
require "checkpoint/rails/setup"
|
3
|
+
require "checkpoint/rails/settings"
|
4
|
+
require "checkpoint/rails/client/base"
|
5
|
+
require "checkpoint/rails/client/authenticate"
|
6
|
+
require "checkpoint/rails/client/trace"
|
7
|
+
require "checkpoint/rails/traces_helper"
|
8
|
+
|
9
|
+
module Checkpoint
|
10
|
+
module Rails
|
11
|
+
class Engine < ::Rails::Engine
|
12
|
+
isolate_namespace Checkpoint::Rails
|
13
|
+
|
14
|
+
include ::Checkpoint::Rails::Traceable
|
15
|
+
|
16
|
+
initializer "engine_name.assets.precompile" do |app|
|
17
|
+
app.config.assets.precompile << "checkpoint_rails_manifest.js"
|
18
|
+
end
|
19
|
+
|
20
|
+
initializer 'local_helper.action_controller' do
|
21
|
+
ActiveSupport.on_load :action_controller do
|
22
|
+
helper Checkpoint::Rails::TracesHelper
|
23
|
+
include Checkpoint::Rails::TracesHelper
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
initializer :append_before_action do
|
28
|
+
ActionController::Base.send :after_action, :inject_hud
|
29
|
+
end
|
30
|
+
|
31
|
+
set_trace
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Checkpoint
|
2
|
+
module Rails
|
3
|
+
class Env
|
4
|
+
DEFAULT_ENVIRONMENT = "development"
|
5
|
+
|
6
|
+
cattr_accessor :environment
|
7
|
+
|
8
|
+
@@environment = (ENV['RAILS_ENV'] || DEFAULT_ENVIRONMENT).parameterize(separator: '_').to_sym
|
9
|
+
|
10
|
+
def self.production?
|
11
|
+
@@environment == DEFAULT_ENVIRONMENT.parameterize(separator: '_').to_sym
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "active_support/core_ext/class/attribute_accessors"
|
3
|
+
require "checkpoint/rails/env"
|
4
|
+
|
5
|
+
module Checkpoint
|
6
|
+
module Rails
|
7
|
+
module Settings
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do |base|
|
11
|
+
attr_accessor :settings
|
12
|
+
end
|
13
|
+
|
14
|
+
SETTINGS_FILE = "#{Dir.home}/.callstacking-rails"
|
15
|
+
PRODUCTION_URL = "https://callstacking.com"
|
16
|
+
|
17
|
+
def url
|
18
|
+
settings[:url]
|
19
|
+
end
|
20
|
+
|
21
|
+
def auth_token
|
22
|
+
settings[:auth_token]
|
23
|
+
end
|
24
|
+
|
25
|
+
def auth_token?
|
26
|
+
auth_token.present?
|
27
|
+
end
|
28
|
+
|
29
|
+
def write_settings(new_settings)
|
30
|
+
File.write(SETTINGS_FILE, new_settings.to_yaml)
|
31
|
+
end
|
32
|
+
|
33
|
+
def read_settings
|
34
|
+
@@settings = @settings = complete_settings.dig(::Checkpoint::Rails::Env.environment, :settings)
|
35
|
+
rescue StandardError => e
|
36
|
+
puts e.full_message
|
37
|
+
puts e.backtrace.join("\n")
|
38
|
+
return {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def complete_settings
|
42
|
+
YAML.load(File.read(Checkpoint::Rails::Client::Base::SETTINGS_FILE)) rescue {}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require "checkpoint/rails/settings"
|
3
|
+
|
4
|
+
module Checkpoint
|
5
|
+
module Rails
|
6
|
+
class Setup
|
7
|
+
include ::Checkpoint::Rails::Settings
|
8
|
+
extend ::Checkpoint::Rails::Settings
|
9
|
+
|
10
|
+
attr_accessor :client
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
read_settings
|
14
|
+
end
|
15
|
+
|
16
|
+
def client
|
17
|
+
@client = Checkpoint::Rails::Client::Authenticate.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
email = prompt("Enter email:")
|
22
|
+
password = prompt("Enter password:")
|
23
|
+
|
24
|
+
url = if Rails::Checkpoint::Env.production? && ENV['CHECKPOINT_RAILS_LOCAL_TEST'].nil?
|
25
|
+
PRODUCTION_URL
|
26
|
+
else
|
27
|
+
prompt("Enter URL for #{Checkpoint::Rails::Env.environment} API calls [#{PRODUCTION_URL}]:") || PRODUCTION_URL
|
28
|
+
end
|
29
|
+
|
30
|
+
save(email, password, url)
|
31
|
+
|
32
|
+
puts "Authentication successful."
|
33
|
+
puts "Settings saved to #{SETTINGS_FILE}"
|
34
|
+
rescue StandardError => e
|
35
|
+
puts "Problem authenticating: #{e.message}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def prompt(label)
|
39
|
+
puts label
|
40
|
+
value = STDIN.gets.chomp
|
41
|
+
puts
|
42
|
+
|
43
|
+
return nil if value == ''
|
44
|
+
value
|
45
|
+
end
|
46
|
+
|
47
|
+
def token(email, password)
|
48
|
+
client.login(email, password)
|
49
|
+
end
|
50
|
+
|
51
|
+
def save(email, password, url)
|
52
|
+
props = { auth_token: '',
|
53
|
+
url: url,
|
54
|
+
}
|
55
|
+
|
56
|
+
props = { Checkpoint::Rails::Env.environment => {
|
57
|
+
settings: props
|
58
|
+
} }
|
59
|
+
|
60
|
+
write_settings(complete_settings.merge(props))
|
61
|
+
|
62
|
+
props[Checkpoint::Rails::Env.environment][:settings][:auth_token] = token(email, password)
|
63
|
+
|
64
|
+
write_settings(complete_settings.merge(props))
|
65
|
+
|
66
|
+
read_settings
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.instructions
|
70
|
+
read_settings
|
71
|
+
puts "loading environment #{Checkpoint::Rails::Env.environment}"
|
72
|
+
puts
|
73
|
+
puts "Usage: "
|
74
|
+
puts
|
75
|
+
puts " callstacking-rails register"
|
76
|
+
puts " Opens a browser window to register as a Callstacking user."
|
77
|
+
puts
|
78
|
+
puts " callstacking-rails setup"
|
79
|
+
puts " Interactively prompts you for your Callstacking username/password, "
|
80
|
+
puts " Stores auth details in #{SETTINGS_FILE} "
|
81
|
+
puts
|
82
|
+
puts " You can have multiple environments."
|
83
|
+
puts " Default is #{Rails::Checkpoint::Env::DEFAULT_ENVIRONMENT}."
|
84
|
+
puts
|
85
|
+
puts " The #{Checkpoint::Rails::Env.environment}: section in the #{SETTINGS_FILE} contains your credentials."
|
86
|
+
puts " By setting the RAILS_ENV environment you can maintain"
|
87
|
+
puts " multiple settings."
|
88
|
+
puts
|
89
|
+
puts "Questions? Create an issue: https://github.com/callstacking/callstacking-rails/issues"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "checkpoint/rails/client/base"
|
3
|
+
require "checkpoint/rails/client/authenticate"
|
4
|
+
require "checkpoint/rails/client/trace"
|
5
|
+
require "checkpoint/rails/settings"
|
6
|
+
|
7
|
+
module Checkpoint
|
8
|
+
module Rails
|
9
|
+
module Traceable
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
include Checkpoint::Rails::Settings
|
12
|
+
|
13
|
+
TARGET_DIV = 'traces'
|
14
|
+
|
15
|
+
mattr_accessor :current_request_id
|
16
|
+
|
17
|
+
def set_trace
|
18
|
+
read_settings
|
19
|
+
|
20
|
+
client = Checkpoint::Rails::Client::Trace.new
|
21
|
+
|
22
|
+
trace_points = {}
|
23
|
+
params = {}
|
24
|
+
prev_event = ''
|
25
|
+
order_num = 0
|
26
|
+
trace_id = nil
|
27
|
+
traces = []
|
28
|
+
lock = Mutex.new
|
29
|
+
task = nil
|
30
|
+
|
31
|
+
ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
|
32
|
+
next if payload[:controller] == 'Checkpoint::Rails::TracesController'
|
33
|
+
|
34
|
+
key = request_key(payload)
|
35
|
+
params[key] = payload[:params]
|
36
|
+
|
37
|
+
nesting_level = -1
|
38
|
+
|
39
|
+
request_id = payload[:request].request_id
|
40
|
+
Checkpoint::Rails::Traceable.current_request_id = request_id
|
41
|
+
|
42
|
+
trace_id, _interval = client.create(payload[:method], payload[:controller],
|
43
|
+
payload[:action], payload[:format],
|
44
|
+
::Rails.root, payload[:request].original_url,
|
45
|
+
request_id,
|
46
|
+
payload[:headers], params[key])
|
47
|
+
|
48
|
+
|
49
|
+
puts "#{settings[:url] || Checkpoint::Rails::Settings::PRODUCTION_URL}/traces/#{trace_id}"
|
50
|
+
|
51
|
+
task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 60) {
|
52
|
+
send_traces!(trace_id, traces, lock, client)
|
53
|
+
}
|
54
|
+
|
55
|
+
task.execute
|
56
|
+
|
57
|
+
create_message(start_request_message(payload), order_num, traces, lock)
|
58
|
+
|
59
|
+
trace_points[key]&.disable
|
60
|
+
trace_point = TracePoint.new(:call, :return) do |t|
|
61
|
+
trace = caller[0]
|
62
|
+
|
63
|
+
next unless trace.match?(Dir.pwd)
|
64
|
+
|
65
|
+
order_num += 1
|
66
|
+
|
67
|
+
if t.event == :call
|
68
|
+
nesting_level += 1
|
69
|
+
|
70
|
+
prev_event = previous_event(t)
|
71
|
+
|
72
|
+
create_call_entry(nesting_level, order_num, t, traces, lock)
|
73
|
+
elsif t.event == :return
|
74
|
+
coupled_callee = false
|
75
|
+
coupled_callee = true if prev_event == previous_event(t)
|
76
|
+
|
77
|
+
prev_event = previous_event(t)
|
78
|
+
return_value = t.return_value.inspect
|
79
|
+
|
80
|
+
create_call_return(coupled_callee, nesting_level, order_num, return_value, t, traces, lock)
|
81
|
+
|
82
|
+
nesting_level -= 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
trace_point.enable
|
87
|
+
trace_points[key] = trace_point
|
88
|
+
end
|
89
|
+
|
90
|
+
ActiveSupport::Notifications.subscribe("process_action.action_controller") do |name, start, finish, id, payload|
|
91
|
+
trace_points[request_key(payload)]&.disable
|
92
|
+
task&.shutdown
|
93
|
+
|
94
|
+
create_message(completed_request_message(payload), order_num, traces, lock)
|
95
|
+
send_traces!(trace_id, traces, lock, client)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def completed_request_message(payload)
|
102
|
+
"Completed request: #{payload[:method]} #{payload[:controller]}##{payload[:action]} as #{payload[:format]}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def start_request_message(payload)
|
106
|
+
puts "start request message - "
|
107
|
+
"Request: #{payload[:method]} #{payload[:controller]}##{payload[:action]} as #{payload[:format]}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_call_return(coupled_callee, nesting_level, order_num, return_value, t, traces, lock)
|
111
|
+
lock.synchronize do
|
112
|
+
traces << { trace_entry: { trace_entryable_type: 'TraceCallReturn',
|
113
|
+
order_num: order_num,
|
114
|
+
nesting_level: nesting_level,
|
115
|
+
trace_entryable_attributes: {
|
116
|
+
local_variables: locals_for(t),
|
117
|
+
klass: t.binding.receiver.class.name.to_s,
|
118
|
+
line_number: t.lineno,
|
119
|
+
path: t.path,
|
120
|
+
method_name: t.method_id,
|
121
|
+
return_value: return_value,
|
122
|
+
coupled_callee: coupled_callee,
|
123
|
+
} } }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def create_call_entry(nesting_level, order_num, t, traces, lock)
|
128
|
+
lock.synchronize do
|
129
|
+
traces << { trace_entry: { trace_entryable_type: 'TraceCallEntry',
|
130
|
+
order_num: order_num,
|
131
|
+
nesting_level: nesting_level,
|
132
|
+
trace_entryable_attributes: {
|
133
|
+
args: arguments_for(t),
|
134
|
+
klass: t.binding.receiver.class.name.to_s,
|
135
|
+
line_number: t.lineno,
|
136
|
+
path: t.path,
|
137
|
+
method_name: t.method_id,
|
138
|
+
} } }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def create_message(message, order_num, traces, lock)
|
143
|
+
lock.synchronize do
|
144
|
+
traces << { trace_entry: { trace_entryable_type: 'TraceMessage',
|
145
|
+
order_num: order_num,
|
146
|
+
nesting_level: 0,
|
147
|
+
trace_entryable_attributes: {
|
148
|
+
message: message
|
149
|
+
} } }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def send_traces!(trace_id, traces, lock, client)
|
154
|
+
lock.synchronize do
|
155
|
+
return if traces.empty?
|
156
|
+
|
157
|
+
client.upsert(trace_id, { traces: traces })
|
158
|
+
traces.clear
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def caller_key(t)
|
163
|
+
"#{t.binding.receiver.class.name.to_s}##{t.method_id}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def request_key(payload)
|
167
|
+
"#{payload[:controller]}##{payload[:action]}"
|
168
|
+
end
|
169
|
+
|
170
|
+
def previous_event(t)
|
171
|
+
"#{t.binding.receiver.class.name.to_s}:#{t.method_id}"
|
172
|
+
end
|
173
|
+
|
174
|
+
def view_rendered?(t)
|
175
|
+
t.path.to_s =~ /view/
|
176
|
+
end
|
177
|
+
|
178
|
+
def partial_rendered?(t)
|
179
|
+
view_rendered?(t) && Pathname.new(t.path).basename.to_s =~ /^_/
|
180
|
+
end
|
181
|
+
|
182
|
+
def layout_rendered?(t)
|
183
|
+
view_rendered?(t) && t.path =~ /layouts/
|
184
|
+
end
|
185
|
+
|
186
|
+
def template_rendered?(t)
|
187
|
+
view_rendered?(t) && !partial_rendered?(t) && !layout_rendered?(t)
|
188
|
+
end
|
189
|
+
|
190
|
+
def arguments_for(trace)
|
191
|
+
param_names = trace&.parameters&.map(&:last)
|
192
|
+
return {} if param_names.nil?
|
193
|
+
|
194
|
+
param_names.map do |param|
|
195
|
+
next if [:&, :*, :**].include?(param)
|
196
|
+
[param, trace.binding.local_variable_get(param.to_s)]
|
197
|
+
end.compact.to_h
|
198
|
+
end
|
199
|
+
|
200
|
+
def locals_for(trace)
|
201
|
+
local_names = trace&.binding&.local_variables
|
202
|
+
return {} if local_names.nil?
|
203
|
+
|
204
|
+
local_names.map do |local|
|
205
|
+
[local, trace.binding.local_variable_get(local)]
|
206
|
+
end.to_h
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "action_view/helpers/tag_helper.rb"
|
2
|
+
require "action_view/context.rb"
|
3
|
+
|
4
|
+
module Checkpoint
|
5
|
+
module Rails
|
6
|
+
module TracesHelper
|
7
|
+
include ActionView::Helpers::TagHelper
|
8
|
+
include ActionView::Context
|
9
|
+
include Checkpoint::Rails::Settings
|
10
|
+
|
11
|
+
def hud
|
12
|
+
read_settings
|
13
|
+
|
14
|
+
frame_url = "#{url || Checkpoint::Rails::Settings::PRODUCTION_URL}/traces/#{Checkpoint::Rails::Traceable.current_request_id}/print"
|
15
|
+
|
16
|
+
body = []
|
17
|
+
body << (content_tag( :div, data: { turbo:false },
|
18
|
+
style: 'background-color: #FFF; color: #0000FF; font-size: 20pt; top: 50%; right: 20px;
|
19
|
+
padding: 30px 10px 0px 10px; position: fixed; height: 100px; width: 40px; cursor: pointer;',
|
20
|
+
onclick: 'document.getElementById("callstacking-debugger").style.display = "unset";
|
21
|
+
document.getElementById("callstacking-close").style.display = "unset";') do
|
22
|
+
"⇐"
|
23
|
+
end)
|
24
|
+
|
25
|
+
body << (content_tag(:iframe, src: frame_url, id: 'callstacking-debugger', data: { turbo:false },
|
26
|
+
style: "width: 50%; height: 100%; overflow: scroll; top: 20px; right: 20px; position: fixed;
|
27
|
+
z-index: 99; opacity: 1.0; background-color: #FFF; color: #000; border: 1px solid;
|
28
|
+
margin: 0; padding: 0; box-shadow: 5px 5px; display: none;") do
|
29
|
+
end)
|
30
|
+
|
31
|
+
body.join
|
32
|
+
end
|
33
|
+
|
34
|
+
def inject_hud
|
35
|
+
response.body = response.body.sub(/<\/body>/i, "#{hud}</body>")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: callstacking-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jim Jones
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-11-21 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: '4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faraday-follow_redirects
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: concurrent-ruby
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Rolling debugger that shows the full state of each call.
|
70
|
+
email:
|
71
|
+
- jim.jones1@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- MIT-LICENSE
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- app/assets/config/checkpoint_rails_manifest.js
|
80
|
+
- app/assets/stylesheets/checkpoint/rails/application.css
|
81
|
+
- app/controllers/checkpoint/rails/application_controller.rb
|
82
|
+
- app/controllers/checkpoint/rails/traces_controller.rb
|
83
|
+
- app/helpers/checkpoint/rails/application_helper.rb
|
84
|
+
- app/jobs/checkpoint/rails/application_job.rb
|
85
|
+
- app/mailers/checkpoint/rails/application_mailer.rb
|
86
|
+
- app/models/checkpoint/rails/application_record.rb
|
87
|
+
- config/routes.rb
|
88
|
+
- lib/checkpoint/rails.rb
|
89
|
+
- lib/checkpoint/rails/client/authenticate.rb
|
90
|
+
- lib/checkpoint/rails/client/base.rb
|
91
|
+
- lib/checkpoint/rails/client/trace.rb
|
92
|
+
- lib/checkpoint/rails/engine.rb
|
93
|
+
- lib/checkpoint/rails/env.rb
|
94
|
+
- lib/checkpoint/rails/settings.rb
|
95
|
+
- lib/checkpoint/rails/setup.rb
|
96
|
+
- lib/checkpoint/rails/traceable.rb
|
97
|
+
- lib/checkpoint/rails/traces_helper.rb
|
98
|
+
- lib/checkpoint/rails/version.rb
|
99
|
+
- lib/tasks/checkpoint/rails_tasks.rake
|
100
|
+
homepage: https://github.com/callstacking/callstacking-rails
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata:
|
104
|
+
homepage_uri: https://github.com/callstacking/callstacking-rails
|
105
|
+
source_code_uri: https://github.com/callstacking/callstacking-rails
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubygems_version: 3.3.7
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: Rolling debugger that shows the full state of each call per request.
|
125
|
+
test_files: []
|