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 +4 -4
- data/lib/codebeacon/tracer/src/configuration.rb +22 -2
- data/lib/codebeacon/tracer/src/data/metadata_mapper.rb +6 -4
- data/lib/codebeacon/tracer/src/data/persistence_manager.rb +13 -23
- data/lib/codebeacon/tracer/src/data/safe_serializer.rb +40 -0
- data/lib/codebeacon/tracer/src/data/trace_metadata.rb +4 -2
- data/lib/codebeacon/tracer/src/data/tree_node_mapper.rb +9 -6
- data/lib/codebeacon/tracer/src/data/type_detector.rb +20 -0
- data/lib/codebeacon/tracer/src/models/node_builder.rb +4 -0
- data/lib/codebeacon/tracer/src/models/tree_node.rb +3 -2
- data/lib/codebeacon/tracer/src/tracer.rb +6 -14
- data/lib/codebeacon/tracer/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47ec3d248b6f0358c3c36310a7b39c506982695565e453d11580ae20b1f28582
|
4
|
+
data.tar.gz: 8f6da9ed15ca33105c73fead83856a014c718853cbf30db5641719e5d6fdfc36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
99
|
-
if node.method == :initialize
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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,
|
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,
|
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
|
-
|
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?(
|
118
|
-
|
119
|
-
|
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
|
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.
|
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-
|
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
|