callstacking-rails 0.1.30 → 0.1.31
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/base.rb +2 -2
- data/lib/callstacking/rails/client/trace.rb +5 -0
- data/lib/callstacking/rails/engine.rb +53 -16
- data/lib/callstacking/rails/instrument.rb +23 -8
- data/lib/callstacking/rails/loader.rb +21 -7
- data/lib/callstacking/rails/logger.rb +18 -0
- data/lib/callstacking/rails/settings.rb +8 -8
- data/lib/callstacking/rails/trace.rb +16 -4
- 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: 26c64da0b1b2bb170e40ee319c04d9d8b090e4801d4aa61ecdb1728e4edb0a10
|
4
|
+
data.tar.gz: 619d6983c3aa94affb3d31c7c0e4a8532565c219bd9d14832a50b9fb46f813b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa3fc9c4d8e079df650a5efefe6704d6682f770ebd35516ff501ddd3c896f3df69e871f7d7908855d6074c0536cff05346d31a7c74024884633c685ad3bc6909
|
7
|
+
data.tar.gz: 0c253b57e8ff3044e17df80fc9bab0b3cc6cb49efc5aeba2d6900b8b27857fde19cb06fbdce5fae05da70371f6922ce7c6d4fc7419cc824c099f10df75aad7aa
|
@@ -21,7 +21,7 @@ module Callstacking
|
|
21
21
|
# https://github.com/lostisland/awesome-faraday
|
22
22
|
@connection ||= Faraday.new(url) do |c|
|
23
23
|
c.response :json
|
24
|
-
c.use Faraday::Response::Logger
|
24
|
+
c.use Faraday::Response::Logger
|
25
25
|
# c.use Faraday::Response::Logger, nil, { headers: false, bodies: false }
|
26
26
|
c.response :follow_redirects
|
27
27
|
c.use Faraday::Response::RaiseError # raise exceptions on 40x, 50x responses
|
@@ -35,7 +35,7 @@ module Callstacking
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def get(url, params = {})
|
38
|
+
def get(url, params = {}, headers = {})
|
39
39
|
if async
|
40
40
|
threads << Thread.new do
|
41
41
|
connection.get(url, params, headers)
|
@@ -6,6 +6,7 @@ module Callstacking
|
|
6
6
|
class Trace < Base
|
7
7
|
CREATE_URL = "/api/v1/traces.json"
|
8
8
|
UPDATE_URL = "/api/v1/traces/:id.json"
|
9
|
+
SHOW_URL = "/api/v1/traces/:id.json"
|
9
10
|
|
10
11
|
def initialize(url, auth_token)
|
11
12
|
super
|
@@ -37,6 +38,10 @@ module Callstacking
|
|
37
38
|
def upsert(trace_id, traces)
|
38
39
|
patch(UPDATE_URL.gsub(':id', trace_id), {}, traces)
|
39
40
|
end
|
41
|
+
|
42
|
+
def show(trace_id, params = {})
|
43
|
+
get(SHOW_URL.gsub(':id', trace_id), params)
|
44
|
+
end
|
40
45
|
end
|
41
46
|
end
|
42
47
|
end
|
@@ -13,46 +13,83 @@ require "callstacking/rails/client/trace"
|
|
13
13
|
require "callstacking/rails/cli"
|
14
14
|
require "callstacking/rails/time_based_uuid"
|
15
15
|
require "callstacking/rails/helpers/instrument_helper"
|
16
|
+
require "callstacking/rails/logger"
|
16
17
|
|
17
18
|
module Callstacking
|
18
19
|
module Rails
|
19
20
|
class Engine < ::Rails::Engine
|
20
|
-
EXCLUDED_TEST_CLASSES = %w[test/dummy/app/models/salutation.rb
|
21
|
+
EXCLUDED_TEST_CLASSES = %w[test/dummy/app/models/salutation.rb
|
22
|
+
test/dummy/app/controllers/application_controller.rb].freeze
|
21
23
|
|
22
|
-
cattr_accessor :spans, :
|
24
|
+
cattr_accessor :spans, :traces, :settings, :instrumenter, :loader, :lock
|
23
25
|
|
24
26
|
isolate_namespace Callstacking::Rails
|
25
27
|
|
28
|
+
@@spans||={}
|
29
|
+
@@traces||={}
|
30
|
+
@@lock||=Mutex.new
|
31
|
+
@@instrumenter||=Instrument.new
|
26
32
|
@@settings||=Callstacking::Rails::Settings.new
|
27
|
-
@@spans||=Spans.new
|
28
|
-
@@trace||=Trace.new(@@spans)
|
29
|
-
@@instrumenter||=Instrument.new(@@spans)
|
30
33
|
|
31
34
|
initializer "engine_name.assets.precompile" do |app|
|
32
35
|
app.config.assets.precompile << "checkpoint_rails_manifest.js"
|
33
36
|
end
|
34
37
|
|
35
38
|
config.after_initialize do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
Logger.log "Call Stacking loading (#{Callstacking::Rails::Env.environment})"
|
40
|
+
|
41
|
+
spans[Thread.current.object_id]||=Spans.new
|
42
|
+
instrumenter.add_span(spans[Thread.current.object_id])
|
43
|
+
|
44
|
+
@@loader = Callstacking::Rails::Loader.new(instrumenter, excluded: settings.excluded + EXCLUDED_TEST_CLASSES)
|
45
|
+
@@loader.instrument_existing
|
46
|
+
|
47
|
+
loader.on_load
|
48
|
+
# loader.reset!
|
40
49
|
end
|
41
50
|
|
51
|
+
# Serialize all tracing requests for now.
|
52
|
+
# Can enable parallel tracing later.
|
42
53
|
def self.start_tracing(controller)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
54
|
+
Logger.log("Callstacking::Rails::Engine.start_tracing")
|
55
|
+
|
56
|
+
settings.enable!
|
57
|
+
|
58
|
+
lock.synchronize do
|
59
|
+
spans[Thread.current.object_id]||=Spans.new
|
60
|
+
span = spans[Thread.current.object_id]
|
61
|
+
|
62
|
+
instrumenter.add_span(span)
|
63
|
+
|
64
|
+
if instrumenter.instrumentation_required?
|
65
|
+
loader.reset!
|
66
|
+
instrumenter.enable!(loader.klasses.to_a)
|
67
|
+
end
|
68
|
+
|
69
|
+
traces[Thread.current.object_id] = Trace.new(span)
|
70
|
+
trace = traces[Thread.current.object_id]
|
71
|
+
|
72
|
+
trace.begin_trace(controller)
|
73
|
+
end
|
47
74
|
|
48
75
|
true
|
49
76
|
end
|
50
77
|
|
51
78
|
def self.stop_tracing(controller)
|
52
|
-
|
53
|
-
|
54
|
-
|
79
|
+
Logger.log("Callstacking::Rails::Engine.stop_tracing")
|
80
|
+
|
81
|
+
settings.disable!
|
82
|
+
|
83
|
+
trace = nil
|
84
|
+
lock.synchronize do
|
85
|
+
trace = traces.delete(Thread.current.object_id)
|
86
|
+
if traces.empty?
|
87
|
+
instrumenter.disable!
|
88
|
+
end
|
89
|
+
end
|
55
90
|
|
91
|
+
trace&.end_trace(controller)
|
92
|
+
|
56
93
|
true
|
57
94
|
end
|
58
95
|
end
|
@@ -7,8 +7,8 @@ module Callstacking
|
|
7
7
|
attr_accessor :spans
|
8
8
|
attr_reader :settings, :span_modules
|
9
9
|
|
10
|
-
def initialize
|
11
|
-
@spans =
|
10
|
+
def initialize
|
11
|
+
@spans = {}
|
12
12
|
@span_modules = Set.new
|
13
13
|
@settings = Callstacking::Rails::Settings.new
|
14
14
|
end
|
@@ -34,6 +34,9 @@ module Callstacking
|
|
34
34
|
new_method = nil
|
35
35
|
if RUBY_VERSION < "2.7.8"
|
36
36
|
new_method = tmp_module.define_method(method_name) do |*args, &block|
|
37
|
+
settings = tmp_module.instance_variable_get(:@settings)
|
38
|
+
return super(*args, &block) if settings.disabled?
|
39
|
+
|
37
40
|
method_name = __method__
|
38
41
|
|
39
42
|
path = method(__method__).super_method.source_location.first
|
@@ -42,19 +45,23 @@ module Callstacking
|
|
42
45
|
p, l = caller.find { |c| c.to_s =~ /#{::Rails.root.to_s}/}&.split(':')
|
43
46
|
|
44
47
|
spans = tmp_module.instance_variable_get(:@spans)
|
48
|
+
span = spans[Thread.current.object_id]
|
45
49
|
klass = tmp_module.instance_variable_get(:@klass)
|
46
50
|
|
47
51
|
arguments = Callstacking::Rails::Instrument.arguments_for(method(__method__).super_method, args)
|
48
52
|
|
49
|
-
|
53
|
+
span.call_entry(klass, method_name, arguments, p || path, l || line_no)
|
50
54
|
return_val = super(*args, &block)
|
51
|
-
|
55
|
+
span.call_return(klass, method_name, p || path, l || line_no, return_val)
|
52
56
|
|
53
57
|
return_val
|
54
58
|
end
|
55
59
|
new_method.ruby2_keywords if new_method.respond_to?(:ruby2_keywords)
|
56
60
|
else
|
57
61
|
new_method = tmp_module.define_method(method_name) do |*args, **kwargs, &block|
|
62
|
+
settings = tmp_module.instance_variable_get(:@settings)
|
63
|
+
return super(*args, **kwargs, &block) if settings.disabled?
|
64
|
+
|
58
65
|
method_name = __method__
|
59
66
|
|
60
67
|
path = method(__method__).super_method.source_location.first
|
@@ -63,13 +70,14 @@ module Callstacking
|
|
63
70
|
p, l = caller.find { |c| c.to_s =~ /#{::Rails.root.to_s}/}&.split(':')
|
64
71
|
|
65
72
|
spans = tmp_module.instance_variable_get(:@spans)
|
73
|
+
span = spans[Thread.current.object_id]
|
66
74
|
klass = tmp_module.instance_variable_get(:@klass)
|
67
75
|
|
68
76
|
arguments = Callstacking::Rails::Instrument.arguments_for(method(__method__).super_method, args)
|
69
77
|
|
70
|
-
|
78
|
+
span.call_entry(klass, method_name, arguments, p || path, l || line_no)
|
71
79
|
return_val = super(*args, **kwargs, &block)
|
72
|
-
|
80
|
+
span.call_return(klass, method_name, p || path, l || line_no, return_val)
|
73
81
|
|
74
82
|
return_val
|
75
83
|
end
|
@@ -80,8 +88,6 @@ module Callstacking
|
|
80
88
|
end
|
81
89
|
|
82
90
|
def enable!(klasses)
|
83
|
-
reset!
|
84
|
-
|
85
91
|
Array.wrap(klasses).each do |klass|
|
86
92
|
instrument_klass(klass, application_level: true)
|
87
93
|
end
|
@@ -97,6 +103,10 @@ module Callstacking
|
|
97
103
|
reset!
|
98
104
|
end
|
99
105
|
|
106
|
+
def instrumentation_required?
|
107
|
+
span_modules.empty?
|
108
|
+
end
|
109
|
+
|
100
110
|
def reset!
|
101
111
|
span_modules.clear
|
102
112
|
end
|
@@ -120,6 +130,10 @@ module Callstacking
|
|
120
130
|
f.filter h
|
121
131
|
end
|
122
132
|
|
133
|
+
def add_span(span)
|
134
|
+
spans[Thread.current.object_id] ||= span
|
135
|
+
end
|
136
|
+
|
123
137
|
private
|
124
138
|
def find_or_initialize_module(klass)
|
125
139
|
name = klass&.name rescue nil
|
@@ -137,6 +151,7 @@ module Callstacking
|
|
137
151
|
|
138
152
|
new_module.instance_variable_set("@klass", klass)
|
139
153
|
new_module.instance_variable_set("@spans", spans)
|
154
|
+
new_module.instance_variable_set("@settings", settings)
|
140
155
|
|
141
156
|
klass.prepend new_module
|
142
157
|
klass.singleton_class.prepend new_module if klass.class == Module
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rails"
|
2
|
+
require "callstacking/rails/logger"
|
2
3
|
|
3
4
|
module Callstacking
|
4
5
|
module Rails
|
@@ -10,18 +11,16 @@ module Callstacking
|
|
10
11
|
@klasses = Set.new
|
11
12
|
end
|
12
13
|
|
14
|
+
def instrument_existing
|
15
|
+
ObjectSpace.each_object(Module){|ob| filter_klass(ob, (Object.const_source_location(ob.to_s)&.first rescue nil))}
|
16
|
+
end
|
17
|
+
|
13
18
|
def on_load
|
14
19
|
trace = TracePoint.new(:end) do |tp|
|
15
20
|
klass = tp.self
|
16
21
|
path = tp.path
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
if path =~ /#{::Rails.root.to_s}/ &&
|
21
|
-
!klasses.include?(klass) &&
|
22
|
-
!excluded_klass
|
23
|
-
klasses << klass
|
24
|
-
end
|
23
|
+
filter_klass(klass, path)
|
25
24
|
end
|
26
25
|
|
27
26
|
trace.enable
|
@@ -31,6 +30,21 @@ module Callstacking
|
|
31
30
|
instrumenter.instrument_method(ActionView::PartialRenderer, :render, application_level: false)
|
32
31
|
instrumenter.instrument_method(ActionView::TemplateRenderer, :render, application_level: false)
|
33
32
|
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def filter_klass(klass, path)
|
36
|
+
return if klass.nil? || path.nil?
|
37
|
+
return if path == false
|
38
|
+
|
39
|
+
excluded_klass = excluded.any? { |ex| path =~ /#{ex}/ }
|
40
|
+
|
41
|
+
if path =~ /#{::Rails.root.to_s}/ &&
|
42
|
+
!klasses.include?(klass) &&
|
43
|
+
!excluded_klass
|
44
|
+
instrumenter.instrument_klass(klass)
|
45
|
+
klasses << klass
|
46
|
+
end
|
47
|
+
end
|
34
48
|
end
|
35
49
|
end
|
36
50
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Callstacking
|
2
|
+
module Rails
|
3
|
+
class Logger
|
4
|
+
def self.log(message)
|
5
|
+
puts message
|
6
|
+
|
7
|
+
if ENV['GITHUB_OUTPUT'].present?
|
8
|
+
File.open(ENV['GITHUB_OUTPUT'], 'a') do |file|
|
9
|
+
# Write your progress output to the file
|
10
|
+
# This could be inside a loop or condition, depending on your needs
|
11
|
+
file.puts "::set-output name=progress_output::#{message}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -23,7 +23,9 @@ module Callstacking
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def auth_token
|
26
|
-
ENV['CALLSTACKING_API_TOKEN'] || settings[:auth_token]
|
26
|
+
x = ENV['CALLSTACKING_API_TOKEN'] || settings[:auth_token]
|
27
|
+
raise "No auth token found. #{ENV['CALLSTACKING_API_TOKEN']} Please run `callstacking login` to get one." if x.nil?
|
28
|
+
x
|
27
29
|
end
|
28
30
|
|
29
31
|
def auth_token?
|
@@ -35,14 +37,14 @@ module Callstacking
|
|
35
37
|
end
|
36
38
|
|
37
39
|
def self.enable!
|
38
|
-
|
40
|
+
Thread.current[CACHE_KEY] = true
|
39
41
|
end
|
40
42
|
def enable!
|
41
43
|
self.class.enable!
|
42
44
|
end
|
43
45
|
|
44
46
|
def self.disable!
|
45
|
-
|
47
|
+
Thread.current[CACHE_KEY] = false
|
46
48
|
end
|
47
49
|
|
48
50
|
def disable!
|
@@ -50,11 +52,9 @@ module Callstacking
|
|
50
52
|
end
|
51
53
|
|
52
54
|
def enabled?
|
53
|
-
return
|
54
|
-
return
|
55
|
-
|
56
|
-
|
57
|
-
settings[:enabled]
|
55
|
+
return Thread.current[CACHE_KEY] if Thread.current[CACHE_KEY].present?
|
56
|
+
return ActiveRecord::Type::Boolean.new.cast(ENV[ENV_KEY]) if ENV[ENV_KEY].present?
|
57
|
+
false
|
58
58
|
end
|
59
59
|
|
60
60
|
def excluded
|
@@ -7,18 +7,22 @@ module Callstacking
|
|
7
7
|
class Trace
|
8
8
|
include Callstacking::Rails::Helpers::HeadsUpDisplayHelper
|
9
9
|
|
10
|
-
attr_accessor :spans, :client, :lock
|
10
|
+
attr_accessor :spans, :client, :lock, :traces
|
11
11
|
attr_reader :settings
|
12
12
|
cattr_accessor :current_trace_id
|
13
13
|
cattr_accessor :current_tuid
|
14
|
+
cattr_accessor :trace_log
|
14
15
|
|
15
16
|
ICON = '💥'
|
16
17
|
MAX_TRACE_ENTRIES = 3000
|
18
|
+
|
19
|
+
@@trace_log||={}
|
17
20
|
|
18
21
|
def initialize(spans)
|
19
|
-
|
20
|
-
@
|
21
|
-
@
|
22
|
+
|
23
|
+
@traces = []
|
24
|
+
@spans = spans
|
25
|
+
@settings = Callstacking::Rails::Settings.new
|
22
26
|
|
23
27
|
@lock = Mutex.new
|
24
28
|
@client = Callstacking::Rails::Client::Trace.new(settings.url, settings.auth_token)
|
@@ -29,6 +33,8 @@ module Callstacking
|
|
29
33
|
|
30
34
|
def begin_trace(controller)
|
31
35
|
@trace_id, @tuid = init_uuids(controller.request&.request_id || SecureRandom.uuid, TimeBasedUUID.generate)
|
36
|
+
trace_log[@trace_id] = controller.request&.original_url
|
37
|
+
|
32
38
|
init_callbacks(@tuid)
|
33
39
|
|
34
40
|
start_request(@trace_id, @tuid,
|
@@ -50,6 +56,10 @@ module Callstacking
|
|
50
56
|
inject_hud(@settings, controller.request, controller.response)
|
51
57
|
end
|
52
58
|
|
59
|
+
def self.trace_log_clear
|
60
|
+
trace_log.clear
|
61
|
+
end
|
62
|
+
|
53
63
|
private
|
54
64
|
|
55
65
|
def init_callbacks(tuid)
|
@@ -138,6 +148,8 @@ module Callstacking
|
|
138
148
|
lock.synchronize do
|
139
149
|
return if traces.empty?
|
140
150
|
|
151
|
+
STDERR.puts "Sending #{traces.size} traces to Callstacking.io -- enabled? - #{settings.enabled?} -- #{traces.inspect}"
|
152
|
+
|
141
153
|
client.upsert(trace_id,
|
142
154
|
{ trace_entries: traces.deep_dup })
|
143
155
|
traces.clear
|
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.31
|
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-05-
|
11
|
+
date: 2023-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -112,6 +112,7 @@ files:
|
|
112
112
|
- lib/callstacking/rails/helpers/instrument_helper.rb
|
113
113
|
- lib/callstacking/rails/instrument.rb
|
114
114
|
- lib/callstacking/rails/loader.rb
|
115
|
+
- lib/callstacking/rails/logger.rb
|
115
116
|
- lib/callstacking/rails/settings.rb
|
116
117
|
- lib/callstacking/rails/setup.rb
|
117
118
|
- lib/callstacking/rails/spans.rb
|