callstacking-rails 0.1.30 → 0.1.32

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7423b24771b1ee8ad73efbd79f287546e2b92876743424250064541c0a91eb0e
4
- data.tar.gz: 79ef13978d8252f26950486c3589b7d8ad2d5414a35683ba95ea0768b6187e52
3
+ metadata.gz: 002a4d9f1b5077534ba5ea5dfc3b418fa77325a4028cd4ed9322803cb0200fb5
4
+ data.tar.gz: e9eb4f9d680d978e66f38f754b8c8e251e243628b8317dfa004df95545661048
5
5
  SHA512:
6
- metadata.gz: 52d1c88ad8385cf8f9f228672927fb8bcc3397fd4799e8dd3987885fe68982bbb6a45e32b7bad7ebae8aa02f9362e727636dc3c23f2b98a04427a1662023b8d5
7
- data.tar.gz: 6f4ef1b35a429613b39fc898edb8e390711895134a0fddf295a988e9e738a182423fdfb923ae17008af36190086e7ef56189d597dae54def16ff913edcee7dd6
6
+ metadata.gz: 70c5b03becd0e1e054a5d7142130dfce3ae8e7310a9a249ea6742a95f45b5f3f52a8f59832820c1cd981fbd556ede7f2b672de1ad56cc61e16a978a216fd0b55
7
+ data.tar.gz: 54719e4784c2c945e05eafdbc202de21c4bf425a5d0a4ce1c9403e9154851ba18a63883e2bf32712d23342cb5f9c0257d65bfd02630b9cb6bd4b58f878479baf
@@ -21,8 +21,6 @@ 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, Logger.new('/tmp/callstacking-rails.log')
25
- # c.use Faraday::Response::Logger, nil, { headers: false, bodies: false }
26
24
  c.response :follow_redirects
27
25
  c.use Faraday::Response::RaiseError # raise exceptions on 40x, 50x responses
28
26
  c.request :json # This will set the "Content-Type" header to application/json and call .to_json on the body
@@ -35,7 +33,7 @@ module Callstacking
35
33
  end
36
34
  end
37
35
 
38
- def get(url, params = {})
36
+ def get(url, params = {}, headers = {})
39
37
  if async
40
38
  threads << Thread.new do
41
39
  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,81 @@ 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 test/dummy/app/controllers/application_controller.rb].freeze
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, :trace, :settings, :instrumenter, :loader
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
- puts "Call Stacking loading (#{Callstacking::Rails::Env.environment})"
37
-
38
- @@loader = Callstacking::Rails::Loader.new(@@instrumenter, excluded: @@settings.excluded + EXCLUDED_TEST_CLASSES)
39
- @@loader.on_load
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,
45
+ excluded: settings.excluded + EXCLUDED_TEST_CLASSES)
46
+ loader.on_load
40
47
  end
41
48
 
49
+ # Serialize all tracing requests for now.
50
+ # Can enable parallel tracing later.
42
51
  def self.start_tracing(controller)
43
- @@settings.enable!
44
- @@loader.reset!
45
- @@instrumenter.enable!(@@loader.klasses.to_a)
46
- @@trace.begin_trace(controller)
52
+ Logger.log("Callstacking::Rails::Engine.start_tracing")
53
+
54
+ settings.enable!
55
+
56
+ lock.synchronize do
57
+ spans[Thread.current.object_id]||=Spans.new
58
+ span = spans[Thread.current.object_id]
59
+
60
+ instrumenter.add_span(span)
61
+
62
+ if instrumenter.instrumentation_required?
63
+ loader.reset!
64
+ instrumenter.enable!(loader.klasses.to_a)
65
+ end
66
+
67
+ traces[Thread.current.object_id] = Trace.new(span)
68
+ trace = traces[Thread.current.object_id]
69
+
70
+ trace.begin_trace(controller)
71
+ end
47
72
 
48
73
  true
49
74
  end
50
75
 
51
76
  def self.stop_tracing(controller)
52
- @@settings.disable!
53
- @@instrumenter.disable!
54
- @@trace.end_trace(controller)
77
+ Logger.log("Callstacking::Rails::Engine.stop_tracing")
78
+
79
+ settings.disable!
80
+
81
+ trace = nil
82
+ lock.synchronize do
83
+ trace = traces.delete(Thread.current.object_id)
84
+ if traces.empty?
85
+ instrumenter.disable!
86
+ end
87
+ end
55
88
 
89
+ trace&.end_trace(controller)
90
+
56
91
  true
57
92
  end
58
93
  end
@@ -7,8 +7,8 @@ module Callstacking
7
7
  attr_accessor :spans
8
8
  attr_reader :settings, :span_modules
9
9
 
10
- def initialize(spans)
11
- @spans = 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
- spans.call_entry(klass, method_name, arguments, p || path, l || line_no)
53
+ span.call_entry(klass, method_name, arguments, p || path, l || line_no)
50
54
  return_val = super(*args, &block)
51
- spans.call_return(klass, method_name, p || path, l || line_no, return_val)
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
- spans.call_entry(klass, method_name, arguments, p || path, l || line_no)
78
+ span.call_entry(klass, method_name, arguments, p || path, l || line_no)
71
79
  return_val = super(*args, **kwargs, &block)
72
- spans.call_return(klass, method_name, p || path, l || line_no, return_val)
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,13 +1,21 @@
1
1
  require "rails"
2
+ require "callstacking/rails/logger"
2
3
 
3
4
  module Callstacking
4
5
  module Rails
5
6
  class Loader
6
- attr_accessor :instrumenter, :klasses, :excluded
7
+ attr_accessor :instrumenter, :klasses, :excluded, :settings
7
8
  def initialize(instrumenter, excluded: [])
8
9
  @excluded = excluded
9
10
  @instrumenter = instrumenter
10
11
  @klasses = Set.new
12
+ @settings = Callstacking::Rails::Settings.new
13
+
14
+ preloaded_klasses
15
+ end
16
+
17
+ def preloaded_klasses
18
+ ObjectSpace.each_object(Module){|ob| filter_klass(ob, (Object.const_source_location(ob.to_s)&.first rescue nil))}
11
19
  end
12
20
 
13
21
  def on_load
@@ -15,13 +23,7 @@ module Callstacking
15
23
  klass = tp.self
16
24
  path = tp.path
17
25
 
18
- excluded_klass = excluded.any? { |ex| path =~ /#{ex}/ }
19
-
20
- if path =~ /#{::Rails.root.to_s}/ &&
21
- !klasses.include?(klass) &&
22
- !excluded_klass
23
- klasses << klass
24
- end
26
+ filter_klass(klass, path)
25
27
  end
26
28
 
27
29
  trace.enable
@@ -31,6 +33,21 @@ module Callstacking
31
33
  instrumenter.instrument_method(ActionView::PartialRenderer, :render, application_level: false)
32
34
  instrumenter.instrument_method(ActionView::TemplateRenderer, :render, application_level: false)
33
35
  end
36
+
37
+ private
38
+ def filter_klass(klass, path)
39
+ return if klass.nil? || path.nil?
40
+ return if path == false
41
+
42
+ excluded_klass = excluded.any? { |ex| path =~ /#{ex}/ }
43
+
44
+ if path =~ /#{::Rails.root.to_s}/ &&
45
+ !klasses.include?(klass) &&
46
+ !excluded_klass
47
+ instrumenter.instrument_klass(klass) if settings.enabled?
48
+ klasses << klass
49
+ end
50
+ end
34
51
  end
35
52
  end
36
53
  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
+
@@ -35,14 +35,14 @@ module Callstacking
35
35
  end
36
36
 
37
37
  def self.enable!
38
- ::Rails.cache.write(CACHE_KEY, true)
38
+ Thread.current[CACHE_KEY] = true
39
39
  end
40
40
  def enable!
41
41
  self.class.enable!
42
42
  end
43
43
 
44
44
  def self.disable!
45
- ::Rails.cache.write(CACHE_KEY, false)
45
+ Thread.current[CACHE_KEY] = false
46
46
  end
47
47
 
48
48
  def disable!
@@ -50,11 +50,9 @@ module Callstacking
50
50
  end
51
51
 
52
52
  def enabled?
53
- return ::Rails.cache.read(CACHE_KEY) unless ::Rails.cache.read(CACHE_KEY).nil?
54
- return false if ENV[ENV_KEY] == 'false'
55
- return false if settings.nil?
56
-
57
- settings[:enabled]
53
+ return ActiveRecord::Type::Boolean.new.cast(ENV[ENV_KEY]) if ENV[ENV_KEY].present?
54
+ return Thread.current[CACHE_KEY] if Thread.current[CACHE_KEY].present?
55
+ false
58
56
  end
59
57
 
60
58
  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
- @traces = []
20
- @spans = spans
21
- @settings = Callstacking::Rails::Settings.new
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)
@@ -1,5 +1,5 @@
1
1
  module Callstacking
2
2
  module Rails
3
- VERSION = "0.1.30"
3
+ VERSION = "0.1.32"
4
4
  end
5
5
  end
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.30
4
+ version: 0.1.32
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-08 00:00:00.000000000 Z
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