callstacking-rails 0.1.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.
@@ -0,0 +1,72 @@
1
+ module Callstacking
2
+ module Rails
3
+ class Spans
4
+ attr_accessor :order_num, :nesting_level, :previous_entry
5
+ attr_accessor :call_entry_callback, :call_return_callback
6
+
7
+ def initialize
8
+ @nesting_level = -1
9
+ @order_num = -1
10
+ @previous_entry = nil
11
+ end
12
+
13
+ def increment_order_num
14
+ @order_num+=1
15
+ @order_num
16
+ end
17
+
18
+ def increment_nesting_level
19
+ @nesting_level+=1
20
+ @nesting_level
21
+ end
22
+
23
+ def call_entry(klass, method_name, path, line_no)
24
+ @nesting_level+=1
25
+ @previous_entry = previous_event(klass, method_name)
26
+ @call_entry_callback.call(@nesting_level, increment_order_num, klass, method_name, path, line_no)
27
+ end
28
+
29
+ def call_return(klass, method_name, path, line_no, return_val)
30
+ @call_return_callback.call(coupled_callee(klass, method_name), @nesting_level,
31
+ increment_order_num, klass, method_name, path, line_no, return_val)
32
+ @nesting_level-=1
33
+ end
34
+
35
+ def on_call_entry(&block)
36
+ @call_entry_callback = block
37
+ end
38
+
39
+ def on_call_return(&block)
40
+ @call_return_callback = block
41
+ end
42
+
43
+ def arguments_for(trace)
44
+ param_names = trace&.parameters&.map(&:last)
45
+ return {} if param_names.nil?
46
+
47
+ param_names.map do |param|
48
+ next if [:&, :*, :**].include?(param)
49
+ [param, trace.binding.local_variable_get(param.to_s)]
50
+ end.compact.to_h
51
+ end
52
+
53
+ def locals_for(trace)
54
+ local_names = trace&.binding&.local_variables
55
+ return {} if local_names.nil?
56
+
57
+ local_names.map do |local|
58
+ [local, trace.binding.local_variable_get(local)]
59
+ end.to_h
60
+ end
61
+
62
+ private
63
+ def previous_event(klass, method_name)
64
+ "#{klass}:#{method_name}"
65
+ end
66
+
67
+ def coupled_callee(klass, method_name)
68
+ previous_entry == previous_event(klass, method_name)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,126 @@
1
+ require "rails"
2
+ require "active_support/concern"
3
+ require "callstacking/rails/client/base"
4
+ require "callstacking/rails/client/authenticate"
5
+ require "callstacking/rails/client/trace"
6
+ require "callstacking/rails/settings"
7
+
8
+ module Callstacking
9
+ module Rails
10
+ class Trace
11
+ include Callstacking::Rails::Settings
12
+
13
+ attr_accessor :spans, :client, :lock
14
+ cattr_accessor :current_request_id
15
+
16
+ def initialize(spans)
17
+ @traces = []
18
+ @spans = spans
19
+
20
+ @lock = Mutex.new
21
+ @client = Callstacking::Rails::Client::Trace.new
22
+ end
23
+
24
+ def tracing
25
+ read_settings
26
+
27
+ trace_id = nil
28
+
29
+ ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
30
+ request_id = payload[:request].request_id
31
+ Callstacking::Rails::Trace.current_request_id = request_id
32
+
33
+ trace_id, _interval = client.create(payload[:method], payload[:controller],
34
+ payload[:action], payload[:format],
35
+ ::Rails.root, payload[:request].original_url,
36
+ request_id, payload[:headers],
37
+ payload[:params])
38
+
39
+ puts "#{settings[:url] || Callstacking::Rails::Settings::PRODUCTION_URL}/traces/#{trace_id}"
40
+
41
+ create_message(start_request_message(payload), spans.increment_order_num, @traces)
42
+ end
43
+
44
+ @spans.on_call_entry do |nesting_level, order_num, klass, method_name, path, line_no|
45
+ create_call_entry(nesting_level, order_num, klass, method_name, path, line_no, @traces)
46
+ end
47
+
48
+ @spans.on_call_return do |coupled_callee, nesting_level, order_num, klass, method_name, path, line_no, return_val|
49
+ create_call_return(coupled_callee, nesting_level, order_num, klass, method_name, path, line_no, return_val, @traces)
50
+ end
51
+
52
+ ActiveSupport::Notifications.subscribe("process_action.action_controller") do |name, start, finish, id, payload|
53
+ create_message(completed_request_message(payload), spans.increment_order_num, @traces)
54
+ send_traces!(trace_id, @traces)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def completed_request_message(payload)
61
+ "Completed request: #{payload[:method]} #{payload[:controller]}##{payload[:action]} as #{payload[:format]}"
62
+ end
63
+
64
+ def start_request_message(payload)
65
+ "Started request: #{payload[:method]} #{payload[:controller]}##{payload[:action]} as #{payload[:format]}"
66
+ end
67
+
68
+ def create_call_return(coupled_callee, nesting_level, order_num, klass, method_name, path, line_no, return_val, traces)
69
+ lock.synchronize do
70
+ traces << { trace_entry: { trace_entryable_type: 'TraceCallReturn',
71
+ order_num: order_num,
72
+ nesting_level: nesting_level,
73
+ trace_entryable_attributes: {
74
+ local_variables: {},
75
+ klass: klass_name(klass),
76
+ line_number: line_no,
77
+ path: path,
78
+ method_name: method_name,
79
+ return_value: return_val.inspect,
80
+ coupled_callee: coupled_callee,
81
+ } } }
82
+ end
83
+ end
84
+
85
+ def create_call_entry(nesting_level, order_num, klass, method_name, path, line_no, traces)
86
+ lock.synchronize do
87
+ traces << { trace_entry: { trace_entryable_type: 'TraceCallEntry',
88
+ order_num: order_num,
89
+ nesting_level: nesting_level,
90
+ trace_entryable_attributes: {
91
+ #args: arguments_for(t),
92
+ klass: klass_name(klass),
93
+ line_number: line_no,
94
+ path: path,
95
+ method_name: method_name,
96
+ } } }
97
+ end
98
+ end
99
+
100
+ def create_message(message, order_num, traces)
101
+ lock.synchronize do
102
+ traces << { trace_entry: { trace_entryable_type: 'TraceMessage',
103
+ order_num: order_num,
104
+ nesting_level: 0,
105
+ trace_entryable_attributes: {
106
+ message: message
107
+ } } }
108
+ end
109
+ end
110
+
111
+ def send_traces!(trace_id, traces)
112
+ lock.synchronize do
113
+ return if traces.empty?
114
+
115
+ client.upsert(trace_id, { traces: traces })
116
+ traces.clear
117
+ end
118
+ end
119
+
120
+ private
121
+ def klass_name(klass)
122
+ (klass.is_a?(Class) ? klass.name : klass.class.name)
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,39 @@
1
+ require "action_view/helpers/tag_helper"
2
+ require "action_view/context.rb"
3
+
4
+ module Callstacking
5
+ module Rails
6
+ module TracesHelper
7
+ include ActionView::Helpers::TagHelper
8
+ include ActionView::Context
9
+ include Callstacking::Rails::Settings
10
+
11
+ def hud
12
+ read_settings
13
+
14
+ frame_url = "#{url || Callstacking::Rails::Settings::PRODUCTION_URL}/traces/#{Callstacking::Rails::Trace.current_request_id}/print"
15
+
16
+ body = []
17
+ body << (content_tag( :div, data: { turbo:false },
18
+ style: 'background-color: #FFF; font-size: 20pt; top: 50%; right: 10px; font-size: 36pt;
19
+ padding: 0px; position: fixed; height: 50px; 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
@@ -0,0 +1,5 @@
1
+ module Callstacking
2
+ module Rails
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require "callstacking/rails/version"
2
+ require "callstacking/rails/engine"
3
+
4
+ module Callstacking
5
+ module Rails
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :checkpoint_rails do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: callstacking-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jim Jones
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-12-19 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
+ description: Rolling debugger that shows the full state of each call.
56
+ email:
57
+ - jim.jones1@gmail.com
58
+ executables:
59
+ - callstacking-rails
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - LICENSE
64
+ - README.md
65
+ - Rakefile
66
+ - app/assets/config/callstacking_rails_manifest.js
67
+ - app/assets/stylesheets/callstacking/rails/application.css
68
+ - app/controllers/callstacking/rails/application_controller.rb
69
+ - app/helpers/callstacking/rails/application_helper.rb
70
+ - app/jobs/callstacking/rails/application_job.rb
71
+ - app/mailers/callstacking/rails/application_mailer.rb
72
+ - app/models/callstacking/rails/application_record.rb
73
+ - config/routes.rb
74
+ - exe/callstacking-rails
75
+ - lib/callstacking/rails.rb
76
+ - lib/callstacking/rails/client/authenticate.rb
77
+ - lib/callstacking/rails/client/base.rb
78
+ - lib/callstacking/rails/client/trace.rb
79
+ - lib/callstacking/rails/engine.rb
80
+ - lib/callstacking/rails/env.rb
81
+ - lib/callstacking/rails/instrument.rb
82
+ - lib/callstacking/rails/loader.rb
83
+ - lib/callstacking/rails/settings.rb
84
+ - lib/callstacking/rails/setup.rb
85
+ - lib/callstacking/rails/spans.rb
86
+ - lib/callstacking/rails/trace.rb
87
+ - lib/callstacking/rails/traces_helper.rb
88
+ - lib/callstacking/rails/version.rb
89
+ - lib/tasks/checkpoint/rails_tasks.rake
90
+ homepage: https://github.com/callstacking/callstacking-rails
91
+ licenses:
92
+ - GPL-3.0-or-later
93
+ metadata:
94
+ homepage_uri: https://github.com/callstacking/callstacking-rails
95
+ source_code_uri: https://github.com/callstacking/callstacking-rails
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.3.7
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Rolling debugger that shows the full state of each call per request.
115
+ test_files: []