bitfab 0.51.2 → 0.51.4
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/README.md +49 -0
- data/lib/bitfab/client.rb +117 -21
- data/lib/bitfab/datasets.rb +23 -5
- data/lib/bitfab/span_context.rb +6 -2
- data/lib/bitfab/subtree.rb +774 -0
- data/lib/bitfab/traceable.rb +253 -6
- data/lib/bitfab/version.rb +1 -1
- metadata +2 -1
data/lib/bitfab/traceable.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "subtree"
|
|
4
|
+
|
|
3
5
|
module Bitfab
|
|
4
6
|
# Mixin for declarative span tracing on instance methods.
|
|
5
7
|
#
|
|
@@ -67,7 +69,9 @@ module Bitfab
|
|
|
67
69
|
args:,
|
|
68
70
|
kwargs:,
|
|
69
71
|
capture_when:,
|
|
70
|
-
mock_on_replay
|
|
72
|
+
mock_on_replay:,
|
|
73
|
+
explicit_span_receiver: self,
|
|
74
|
+
explicit_span_method_name: method_name) do
|
|
71
75
|
super(*args, **kwargs, &block)
|
|
72
76
|
end
|
|
73
77
|
end
|
|
@@ -78,6 +82,137 @@ module Bitfab
|
|
|
78
82
|
klass.prepend(wrapper)
|
|
79
83
|
end
|
|
80
84
|
|
|
85
|
+
# Wrap an existing method with experimental whole-subtree tracing.
|
|
86
|
+
# The root records with the requested type; first-party Ruby methods called
|
|
87
|
+
# beneath it record automatically as function spans.
|
|
88
|
+
def self.trace(klass, method_name, trace_function_key:, name: nil, type: "custom",
|
|
89
|
+
max_depth: Subtree::DEFAULT_MAX_DEPTH, max_spans: Subtree::DEFAULT_MAX_SPANS,
|
|
90
|
+
exclude: [], include_wrappers: false, client: nil)
|
|
91
|
+
source = source_of(klass, method_name)
|
|
92
|
+
capture_config = Subtree.prepare(root_source: source, exclude:)
|
|
93
|
+
span_name = name || method_name.to_s
|
|
94
|
+
method_name_str = method_name.to_s
|
|
95
|
+
bound_client = client
|
|
96
|
+
|
|
97
|
+
wrapper = Module.new do
|
|
98
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
99
|
+
if Subtree.active_trace?(trace_function_key)
|
|
100
|
+
next super(*args, **kwargs, &block)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
target_client = bound_client || Bitfab.client
|
|
104
|
+
target_client.send(
|
|
105
|
+
:execute_span,
|
|
106
|
+
trace_function_key:,
|
|
107
|
+
span_name:,
|
|
108
|
+
span_type: type,
|
|
109
|
+
function_name: method_name_str,
|
|
110
|
+
args:,
|
|
111
|
+
kwargs:,
|
|
112
|
+
independent_root: true
|
|
113
|
+
) do
|
|
114
|
+
Subtree.capture(
|
|
115
|
+
client: target_client,
|
|
116
|
+
trace_function_key:,
|
|
117
|
+
config: capture_config,
|
|
118
|
+
max_depth:,
|
|
119
|
+
max_spans:,
|
|
120
|
+
include_wrappers:,
|
|
121
|
+
root_receiver: self,
|
|
122
|
+
root_method_name: method_name
|
|
123
|
+
) { super(*args, **kwargs, &block) }
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
wrapper.instance_variable_set(:@bitfab_span_method, method_name_str)
|
|
129
|
+
wrapper.instance_variable_set(:@bitfab_span_key, trace_function_key)
|
|
130
|
+
preserve_visibility(klass, wrapper, method_name)
|
|
131
|
+
klass.prepend(wrapper)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Configure an existing method only when it is discovered beneath a
|
|
135
|
+
# bitfab_trace root. Outside subtree capture the method runs unchanged.
|
|
136
|
+
def self.node(klass, method_name, name: nil, type: "custom", capture: true,
|
|
137
|
+
test_run_id: nil, mock_on_replay: false, finalize: nil, client: nil)
|
|
138
|
+
validate_node_options!(capture:, mock_on_replay:)
|
|
139
|
+
span_name = name || method_name.to_s
|
|
140
|
+
method_name_str = method_name.to_s
|
|
141
|
+
bound_client = client
|
|
142
|
+
|
|
143
|
+
wrapper = Module.new do
|
|
144
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
145
|
+
session = Subtree.current_session
|
|
146
|
+
if !session || (bound_client && !session.client.equal?(bound_client))
|
|
147
|
+
next super(*args, **kwargs, &block)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
unless capture
|
|
151
|
+
next Subtree.with_suppressed_node(receiver: self, method_name:) do
|
|
152
|
+
super(*args, **kwargs, &block)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if session.excluded?(method_name)
|
|
157
|
+
next Subtree.with_suppressed_node(receiver: self, method_name:, sessions: [session]) do
|
|
158
|
+
super(*args, **kwargs, &block)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
unless session.reserve_node
|
|
163
|
+
next Subtree.with_suppressed_node(receiver: self, method_name:, sessions: [session]) do
|
|
164
|
+
super(*args, **kwargs, &block)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
session.client.send(
|
|
169
|
+
:execute_span,
|
|
170
|
+
trace_function_key: session.trace_function_key,
|
|
171
|
+
span_name:,
|
|
172
|
+
span_type: type,
|
|
173
|
+
function_name: method_name_str,
|
|
174
|
+
args:,
|
|
175
|
+
kwargs:,
|
|
176
|
+
capture_when: "nested",
|
|
177
|
+
mock_on_replay:,
|
|
178
|
+
explicit_span_receiver: self,
|
|
179
|
+
explicit_span_method_name: method_name,
|
|
180
|
+
test_run_id:,
|
|
181
|
+
finalize:
|
|
182
|
+
) { super(*args, **kwargs, &block) }
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
preserve_visibility(klass, wrapper, method_name)
|
|
187
|
+
klass.prepend(wrapper)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def self.source_of(klass, method_name)
|
|
191
|
+
path, line = klass.instance_method(method_name).source_location || [nil, nil]
|
|
192
|
+
[path, line, method_name]
|
|
193
|
+
end
|
|
194
|
+
private_class_method :source_of
|
|
195
|
+
|
|
196
|
+
def self.preserve_visibility(klass, wrapper, method_name)
|
|
197
|
+
visibility = if klass.private_method_defined?(method_name)
|
|
198
|
+
:private
|
|
199
|
+
elsif klass.protected_method_defined?(method_name)
|
|
200
|
+
:protected
|
|
201
|
+
else
|
|
202
|
+
:public
|
|
203
|
+
end
|
|
204
|
+
wrapper.send(visibility, method_name)
|
|
205
|
+
end
|
|
206
|
+
private_class_method :preserve_visibility
|
|
207
|
+
|
|
208
|
+
def self.validate_node_options!(capture:, mock_on_replay:)
|
|
209
|
+
return unless !capture && mock_on_replay
|
|
210
|
+
|
|
211
|
+
raise ArgumentError,
|
|
212
|
+
"bitfab_node(capture: false) cannot use mock_on_replay: true because an uncaptured node has no recorded output."
|
|
213
|
+
end
|
|
214
|
+
private_class_method :validate_node_options!
|
|
215
|
+
|
|
81
216
|
# Resolve the trace function key a traced method records spans under, or
|
|
82
217
|
# nil if the method carries no Bitfab span wrapper. Walks the receiver's
|
|
83
218
|
# ancestor chain for the wrapper module that bitfab_span / Traceable.wrap
|
|
@@ -168,14 +303,62 @@ module Bitfab
|
|
|
168
303
|
end
|
|
169
304
|
end
|
|
170
305
|
|
|
306
|
+
# EXPERIMENTAL. Trace a method and every first-party Ruby method it calls.
|
|
307
|
+
# Descendants require no Bitfab declarations and record as function spans.
|
|
308
|
+
def bitfab_trace(method_name, trace_function_key: nil, name: nil, type: "custom",
|
|
309
|
+
max_depth: Subtree::DEFAULT_MAX_DEPTH, max_spans: Subtree::DEFAULT_MAX_SPANS,
|
|
310
|
+
exclude: [], include_wrappers: false)
|
|
311
|
+
trace_function_key ||= @bitfab_function_key
|
|
312
|
+
unless trace_function_key
|
|
313
|
+
raise "No trace function key provided. Pass `trace_function_key:` to `bitfab_trace` " \
|
|
314
|
+
"or call `bitfab_function 'my-key'` before using `bitfab_trace`."
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
config = {
|
|
318
|
+
trace_function_key:,
|
|
319
|
+
name:,
|
|
320
|
+
type:,
|
|
321
|
+
max_depth:,
|
|
322
|
+
max_spans:,
|
|
323
|
+
exclude:,
|
|
324
|
+
include_wrappers:
|
|
325
|
+
}
|
|
326
|
+
if method_defined?(method_name) || private_method_defined?(method_name)
|
|
327
|
+
_bitfab_trace_method(method_name, **config)
|
|
328
|
+
else
|
|
329
|
+
@_bitfab_pending_traces ||= {}
|
|
330
|
+
@_bitfab_pending_traces[method_name] = config
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# EXPERIMENTAL. Configure one method discovered beneath bitfab_trace.
|
|
335
|
+
# The node inherits the active trace key and never traces by itself.
|
|
336
|
+
def bitfab_node(method_name, name: nil, type: "custom", capture: true,
|
|
337
|
+
test_run_id: nil, mock_on_replay: false, finalize: nil)
|
|
338
|
+
Traceable.send(:validate_node_options!, capture:, mock_on_replay:)
|
|
339
|
+
config = {name:, type:, capture:, test_run_id:, mock_on_replay:, finalize:}
|
|
340
|
+
if method_defined?(method_name) || private_method_defined?(method_name)
|
|
341
|
+
_bitfab_node_method(method_name, **config)
|
|
342
|
+
else
|
|
343
|
+
@_bitfab_pending_nodes ||= {}
|
|
344
|
+
@_bitfab_pending_nodes[method_name] = config
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
171
348
|
private
|
|
172
349
|
|
|
173
350
|
def method_added(method_name)
|
|
174
351
|
super
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
352
|
+
if defined?(@_bitfab_pending_spans) && @_bitfab_pending_spans&.key?(method_name)
|
|
353
|
+
config = @_bitfab_pending_spans.delete(method_name)
|
|
354
|
+
_bitfab_wrap_method(method_name, **config)
|
|
355
|
+
elsif defined?(@_bitfab_pending_traces) && @_bitfab_pending_traces&.key?(method_name)
|
|
356
|
+
config = @_bitfab_pending_traces.delete(method_name)
|
|
357
|
+
_bitfab_trace_method(method_name, **config)
|
|
358
|
+
elsif defined?(@_bitfab_pending_nodes) && @_bitfab_pending_nodes&.key?(method_name)
|
|
359
|
+
config = @_bitfab_pending_nodes.delete(method_name)
|
|
360
|
+
_bitfab_node_method(method_name, **config)
|
|
361
|
+
end
|
|
179
362
|
end
|
|
180
363
|
|
|
181
364
|
def _bitfab_wrap_method(method_name, trace_function_key:, name: nil, type: "custom",
|
|
@@ -198,7 +381,9 @@ module Bitfab
|
|
|
198
381
|
args:,
|
|
199
382
|
kwargs:,
|
|
200
383
|
capture_when:,
|
|
201
|
-
mock_on_replay
|
|
384
|
+
mock_on_replay:,
|
|
385
|
+
explicit_span_receiver: self,
|
|
386
|
+
explicit_span_method_name: method_name) do
|
|
202
387
|
super(*args, **kwargs, &block)
|
|
203
388
|
end
|
|
204
389
|
end
|
|
@@ -208,6 +393,68 @@ module Bitfab
|
|
|
208
393
|
wrapper.instance_variable_set(:@bitfab_span_key, trace_function_key)
|
|
209
394
|
prepend(wrapper)
|
|
210
395
|
end
|
|
396
|
+
|
|
397
|
+
def _bitfab_trace_method(method_name, trace_function_key:, name: nil, type: "custom",
|
|
398
|
+
max_depth: Subtree::DEFAULT_MAX_DEPTH, max_spans: Subtree::DEFAULT_MAX_SPANS,
|
|
399
|
+
exclude: [], include_wrappers: false)
|
|
400
|
+
source = Traceable.send(:source_of, self, method_name)
|
|
401
|
+
capture_config = Subtree.prepare(root_source: source, exclude:)
|
|
402
|
+
span_name = name || method_name.to_s
|
|
403
|
+
method_name_str = method_name.to_s
|
|
404
|
+
|
|
405
|
+
wrapper = Module.new do
|
|
406
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
407
|
+
if Subtree.active_trace?(trace_function_key)
|
|
408
|
+
next super(*args, **kwargs, &block)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
client = Bitfab.client_or_nil
|
|
412
|
+
return super(*args, **kwargs, &block) unless client
|
|
413
|
+
|
|
414
|
+
client.send(
|
|
415
|
+
:execute_span,
|
|
416
|
+
trace_function_key:,
|
|
417
|
+
span_name:,
|
|
418
|
+
span_type: type,
|
|
419
|
+
function_name: method_name_str,
|
|
420
|
+
args:,
|
|
421
|
+
kwargs:,
|
|
422
|
+
independent_root: true
|
|
423
|
+
) do
|
|
424
|
+
Subtree.capture(
|
|
425
|
+
client:,
|
|
426
|
+
trace_function_key:,
|
|
427
|
+
config: capture_config,
|
|
428
|
+
max_depth:,
|
|
429
|
+
max_spans:,
|
|
430
|
+
include_wrappers:,
|
|
431
|
+
root_receiver: self,
|
|
432
|
+
root_method_name: method_name
|
|
433
|
+
) { super(*args, **kwargs, &block) }
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
wrapper.instance_variable_set(:@bitfab_span_method, method_name_str)
|
|
439
|
+
wrapper.instance_variable_set(:@bitfab_span_key, trace_function_key)
|
|
440
|
+
Traceable.send(:preserve_visibility, self, wrapper, method_name)
|
|
441
|
+
prepend(wrapper)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def _bitfab_node_method(method_name, name: nil, type: "custom", capture: true,
|
|
445
|
+
test_run_id: nil, mock_on_replay: false, finalize: nil)
|
|
446
|
+
Traceable.node(
|
|
447
|
+
self,
|
|
448
|
+
method_name,
|
|
449
|
+
name:,
|
|
450
|
+
type:,
|
|
451
|
+
capture:,
|
|
452
|
+
test_run_id:,
|
|
453
|
+
mock_on_replay:,
|
|
454
|
+
finalize:,
|
|
455
|
+
client: nil
|
|
456
|
+
)
|
|
457
|
+
end
|
|
211
458
|
end
|
|
212
459
|
end
|
|
213
460
|
end
|
data/lib/bitfab/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bitfab
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.51.
|
|
4
|
+
version: 0.51.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Harvest Team
|
|
@@ -156,6 +156,7 @@ files:
|
|
|
156
156
|
- lib/bitfab/replay_registry.rb
|
|
157
157
|
- lib/bitfab/serialize.rb
|
|
158
158
|
- lib/bitfab/span_context.rb
|
|
159
|
+
- lib/bitfab/subtree.rb
|
|
159
160
|
- lib/bitfab/timestamp.rb
|
|
160
161
|
- lib/bitfab/traceable.rb
|
|
161
162
|
- lib/bitfab/transport.rb
|