codebeacon-tracer 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 834e2095be6b5d5a919b96bbb1395dc33bfc569cfb189d67b7e95434574c8641
4
- data.tar.gz: 1215e606cf263cc3cf77954e4f60e175bd8df43ad9adbe1ad17859bc03003c4b
3
+ metadata.gz: 47ec3d248b6f0358c3c36310a7b39c506982695565e453d11580ae20b1f28582
4
+ data.tar.gz: 8f6da9ed15ca33105c73fead83856a014c718853cbf30db5641719e5d6fdfc36
5
5
  SHA512:
6
- metadata.gz: 7f7871f11660157413fecdb33a856d9f06465690570226b201e9c5ff2a31dd3a6dbea826b2c4a0d4729a15ebadfab4d07357e9ff8978f76314ed43ef63844471
7
- data.tar.gz: c2d72f58559f32136268687dfaea900344a4141d6beb4f252821f9cb838e7e2b296cd304c0a9e51387589611068dff1e34f9a88139c56c5a85ebe2022389cb2a
6
+ metadata.gz: 69b782857204580baf4b8251f478d0e16652d015fcbf5f79c65a47f691f9623a52ee9271c973d5539ed4d680d4c37c21f9571299225ce16ecab446e9bbd43ffd
7
+ data.tar.gz: 27dd8bdcb652b7db338e0ac8aba0862b6cb3106c00a76524265cec238714849d046c416c43c62448aa3ca4ca84be19a3c0651b53b347cdd79b9561fd3b7dca99
@@ -14,6 +14,7 @@ module Codebeacon
14
14
  RETURN_VAL_MAX_LENGTH = 1000
15
15
  MAX_CALL_COUNT = 100000000
16
16
  MAX_DEPTH = 99999
17
+ SERIALIZATION_TIMEOUT_MS = 10
17
18
 
18
19
  def initialize()
19
20
  @query = ""
@@ -69,7 +70,7 @@ module Codebeacon
69
70
  end
70
71
 
71
72
  def data_dir
72
- ".code-beacon"
73
+ File.join(root_path, ".code-beacon")
73
74
  end
74
75
 
75
76
  def db_path
@@ -115,7 +116,7 @@ module Codebeacon
115
116
  end
116
117
 
117
118
  def root_path
118
- @root_path ||= defined?(Rails) ? Rails.root.to_s : Dir.pwd
119
+ @root_path ||= @config_root_path || (defined?(Rails) ? Rails.root.to_s : Dir.pwd)
119
120
  end
120
121
 
121
122
  def rubylib_path
@@ -145,9 +146,15 @@ module Codebeacon
145
146
 
146
147
  def reload_tracer_config
147
148
  @trace_enabled = load_tracer_config_enabled
149
+ @config_root_path = load_tracer_config_root_path
148
150
  @recording_meta_exclude_patterns = nil # Force reload of exclusion patterns
149
151
  end
150
152
 
153
+ def reload_main_config
154
+ @root_path = nil # Force reload of root_path
155
+ load_main_config
156
+ end
157
+
151
158
  def recording_meta_exclude_patterns
152
159
  if @donotcache
153
160
  load_recording_meta_exclude_patterns
@@ -229,6 +236,10 @@ module Codebeacon
229
236
  MAX_DEPTH
230
237
  end
231
238
 
239
+ def serialization_timeout_ms
240
+ SERIALIZATION_TIMEOUT_MS
241
+ end
242
+
232
243
  def logger
233
244
  @logger ||= Codebeacon::Tracer::Logger.new()
234
245
  end
@@ -301,6 +312,15 @@ module Codebeacon
301
312
  logger.warn("Error loading tracer config: #{e.message}")
302
313
  true
303
314
  end
315
+
316
+ def load_tracer_config_root_path
317
+ if File.exist?(tracer_config_path)
318
+ config_data = YAML.load_file(tracer_config_path)
319
+ @config_root_path = config_data['root_path'] if config_data['root_path']
320
+ end
321
+ rescue => e
322
+ logger.warn("Error loading tracer config root_path: #{e.message}")
323
+ end
304
324
  end
305
325
  end
306
326
  end
@@ -19,7 +19,8 @@ module Codebeacon
19
19
  start_time TEXT,
20
20
  end_time TEXT,
21
21
  duration_ms REAL,
22
- trigger_type TEXT
22
+ trigger_type TEXT,
23
+ language TEXT
23
24
  );
24
25
  SQL
25
26
  end
@@ -34,9 +35,9 @@ module Codebeacon
34
35
  INSERT INTO metadata (
35
36
  name, description, caller_file, caller_method, caller_line,
36
37
  caller_class, caller_defined_class, start_time, end_time,
37
- duration_ms, trigger_type
38
+ duration_ms, trigger_type, language
38
39
  ) VALUES (
39
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
40
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
40
41
  )
41
42
  SQL
42
43
  metadata_hash[:name],
@@ -49,7 +50,8 @@ module Codebeacon
49
50
  metadata_hash[:start_time]&.iso8601,
50
51
  metadata_hash[:end_time]&.iso8601,
51
52
  metadata_hash[:duration_ms],
52
- metadata_hash[:trigger_type]
53
+ metadata_hash[:trigger_type],
54
+ metadata_hash[:language]
53
55
  )
54
56
  end
55
57
  end
@@ -1,27 +1,13 @@
1
1
  require_relative 'tree_node_mapper'
2
2
  require_relative 'node_source_mapper'
3
3
  require_relative 'metadata_mapper'
4
+ require_relative 'type_detector'
5
+ require_relative 'safe_serializer'
4
6
 
5
7
  module Codebeacon
6
8
  module Tracer
7
9
  class PersistenceManager
8
10
 
9
- def self.marshal(name, value, tree_node)
10
- begin
11
- return value.inspect[0..Codebeacon::Tracer.config.max_value_length]
12
- rescue => e
13
- begin
14
- if Codebeacon::Tracer.config.debug?
15
- Codebeacon::Tracer.logger.warn "Marshal inspect failure - attempting to_s fallback for: \"#{name}\", located at: \"#{tree_node.file}:#{tree_node.line}\"\nerror message: \"#{e.message}\", error_location: \"#{e.backtrace[0]}\""
16
- end
17
- return value.to_s[0..Codebeacon::Tracer.config.max_value_length]
18
- rescue => e
19
- Codebeacon::Tracer.logger.error "Marshal failure for: \"#{name}\", located at: \"#{tree_node.file}:#{tree_node.line}\"\nerror message: \"#{e.message}\", error_location: \"#{e.backtrace[0]}\""
20
- return "--Codebeacon::Tracer ERROR-- could not marshall value. See logs."
21
- end
22
- end
23
- end
24
-
25
11
  def initialize(database)
26
12
  @database = database
27
13
  @tree_node_mapper = TreeNodeMapper.new(database)
@@ -69,6 +55,7 @@ module Codebeacon
69
55
  node_id = @tree_node_mapper.insert(
70
56
  tree_node.file,
71
57
  tree_node.line,
58
+ tree_node.called_method&.to_s, # Convert symbols to strings, keep nil as nil
72
59
  tree_node.method.to_s,
73
60
  tree_node.tp_class.to_s,
74
61
  tree_node.tp_defined_class.to_s,
@@ -80,7 +67,8 @@ module Codebeacon
80
67
  parent_id,
81
68
  tree_node.block,
82
69
  tree_node.node_source&.id,
83
- _return_value(tree_node)
70
+ return_type(tree_node),
71
+ return_value(tree_node)
84
72
  )
85
73
 
86
74
  unless tree_node.depth_truncated?
@@ -95,12 +83,14 @@ module Codebeacon
95
83
  end
96
84
  end
97
85
 
98
- def _return_value(node)
99
- if node.method == :initialize
100
- return nil
101
- else
102
- PersistenceManager.marshal(node.method, node.return_value, node)
103
- end
86
+ private def return_type(node)
87
+ return nil if node.method == :initialize
88
+ node.return_value.class.name
89
+ end
90
+
91
+ private def return_value(node)
92
+ return nil if node.method == :initialize
93
+ SafeSerializer.serialize(node.return_value, Codebeacon::Tracer.config.max_value_length)
104
94
  end
105
95
  end
106
96
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'type_detector'
4
+ require 'timeout'
5
+
6
+ module Codebeacon
7
+ module Tracer
8
+ class SafeSerializer
9
+ def self.serialize(value, max_length)
10
+ return nil unless TypeDetector.serializable_type?(value)
11
+ return "..." if max_length <= 0
12
+
13
+ timeout_ms = Codebeacon::Tracer.config.serialization_timeout_ms
14
+
15
+ begin
16
+ Timeout.timeout(timeout_ms / 1000.0) do
17
+ serialized = case value
18
+ when String
19
+ value
20
+ when Integer, Float
21
+ value.to_s
22
+ when Symbol, TrueClass, FalseClass
23
+ value.to_s
24
+ when NilClass
25
+ "nil"
26
+ else
27
+ nil
28
+ end
29
+
30
+ return nil if serialized.nil?
31
+
32
+ serialized.length <= max_length ? serialized : serialized[0...max_length] + "..."
33
+ end
34
+ rescue Timeout::Error
35
+ return "(serialization timeout)"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -3,7 +3,7 @@ module Codebeacon
3
3
  class TraceMetadata
4
4
  attr_reader :name, :description, :caller_file, :caller_method, :caller_line,
5
5
  :caller_class, :caller_defined_class, :start_time,
6
- :end_time, :duration_ms, :trigger_type
6
+ :end_time, :duration_ms, :trigger_type, :language
7
7
 
8
8
  def initialize(name: nil, description: nil, caller_location: nil, trigger_type: nil)
9
9
  @name = name
@@ -12,6 +12,7 @@ module Codebeacon
12
12
  @end_time = nil
13
13
  @duration_ms = nil
14
14
  @trigger_type = trigger_type
15
+ @language = "ruby"
15
16
 
16
17
  if caller_location
17
18
  capture_caller_info_from_location(caller_location)
@@ -35,7 +36,8 @@ module Codebeacon
35
36
  start_time: @start_time,
36
37
  end_time: @end_time,
37
38
  duration_ms: @duration_ms,
38
- trigger_type: @trigger_type
39
+ trigger_type: @trigger_type,
40
+ language: @language
39
41
  }
40
42
  end
41
43
 
@@ -8,20 +8,20 @@ module Codebeacon
8
8
  @db = database
9
9
  end
10
10
 
11
- def insert(file, line, method, tp_class, tp_defined_class, tp_class_name, self_type, depth, caller, gem_entry, parent_id, block, node_source_id, return_value)
11
+ def insert(file, line, called_method, method, tp_class, tp_defined_class, tp_class_name, self_type, depth, caller, gem_entry, parent_id, block, node_source_id, return_type, return_value)
12
12
  @db.execute(<<-SQL,
13
13
  INSERT INTO treenodes
14
14
  (
15
- file, line, method, tp_class, tp_defined_class, tp_class_name, self_type, depth, caller,
16
- gemEntry, parent_id, block, node_source_id, return_value
15
+ file, line, called_method, method, tp_class, tp_defined_class, tp_class_name, self_type, depth, caller,
16
+ gemEntry, parent_id, block, node_source_id, return_type, return_value
17
17
  )
18
18
  VALUES
19
19
  (
20
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
20
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
21
21
  )
22
22
  SQL
23
- file, line, method, tp_class, tp_defined_class, tp_class_name, self_type, depth, caller,
24
- gem_entry ? 1 : 0, parent_id, block ? 1 : 0, node_source_id, return_value)
23
+ file, line, called_method, method, tp_class, tp_defined_class, tp_class_name, self_type, depth, caller,
24
+ gem_entry ? 1 : 0, parent_id, block ? 1 : 0, node_source_id, return_type, return_value)
25
25
 
26
26
  @db.last_insert_row_id
27
27
  end
@@ -32,6 +32,7 @@ module Codebeacon
32
32
  id INTEGER PRIMARY KEY,
33
33
  file TEXT,
34
34
  line INTEGER,
35
+ called_method TEXT,
35
36
  method TEXT,
36
37
  tp_class TEXT,
37
38
  tp_defined_class TEXT,
@@ -43,6 +44,7 @@ module Codebeacon
43
44
  parent_id INTEGER,
44
45
  block INTEGER,
45
46
  node_source_id INTEGER,
47
+ return_type TEXT,
46
48
  return_value TEXT,
47
49
  FOREIGN KEY (parent_id) REFERENCES treenodes(id),
48
50
  FOREIGN KEY (node_source_id) REFERENCES node_sources(id)
@@ -54,6 +56,7 @@ module Codebeacon
54
56
  database.execute("CREATE INDEX IF NOT EXISTS IDX_treenode_parent_id ON treenodes(parent_id)")
55
57
  database.execute("CREATE INDEX IF NOT EXISTS IDX_treenode_node_source_id ON treenodes(node_source_id)")
56
58
  database.execute("CREATE INDEX IF NOT EXISTS IDX_treenode_file ON treenodes(file)")
59
+ database.execute("CREATE INDEX IF NOT EXISTS IDX_treenode_called_method ON treenodes(called_method)")
57
60
  end
58
61
  end
59
62
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebeacon
4
+ module Tracer
5
+ class TypeDetector
6
+ BASIC_TYPES = [
7
+ String, Integer, Float, Symbol, TrueClass, FalseClass, NilClass
8
+ ].freeze
9
+
10
+ def self.basic_type?(value)
11
+ return true if value.nil?
12
+ BASIC_TYPES.any? { |type| value.is_a?(type) }
13
+ end
14
+
15
+ def self.serializable_type?(value)
16
+ basic_type?(value)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -37,6 +37,10 @@ module Codebeacon
37
37
  current_context.line = tp.lineno
38
38
  current_context.object_id = tp.self.object_id
39
39
  current_context.method = tp.method_id
40
+ if tp.callee_id != tp.method_id
41
+ current_context.called_method = tp.callee_id
42
+ end
43
+
40
44
  klass = TPKlass.new(tp)
41
45
 
42
46
  current_context.tp_class = klass.tp_class.to_s
@@ -31,9 +31,9 @@ module Codebeacon
31
31
  # @children = []
32
32
  # end
33
33
 
34
- attr_accessor :file, :line, :method, :object_id, :tp_class, :tp_defined_class, :tp_class_name, :self_type, :depth, :caller, :gem_entry, :children, :parent, :block, :locals, :return_value, :linevars, :node_source, :trace_status, :script, :backtrace_count, :backtrace_location, :script_binding, :script_self
34
+ attr_accessor :file, :line, :method, :object_id, :tp_class, :tp_defined_class, :tp_class_name, :self_type, :depth, :caller, :gem_entry, :children, :parent, :block, :locals, :return_value, :linevars, :node_source, :trace_status, :script, :backtrace_count, :backtrace_location, :script_binding, :script_self, :called_method
35
35
 
36
- def initialize(file: nil, line: nil, object_id: nil, method: nil, tp_class: nil, tp_defined_class: nil, tp_class_name: nil, self_type: nil, depth: 0, caller: "", gem_entry: false, parent: nil, block: false, locals: [], return_value: nil, node_source: nil, script: false)
36
+ def initialize(file: nil, line: nil, object_id: nil, method: nil, tp_class: nil, tp_defined_class: nil, tp_class_name: nil, self_type: nil, depth: 0, caller: "", gem_entry: false, parent: nil, block: false, locals: [], return_value: nil, node_source: nil, script: false, called_method: nil)
37
37
  @file = file
38
38
  @line = line
39
39
  @method = method
@@ -58,6 +58,7 @@ module Codebeacon
58
58
  @backtrace_location = nil
59
59
  @script_binding = nil
60
60
  @script_self = nil
61
+ @called_method = called_method
61
62
  end
62
63
 
63
64
  def add_line(lineno, variables)
@@ -73,7 +73,7 @@ module Codebeacon
73
73
 
74
74
  def trace_call
75
75
  trace(:call) do |tp|
76
- NodeBuilder.trace_method_call(call_tree, tp, Kernel.caller[2..])
76
+ NodeBuilder.trace_method_call(call_tree, tp, "")
77
77
  ensure
78
78
  @progress_logger.increment()
79
79
  end
@@ -81,7 +81,7 @@ module Codebeacon
81
81
 
82
82
  def trace_b_call
83
83
  trace(:b_call) do |tp|
84
- NodeBuilder.trace_block_call(call_tree, tp, Kernel.caller[2..])
84
+ NodeBuilder.trace_block_call(call_tree, tp, "")
85
85
  ensure
86
86
  @progress_logger.increment()
87
87
  end
@@ -101,24 +101,16 @@ module Codebeacon
101
101
 
102
102
  def trace(type)
103
103
  TracePoint.new(type) do |tp|
104
- paths = [tp.path]
105
- # capture calls and returns to skipped paths from non skipped paths. All I need is the return value to display in recorded files, but the code doesn't yet support this without tracing the entire call and return.
106
- if [:call, :b_call, :return, :b_return].include?(type)
107
- paths << Kernel.caller(1..1)[0]
108
- end
109
- paths.uniq!
110
- next if skip_methods?(paths)
104
+ next if skip_methods?(tp.path)
111
105
  yield tp
112
106
  rescue => e
113
107
  Codebeacon::Tracer.logger.error("TracePoint(#{type}) #{tp.path} #{e.message}")
114
108
  end
115
109
  end
116
110
 
117
- def skip_methods?(paths)
118
- paths.all? do |path|
119
- path.nil? || Codebeacon::Tracer.config.exclude_paths.any?{ |exclude_path| path.start_with?(exclude_path) } ||
120
- Codebeacon::Tracer.config.local_methods_only? && !path.start_with?(Codebeacon::Tracer.config.root_path)
121
- end
111
+ def skip_methods?(path)
112
+ path.nil? || Codebeacon::Tracer.config.exclude_paths.any?{ |exclude_path| path.start_with?(exclude_path) } ||
113
+ Codebeacon::Tracer.config.local_methods_only? && !path.start_with?(Codebeacon::Tracer.config.root_path)
122
114
  end
123
115
  end
124
116
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Codebeacon
4
4
  module Tracer
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: codebeacon-tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Conley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-16 00:00:00.000000000 Z
11
+ date: 2025-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -172,8 +172,10 @@ files:
172
172
  - lib/codebeacon/tracer/src/data/metadata_mapper.rb
173
173
  - lib/codebeacon/tracer/src/data/node_source_mapper.rb
174
174
  - lib/codebeacon/tracer/src/data/persistence_manager.rb
175
+ - lib/codebeacon/tracer/src/data/safe_serializer.rb
175
176
  - lib/codebeacon/tracer/src/data/trace_metadata.rb
176
177
  - lib/codebeacon/tracer/src/data/tree_node_mapper.rb
178
+ - lib/codebeacon/tracer/src/data/type_detector.rb
177
179
  - lib/codebeacon/tracer/src/logger.rb
178
180
  - lib/codebeacon/tracer/src/models/call_tree.rb
179
181
  - lib/codebeacon/tracer/src/models/node_builder.rb