callstacking-rails 0.1.20 → 0.1.22
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 +4 -4
- data/lib/callstacking/rails/client/trace.rb +5 -3
- data/lib/callstacking/rails/engine.rb +23 -3
- data/lib/callstacking/rails/instrument.rb +46 -31
- data/lib/callstacking/rails/loader.rb +16 -9
- data/lib/callstacking/rails/settings.rb +6 -2
- data/lib/callstacking/rails/time_based_uuid.rb +25 -0
- data/lib/callstacking/rails/trace.rb +57 -37
- data/lib/callstacking/rails/traces_helper.rb +1 -1
- data/lib/callstacking/rails/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db3f273ed8ae89ee3727de24fa80f8a3621ebb9aa5997874cf8f8f2607f9595b
|
4
|
+
data.tar.gz: facd7b5f062db3b4e50ecdd78857b5b0635119a8aa07589bc1e9fa03583bf036
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a74d58ed4f01a8a466ae81d763499d4a0004a97bdaee5cf8fe7aba47d97b367dbbc4ba7d2f1e8d2be0bff8d450b09a99b8de1fb4f738baa67664623583884660
|
7
|
+
data.tar.gz: 104df4887100b79f1d056fc24e7690dee30404a5c3827fd9d7fcce2c6c84e74124649a98e6c71c77cd62310d227b5987ee306d2f09f4816b3825adf32250aeb4
|
@@ -7,16 +7,18 @@ module Callstacking
|
|
7
7
|
CREATE_URL = "/api/v1/traces.json"
|
8
8
|
UPDATE_URL = "/api/v1/traces/:id.json"
|
9
9
|
|
10
|
-
def create(method_name, klass, action_name, format_name, root_path, url,
|
10
|
+
def create(request_id, tuid, method_name, klass, action_name, format_name, root_path, url, headers, params)
|
11
11
|
resp = post(CREATE_URL,
|
12
12
|
{},
|
13
|
-
{
|
13
|
+
{
|
14
|
+
request_id: request_id,
|
15
|
+
tuid: tuid,
|
16
|
+
method_name: method_name,
|
14
17
|
klass: klass,
|
15
18
|
action_name: action_name,
|
16
19
|
format_name: format_name,
|
17
20
|
root_path: root_path,
|
18
21
|
url: url,
|
19
|
-
request_id: request_id,
|
20
22
|
h: headers.to_h,
|
21
23
|
p: params.to_h,
|
22
24
|
})
|
@@ -12,17 +12,21 @@ require "callstacking/rails/client/authenticate"
|
|
12
12
|
require "callstacking/rails/client/trace"
|
13
13
|
require "callstacking/rails/cli"
|
14
14
|
require "callstacking/rails/traces_helper"
|
15
|
+
require "callstacking/rails/time_based_uuid"
|
15
16
|
|
16
17
|
module Callstacking
|
17
18
|
module Rails
|
18
19
|
class Engine < ::Rails::Engine
|
19
|
-
|
20
|
+
EXCLUDED_TEST_CLASSES = ['test/dummy/app/models/salutation.rb'].freeze
|
21
|
+
|
22
|
+
cattr_accessor :spans, :trace, :settings, :instrumenter, :loader
|
20
23
|
|
21
24
|
isolate_namespace Callstacking::Rails
|
22
25
|
|
23
26
|
@@settings||=Callstacking::Rails::Settings.new
|
24
27
|
@@spans||=Spans.new
|
25
28
|
@@trace||=Trace.new(@@spans)
|
29
|
+
@@instrumenter||=Instrument.new(@@spans)
|
26
30
|
|
27
31
|
initializer "engine_name.assets.precompile" do |app|
|
28
32
|
app.config.assets.precompile << "checkpoint_rails_manifest.js"
|
@@ -42,13 +46,29 @@ module Callstacking
|
|
42
46
|
inject_hud(@@settings)
|
43
47
|
end
|
44
48
|
|
45
|
-
Callstacking::Rails::Loader.new
|
49
|
+
@@loader = Callstacking::Rails::Loader.new(@@instrumenter,
|
50
|
+
excluded: @@settings.excluded + EXCLUDED_TEST_CLASSES)
|
51
|
+
@@loader.on_load
|
46
52
|
|
47
|
-
@@trace.
|
53
|
+
@@trace.request_tracing
|
48
54
|
else
|
49
55
|
puts "Call Stacking disabled (#{Callstacking::Rails::Env.environment})"
|
50
56
|
end
|
51
57
|
end
|
58
|
+
|
59
|
+
def self.start_tracing
|
60
|
+
return false if @@settings.disabled?
|
61
|
+
|
62
|
+
@@instrumenter.enable!(@@loader.klasses)
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.stop_tracing
|
67
|
+
return false if @@settings.disabled?
|
68
|
+
|
69
|
+
@@instrumenter.disable!
|
70
|
+
true
|
71
|
+
end
|
52
72
|
end
|
53
73
|
end
|
54
74
|
end
|
@@ -4,25 +4,26 @@ require 'rails'
|
|
4
4
|
module Callstacking
|
5
5
|
module Rails
|
6
6
|
class Instrument
|
7
|
-
attr_accessor :spans
|
8
|
-
attr_reader :root
|
7
|
+
attr_accessor :spans
|
8
|
+
attr_reader :root, :settings, :span_modules
|
9
9
|
|
10
|
-
def initialize(spans
|
10
|
+
def initialize(spans)
|
11
11
|
@spans = spans
|
12
|
-
@
|
13
|
-
@
|
12
|
+
@span_modules = Set.new
|
13
|
+
@settings = Callstacking::Rails::Settings.new
|
14
|
+
@root = Regexp.new(::Rails.root.to_s) # <-------
|
14
15
|
end
|
15
16
|
|
16
|
-
def instrument_method(method_name, application_level: true)
|
17
|
+
def instrument_method(klass, method_name, application_level: true)
|
17
18
|
method_path = (klass.instance_method(method_name).source_location.first rescue nil) ||
|
18
19
|
(klass.method(method_name).source_location.first rescue nil)
|
19
20
|
|
20
21
|
# Application level method definitions
|
21
|
-
return
|
22
|
+
return if method_path =~ /#{::Rails.root.to_s}/ && application_level
|
22
23
|
|
23
24
|
return if method_path =~ /initializer/i
|
24
25
|
|
25
|
-
tmp_module = find_or_initialize_module
|
26
|
+
tmp_module = find_or_initialize_module(klass)
|
26
27
|
|
27
28
|
return if tmp_module.nil? ||
|
28
29
|
tmp_module.instance_methods.include?(method_name) ||
|
@@ -76,31 +77,23 @@ module Callstacking
|
|
76
77
|
new_method
|
77
78
|
end
|
78
79
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
module_name = "#{klass.name.gsub('::', '')}Span"
|
84
|
-
module_index = klass.ancestors.map(&:to_s).index(module_name)
|
85
|
-
|
86
|
-
unless module_index
|
87
|
-
new_module = Object.const_set(module_name, Module.new)
|
88
|
-
|
89
|
-
new_module.instance_variable_set("@klass", klass)
|
90
|
-
new_module.instance_variable_set("@spans", spans)
|
91
|
-
|
92
|
-
klass.prepend new_module
|
93
|
-
klass.singleton_class.prepend new_module if klass.class == Module
|
94
|
-
|
95
|
-
return find_or_initialize_module
|
80
|
+
def enable!(klasses)
|
81
|
+
klasses.each do |klass|
|
82
|
+
instrument_klass(klass, application_level: true)
|
96
83
|
end
|
84
|
+
end
|
97
85
|
|
98
|
-
|
86
|
+
def disable!
|
87
|
+
span_modules.each do |mod|
|
88
|
+
mod.instance_methods.each do |method_name|
|
89
|
+
mod.remove_method(method_name)
|
90
|
+
end
|
91
|
+
end
|
99
92
|
end
|
100
93
|
|
101
|
-
def instrument_klass(application_level: true)
|
102
|
-
|
103
|
-
|
94
|
+
def instrument_klass(klass, application_level: true)
|
95
|
+
relevant_methods = all_methods(klass) - filtered
|
96
|
+
relevant_methods.each { |method| instrument_method(klass, method, application_level: application_level) }
|
104
97
|
end
|
105
98
|
|
106
99
|
def self.arguments_for(m, args)
|
@@ -118,9 +111,31 @@ module Callstacking
|
|
118
111
|
end
|
119
112
|
|
120
113
|
private
|
114
|
+
def find_or_initialize_module(klass)
|
115
|
+
name = klass&.name rescue nil
|
116
|
+
return if name.nil?
|
117
|
+
|
118
|
+
module_name = "#{klass.name.gsub('::', '')}Span"
|
119
|
+
module_index = klass.ancestors.map(&:to_s).index(module_name)
|
120
|
+
|
121
|
+
unless module_index
|
122
|
+
new_module = Object.const_set(module_name, Module.new)
|
123
|
+
span_modules << new_module
|
124
|
+
|
125
|
+
new_module.instance_variable_set("@klass", klass)
|
126
|
+
new_module.instance_variable_set("@spans", spans)
|
127
|
+
|
128
|
+
klass.prepend new_module
|
129
|
+
klass.singleton_class.prepend new_module if klass.class == Module
|
130
|
+
|
131
|
+
return find_or_initialize_module(klass)
|
132
|
+
end
|
133
|
+
|
134
|
+
klass.ancestors[module_index]
|
135
|
+
end
|
121
136
|
|
122
|
-
def all_methods
|
123
|
-
|
137
|
+
def all_methods(klass)
|
138
|
+
(klass.instance_methods +
|
124
139
|
klass.private_instance_methods(false) +
|
125
140
|
klass.protected_instance_methods(false) +
|
126
141
|
klass.methods +
|
@@ -3,26 +3,33 @@ require "rails"
|
|
3
3
|
module Callstacking
|
4
4
|
module Rails
|
5
5
|
class Loader
|
6
|
-
attr_accessor :
|
7
|
-
def initialize
|
6
|
+
attr_accessor :root, :instrumenter, :klasses, :excluded
|
7
|
+
def initialize(instrumenter, excluded: [])
|
8
8
|
@root = Regexp.new(::Rails.root.to_s)
|
9
|
+
@excluded = excluded
|
10
|
+
@instrumenter = instrumenter
|
11
|
+
@klasses = Set.new
|
9
12
|
end
|
10
13
|
|
11
|
-
def on_load
|
14
|
+
def on_load
|
12
15
|
trace = TracePoint.new(:end) do |tp|
|
13
16
|
klass = tp.self
|
14
17
|
path = tp.path
|
15
18
|
|
16
|
-
|
19
|
+
excluded_klass = excluded.any? { |ex| path =~ /#{ex}/ }
|
20
|
+
|
21
|
+
if path =~ root &&
|
22
|
+
!klasses.include?(klass) &&
|
23
|
+
!excluded_klass
|
24
|
+
instrumenter.instrument_klass(klass)
|
25
|
+
klasses << klass
|
26
|
+
end
|
17
27
|
end
|
18
28
|
|
19
29
|
trace.enable
|
20
30
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
Instrument.new(spans, ActionView::TemplateRenderer).instrument_method( :render,
|
25
|
-
application_level: false)
|
31
|
+
instrumenter.instrument_method(ActionView::PartialRenderer, :render, application_level: false)
|
32
|
+
instrumenter.instrument_method(ActionView::TemplateRenderer, :render, application_level: false)
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
@@ -34,11 +34,11 @@ module Callstacking
|
|
34
34
|
File.write(SETTINGS_FILE, new_settings.to_yaml)
|
35
35
|
end
|
36
36
|
|
37
|
-
def enable!
|
37
|
+
def self.enable!
|
38
38
|
::Rails.cache.write(CACHE_KEY, true)
|
39
39
|
end
|
40
40
|
|
41
|
-
def disable!
|
41
|
+
def self.disable!
|
42
42
|
::Rails.cache.write(CACHE_KEY, false)
|
43
43
|
end
|
44
44
|
|
@@ -50,6 +50,10 @@ module Callstacking
|
|
50
50
|
settings[:enabled]
|
51
51
|
end
|
52
52
|
|
53
|
+
def excluded
|
54
|
+
settings[:excluded] || []
|
55
|
+
end
|
56
|
+
|
53
57
|
def disabled?
|
54
58
|
!enabled?
|
55
59
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class TimeBasedUUID
|
4
|
+
EPOCH_OFFSET = 1468418800000 # A custom epoch, it could be the UNIX timestamp when the application was created (in milliseconds)
|
5
|
+
MAX_INT8_VALUE = 9223372036854775807
|
6
|
+
|
7
|
+
def self.generate
|
8
|
+
# Get the current time in milliseconds
|
9
|
+
current_time = (Time.now.to_f * 1000).to_i
|
10
|
+
|
11
|
+
# Subtract the custom epoch to reduce the timestamp size
|
12
|
+
timestamp = current_time - EPOCH_OFFSET
|
13
|
+
|
14
|
+
# Generate a random 64-bit number using SecureRandom
|
15
|
+
random_bits = SecureRandom.random_number(1 << 64)
|
16
|
+
|
17
|
+
# Combine the timestamp and the random bits
|
18
|
+
uuid = (timestamp << 64) | random_bits
|
19
|
+
|
20
|
+
# Ensure the UUID fits into a PostgreSQL int8 column
|
21
|
+
uuid = uuid % MAX_INT8_VALUE if uuid > MAX_INT8_VALUE
|
22
|
+
|
23
|
+
uuid
|
24
|
+
end
|
25
|
+
end
|
@@ -6,9 +6,11 @@ module Callstacking
|
|
6
6
|
class Trace
|
7
7
|
attr_accessor :spans, :client, :lock
|
8
8
|
attr_reader :settings
|
9
|
-
cattr_accessor :
|
9
|
+
cattr_accessor :current_trace_id
|
10
|
+
cattr_accessor :current_tuid
|
10
11
|
|
11
12
|
ICON = '💥'
|
13
|
+
MAX_TRACE_ENTRIES = 3000
|
12
14
|
|
13
15
|
def initialize(spans)
|
14
16
|
@traces = []
|
@@ -17,37 +19,54 @@ module Callstacking
|
|
17
19
|
|
18
20
|
@lock = Mutex.new
|
19
21
|
@client = Callstacking::Rails::Client::Trace.new(settings.url, settings.auth_token)
|
22
|
+
|
23
|
+
init_uuids(nil, nil)
|
24
|
+
init_callbacks(nil)
|
20
25
|
end
|
21
26
|
|
22
|
-
def
|
27
|
+
def request_tracing
|
28
|
+
tuid = nil
|
23
29
|
trace_id = nil
|
24
|
-
max_trace_entries = nil
|
25
30
|
|
26
|
-
ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |
|
27
|
-
trace_id,
|
28
|
-
|
29
|
-
payload[:request]&.original_url || payload[:path],
|
30
|
-
payload[:headers], payload[:params])
|
31
|
-
end
|
31
|
+
ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |_name, _start, _finish, _id, payload|
|
32
|
+
trace_id, tuid = init_uuids(payload[:request]&.request_id || SecureRandom.uuid, TimeBasedUUID.generate)
|
33
|
+
init_callbacks(tuid)
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
+
start_request(trace_id, tuid,
|
36
|
+
payload[:method], payload[:controller],
|
37
|
+
payload[:action], payload[:format], ::Rails.root,
|
38
|
+
payload[:request]&.original_url || payload[:path],
|
39
|
+
payload[:headers], payload[:params])
|
35
40
|
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
ActiveSupport::Notifications.subscribe("process_action.action_controller") do |name, start, finish, id, payload|
|
42
|
-
complete_request(payload[:method], payload[:controller],
|
42
|
+
ActiveSupport::Notifications.subscribe("process_action.action_controller") do |_name, _start, _finish, _id, payload|
|
43
|
+
complete_request(trace_id, tuid,
|
44
|
+
payload[:method], payload[:controller],
|
43
45
|
payload[:action], payload[:format],
|
44
46
|
payload[:request]&.original_url || payload[:path],
|
45
|
-
|
47
|
+
@traces, MAX_TRACE_ENTRIES)
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
51
|
private
|
50
52
|
|
53
|
+
def init_callbacks(tuid)
|
54
|
+
@spans.on_call_entry do |nesting_level, order_num, klass, method_name, arguments, path, line_no|
|
55
|
+
create_call_entry(tuid, nesting_level, order_num, klass, method_name, arguments, path, line_no, @traces)
|
56
|
+
end
|
57
|
+
|
58
|
+
@spans.on_call_return do |coupled_callee, nesting_level, order_num, klass, method_name, path, line_no, return_val|
|
59
|
+
create_call_return(tuid, coupled_callee, nesting_level, order_num, klass, method_name, path, line_no, return_val, @traces)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def init_uuids(trace_id, tuid)
|
64
|
+
Callstacking::Rails::Trace.current_trace_id = trace_id
|
65
|
+
Callstacking::Rails::Trace.current_tuid = tuid
|
66
|
+
|
67
|
+
return trace_id, tuid
|
68
|
+
end
|
69
|
+
|
51
70
|
def completed_request_message(method, controller, action, format)
|
52
71
|
"Completed request: #{method} #{controller}##{action} as #{format}"
|
53
72
|
end
|
@@ -56,9 +75,10 @@ module Callstacking
|
|
56
75
|
"Started request: #{method} #{controller}##{action} as #{format}"
|
57
76
|
end
|
58
77
|
|
59
|
-
def create_call_return(coupled_callee, nesting_level, order_num, klass, method_name, path, line_no, return_val, traces)
|
78
|
+
def create_call_return(tuid, coupled_callee, nesting_level, order_num, klass, method_name, path, line_no, return_val, traces)
|
60
79
|
lock.synchronize do
|
61
|
-
traces << {
|
80
|
+
traces << { tuid: tuid,
|
81
|
+
type: 'TraceCallReturn',
|
62
82
|
order_num: order_num,
|
63
83
|
nesting_level: nesting_level,
|
64
84
|
local_variables: {},
|
@@ -74,9 +94,10 @@ module Callstacking
|
|
74
94
|
end
|
75
95
|
end
|
76
96
|
|
77
|
-
def create_call_entry(nesting_level, order_num, klass, method_name, arguments, path, line_no, traces)
|
97
|
+
def create_call_entry(tuid, nesting_level, order_num, klass, method_name, arguments, path, line_no, traces)
|
78
98
|
lock.synchronize do
|
79
|
-
traces << {
|
99
|
+
traces << { tuid: tuid,
|
100
|
+
type: 'TraceCallEntry',
|
80
101
|
order_num: order_num,
|
81
102
|
nesting_level: nesting_level,
|
82
103
|
args: arguments,
|
@@ -92,9 +113,10 @@ module Callstacking
|
|
92
113
|
end
|
93
114
|
end
|
94
115
|
|
95
|
-
def create_message(message, order_num, traces)
|
116
|
+
def create_message(tuid, message, order_num, traces)
|
96
117
|
lock.synchronize do
|
97
|
-
traces << {
|
118
|
+
traces << { tuid: tuid,
|
119
|
+
type: 'TraceMessage',
|
98
120
|
order_num: order_num,
|
99
121
|
nesting_level: 0,
|
100
122
|
message: message,
|
@@ -114,26 +136,24 @@ module Callstacking
|
|
114
136
|
lock.synchronize do
|
115
137
|
return if traces.empty?
|
116
138
|
|
117
|
-
client.upsert(trace_id,
|
139
|
+
client.upsert(trace_id,
|
140
|
+
{ trace_entries: traces })
|
118
141
|
traces.clear
|
119
142
|
end
|
120
143
|
end
|
121
|
-
def start_request(
|
144
|
+
def start_request(trace_id, tuid, method, controller, action, format, path, original_url, headers, params)
|
122
145
|
return if do_not_track_request?(original_url, format)
|
123
|
-
|
124
|
-
request_id = request_id || SecureRandom.uuid
|
125
|
-
Callstacking::Rails::Trace.current_request_id = request_id
|
126
146
|
|
127
|
-
|
128
|
-
|
129
|
-
|
147
|
+
client.create(trace_id, tuid,
|
148
|
+
method, controller,
|
149
|
+
action, format,
|
150
|
+
path, original_url,
|
151
|
+
headers, params)
|
130
152
|
|
131
153
|
print_trace_url(trace_id)
|
132
154
|
|
133
|
-
create_message(start_request_message(method, controller, action, format),
|
155
|
+
create_message(tuid, start_request_message(method, controller, action, format),
|
134
156
|
spans.increment_order_num, @traces)
|
135
|
-
|
136
|
-
return trace_id, max_trace_entries
|
137
157
|
end
|
138
158
|
|
139
159
|
def print_trace_url(trace_id)
|
@@ -146,13 +166,13 @@ module Callstacking
|
|
146
166
|
url
|
147
167
|
end
|
148
168
|
|
149
|
-
def complete_request(method, controller, action, format, original_url,
|
169
|
+
def complete_request(trace_id, tuid, method, controller, action, format, original_url, traces, max_trace_entries)
|
150
170
|
if do_not_track_request?(original_url, format)
|
151
171
|
traces.clear
|
152
172
|
return
|
153
173
|
end
|
154
174
|
|
155
|
-
create_message(completed_request_message(method, controller, action, format),
|
175
|
+
create_message(tuid, completed_request_message(method, controller, action, format),
|
156
176
|
spans.increment_order_num, traces)
|
157
177
|
|
158
178
|
send_traces!(trace_id, traces[0..max_trace_entries])
|
@@ -9,7 +9,7 @@ module Callstacking
|
|
9
9
|
include ActionView::Context
|
10
10
|
|
11
11
|
def hud(url)
|
12
|
-
frame_url = "#{url || Callstacking::Rails::Settings::PRODUCTION_URL}/traces/#{Callstacking::Rails::Trace.
|
12
|
+
frame_url = "#{url || Callstacking::Rails::Settings::PRODUCTION_URL}/traces/#{Callstacking::Rails::Trace.current_trace_id}/print"
|
13
13
|
|
14
14
|
body = []
|
15
15
|
body << (content_tag( :div, data: { turbo:false },
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: callstacking-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Jones
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- lib/callstacking/rails/settings.rb
|
114
114
|
- lib/callstacking/rails/setup.rb
|
115
115
|
- lib/callstacking/rails/spans.rb
|
116
|
+
- lib/callstacking/rails/time_based_uuid.rb
|
116
117
|
- lib/callstacking/rails/trace.rb
|
117
118
|
- lib/callstacking/rails/traces_helper.rb
|
118
119
|
- lib/callstacking/rails/version.rb
|