bitfab 0.51.9 → 0.57.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/README.md +4 -4
- data/exe/bitfab-seed +13 -0
- data/lib/bitfab/assertion_categories.rb +42 -0
- data/lib/bitfab/assertions.rb +35 -0
- data/lib/bitfab/baml.rb +208 -0
- data/lib/bitfab/client.rb +157 -50
- data/lib/bitfab/db_snapshot.rb +18 -5
- data/lib/bitfab/detached_trace.rb +46 -0
- data/lib/bitfab/graders.rb +23 -0
- data/lib/bitfab/http_client.rb +124 -35
- data/lib/bitfab/labels.rb +80 -0
- data/lib/bitfab/otel.rb +1 -1
- data/lib/bitfab/replay.rb +92 -23
- data/lib/bitfab/replay_cli.rb +7 -1
- data/lib/bitfab/replay_concurrency.rb +44 -0
- data/lib/bitfab/replay_memory.rb +111 -0
- data/lib/bitfab/replay_processes.rb +218 -0
- data/lib/bitfab/replay_registry.rb +100 -15
- data/lib/bitfab/seed.rb +128 -0
- data/lib/bitfab/seed_cli.rb +67 -0
- data/lib/bitfab/seed_context.rb +21 -0
- data/lib/bitfab/simulation_plan.rb +307 -0
- data/lib/bitfab/span_context.rb +9 -4
- data/lib/bitfab/span_origin.rb +13 -0
- data/lib/bitfab/subtree.rb +214 -26
- data/lib/bitfab/thread_propagation.rb +111 -0
- data/lib/bitfab/traceable.rb +85 -155
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +9 -2
- metadata +32 -1
data/lib/bitfab/subtree.rb
CHANGED
|
@@ -6,9 +6,12 @@ require "time"
|
|
|
6
6
|
|
|
7
7
|
module Bitfab
|
|
8
8
|
# Captures Ruby method calls beneath one traced root via TracePoint.
|
|
9
|
+
class MixedTracingError < StandardError; end
|
|
10
|
+
|
|
9
11
|
module Subtree
|
|
10
12
|
DEFAULT_MAX_DEPTH = 30
|
|
11
|
-
DEFAULT_MAX_SPANS =
|
|
13
|
+
DEFAULT_MAX_SPANS = 2048
|
|
14
|
+
DEFAULT_MAX_CAPTURED_SUBTREE_SPANS = 512
|
|
12
15
|
SESSION_KEY = :bitfab_subtree_session
|
|
13
16
|
DISPATCHER_KEY = :bitfab_subtree_dispatcher
|
|
14
17
|
FIBER_CONTEXT_KEY = :bitfab_subtree_fiber_context
|
|
@@ -46,11 +49,119 @@ module Bitfab
|
|
|
46
49
|
:kwargs,
|
|
47
50
|
:started_at,
|
|
48
51
|
:error,
|
|
52
|
+
:span_type,
|
|
53
|
+
:test_run_id,
|
|
54
|
+
:span_data,
|
|
49
55
|
keyword_init: true
|
|
50
56
|
)
|
|
51
57
|
|
|
52
58
|
module_function
|
|
53
59
|
|
|
60
|
+
def assert_surface!(surface)
|
|
61
|
+
enclosing = current_session ? :trace : SpanContext.current&.dig(:surface)
|
|
62
|
+
return unless enclosing && enclosing != surface
|
|
63
|
+
|
|
64
|
+
raise MixedTracingError,
|
|
65
|
+
"opt-in and opt-out tracing cannot be mixed: #{surface} entered inside #{enclosing}. " \
|
|
66
|
+
"Use bitfab_node inside bitfab_trace, or use bitfab_span throughout the workflow."
|
|
67
|
+
rescue MixedTracingError
|
|
68
|
+
raise
|
|
69
|
+
rescue
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def nested_root_suppressed?
|
|
74
|
+
current_session && (ReplayContext.current || (defined?(SeedContext) && SeedContext.current))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def qualified_name(receiver, method_name)
|
|
78
|
+
owner = receiver.is_a?(Module) ? receiver.name : receiver.class.name
|
|
79
|
+
(owner && !owner.empty? && owner != "Object") ? "#{owner}.#{method_name}" : method_name.to_s
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class ConfiguredCall
|
|
83
|
+
attr_reader :span_data, :owner
|
|
84
|
+
|
|
85
|
+
def initialize(receiver:, method_name:, name:, type:, args:, kwargs:, test_run_id: nil,
|
|
86
|
+
nested_trace_key: nil)
|
|
87
|
+
@receiver = receiver
|
|
88
|
+
@method_name = method_name
|
|
89
|
+
@nested_trace_key = nested_trace_key
|
|
90
|
+
@span_data = {}
|
|
91
|
+
@completed = false
|
|
92
|
+
@records = Subtree.active_sessions.map do |session|
|
|
93
|
+
omitted = session.excluded?(method_name)
|
|
94
|
+
entry = unless omitted
|
|
95
|
+
session.configured_entry(name:, type:, args:, kwargs:, test_run_id:)
|
|
96
|
+
end
|
|
97
|
+
[session, entry, !omitted]
|
|
98
|
+
end
|
|
99
|
+
@owner = @records.find { |_, entry, _| entry }&.first unless nested_trace_key
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def recorded_elsewhere?
|
|
103
|
+
@records.any? { |session, entry, _| entry && !session.equal?(@owner) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def start(trace_id:, span_id:)
|
|
107
|
+
@records.each do |session, entry, _|
|
|
108
|
+
next unless entry
|
|
109
|
+
|
|
110
|
+
if session.equal?(@owner)
|
|
111
|
+
entry.span_id = span_id
|
|
112
|
+
session.discard_configured(entry)
|
|
113
|
+
elsif @nested_trace_key
|
|
114
|
+
entry.span_data = {
|
|
115
|
+
"nested_trace_id" => trace_id,
|
|
116
|
+
"nested_trace_function_key" => @nested_trace_key,
|
|
117
|
+
"nested_root_span_id" => span_id
|
|
118
|
+
}
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
if @nested_trace_key && (enclosing = @records.find { |_, entry, _| entry })
|
|
122
|
+
session, entry = enclosing
|
|
123
|
+
@span_data.merge!(
|
|
124
|
+
"enclosing_trace_id" => session.trace_id,
|
|
125
|
+
"enclosing_trace_function_key" => session.trace_function_key,
|
|
126
|
+
"enclosing_span_id" => entry.span_id
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def run
|
|
132
|
+
boundaries = @records.filter_map do |session, entry, counts_toward_depth|
|
|
133
|
+
next if session.equal?(@owner)
|
|
134
|
+
|
|
135
|
+
[session, session.push_boundary(
|
|
136
|
+
receiver: @receiver, method_name: @method_name,
|
|
137
|
+
span_id: entry&.span_id, counts_toward_depth:
|
|
138
|
+
)]
|
|
139
|
+
end
|
|
140
|
+
yield
|
|
141
|
+
ensure
|
|
142
|
+
boundaries&.reverse_each { |session, boundary| session.pop_boundary(boundary) }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def with_owner_parent(&body)
|
|
146
|
+
return yield unless @owner
|
|
147
|
+
|
|
148
|
+
SpanContext.with_span(trace_id: @owner.trace_id,
|
|
149
|
+
span_id: @owner.current_parent_span_id, surface: :trace, &body)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def complete(result:, error:, ended_at:)
|
|
153
|
+
return if @completed
|
|
154
|
+
|
|
155
|
+
@completed = true
|
|
156
|
+
@records.each do |session, entry, _|
|
|
157
|
+
next if !entry || session.equal?(@owner)
|
|
158
|
+
|
|
159
|
+
entry.error = error
|
|
160
|
+
session.emit_configured(entry, result:)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
54
165
|
def prepare(root_source:, exclude:)
|
|
55
166
|
path = root_source[0]
|
|
56
167
|
root = first_party_root(path)
|
|
@@ -64,7 +175,7 @@ module Bitfab
|
|
|
64
175
|
end
|
|
65
176
|
|
|
66
177
|
def capture(client:, trace_function_key:, config:, max_depth:, max_spans:, include_wrappers:,
|
|
67
|
-
root_receiver:, root_method_name:)
|
|
178
|
+
root_receiver:, root_method_name:, max_captured_subtree_spans: DEFAULT_MAX_CAPTURED_SUBTREE_SPANS, mock_on_replay_default: false)
|
|
68
179
|
return yield unless config.root
|
|
69
180
|
|
|
70
181
|
span = SpanContext.current || ReplayContext.current_span
|
|
@@ -82,7 +193,9 @@ module Bitfab
|
|
|
82
193
|
config:,
|
|
83
194
|
max_depth:,
|
|
84
195
|
max_spans:,
|
|
196
|
+
max_captured_subtree_spans:,
|
|
85
197
|
include_wrappers:,
|
|
198
|
+
mock_on_replay_default:,
|
|
86
199
|
root_receiver:,
|
|
87
200
|
root_method_name:
|
|
88
201
|
)
|
|
@@ -294,7 +407,7 @@ module Bitfab
|
|
|
294
407
|
end
|
|
295
408
|
|
|
296
409
|
class Session
|
|
297
|
-
attr_reader :client, :parent_session, :trace_function_key, :trace_id, :deferred_frames
|
|
410
|
+
attr_reader :client, :parent_session, :trace_function_key, :trace_id, :deferred_frames, :mock_on_replay_default
|
|
298
411
|
|
|
299
412
|
def active? = @active
|
|
300
413
|
def deferred? = @deferred
|
|
@@ -305,7 +418,8 @@ module Bitfab
|
|
|
305
418
|
|
|
306
419
|
def initialize(client:, trace_function_key:, trace_id:, root_span_id:, test_run_id:,
|
|
307
420
|
input_source_span_id:, config:, max_depth:, max_spans:, include_wrappers:,
|
|
308
|
-
root_receiver:, root_method_name:)
|
|
421
|
+
root_receiver:, root_method_name:, max_captured_subtree_spans: DEFAULT_MAX_CAPTURED_SUBTREE_SPANS, mock_on_replay_default: false)
|
|
422
|
+
@mock_on_replay_default = mock_on_replay_default
|
|
309
423
|
@client = client
|
|
310
424
|
@trace_function_key = trace_function_key
|
|
311
425
|
@trace_id = trace_id
|
|
@@ -319,14 +433,17 @@ module Bitfab
|
|
|
319
433
|
@skip_root_call = true
|
|
320
434
|
@max_depth = max_depth
|
|
321
435
|
@max_spans = max_spans
|
|
436
|
+
@max_captured_subtree_spans = [max_captured_subtree_spans, max_spans].min
|
|
322
437
|
@exclude = config.exclude
|
|
323
438
|
@include_wrappers = include_wrappers
|
|
324
439
|
@stacks = {}
|
|
440
|
+
@configured_entries = {}
|
|
325
441
|
@fiber_parents = {}
|
|
326
442
|
@suppressed_depths = {}
|
|
327
443
|
@owned_paths = {}
|
|
328
444
|
@method_metadata = Hash.new { |owners, owner| owners[owner] = {} }
|
|
329
|
-
@spans_used =
|
|
445
|
+
@spans_used = 1
|
|
446
|
+
@captured_subtree_count = 0
|
|
330
447
|
@truncated = false
|
|
331
448
|
@exhausted = false
|
|
332
449
|
@drained = false
|
|
@@ -409,14 +526,34 @@ module Bitfab
|
|
|
409
526
|
@truncated = true
|
|
410
527
|
return false
|
|
411
528
|
end
|
|
412
|
-
if
|
|
413
|
-
@truncated = true
|
|
414
|
-
@exhausted = true
|
|
415
|
-
return false
|
|
416
|
-
end
|
|
529
|
+
return true if take_slot(discovered: false)
|
|
417
530
|
|
|
418
|
-
@
|
|
419
|
-
true
|
|
531
|
+
@truncated = true
|
|
532
|
+
@exhausted = true
|
|
533
|
+
false
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def configured_entry(name:, type:, args:, kwargs:, test_run_id:)
|
|
537
|
+
return unless reserve_node
|
|
538
|
+
|
|
539
|
+
entry = OpenSpan.new(
|
|
540
|
+
span_id: SecureRandom.uuid,
|
|
541
|
+
parent_span_id: current_parent_span_id,
|
|
542
|
+
name:, span_type: type, args:, kwargs:, test_run_id:, started_at: now,
|
|
543
|
+
span_data: {}
|
|
544
|
+
)
|
|
545
|
+
@configured_entries[entry.object_id] = entry
|
|
546
|
+
entry
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def discard_configured(entry)
|
|
550
|
+
@configured_entries.delete(entry.object_id)
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
def emit_configured(entry, result:)
|
|
554
|
+
return unless discard_configured(entry)
|
|
555
|
+
|
|
556
|
+
emit(entry, result:)
|
|
420
557
|
end
|
|
421
558
|
|
|
422
559
|
def current_parent_span_id
|
|
@@ -465,6 +602,11 @@ module Bitfab
|
|
|
465
602
|
private
|
|
466
603
|
|
|
467
604
|
def finish_open_spans
|
|
605
|
+
@configured_entries.each_value do |entry|
|
|
606
|
+
entry.error ||= "Subtree capture ended before the call returned"
|
|
607
|
+
emit(entry, result: nil)
|
|
608
|
+
end
|
|
609
|
+
@configured_entries.clear
|
|
468
610
|
@stacks.each_value do |stack|
|
|
469
611
|
stack.reverse_each do |entry|
|
|
470
612
|
next unless entry.is_a?(OpenSpan)
|
|
@@ -500,14 +642,10 @@ module Bitfab
|
|
|
500
642
|
suppress(fiber_id)
|
|
501
643
|
return
|
|
502
644
|
end
|
|
503
|
-
|
|
645
|
+
content_off = content_off?(metadata.name)
|
|
646
|
+
if (limit = limit_reached(discovered: true, content_off:))
|
|
504
647
|
@truncated = true
|
|
505
|
-
|
|
506
|
-
if captured_calls_drained?
|
|
507
|
-
@drained = true
|
|
508
|
-
else
|
|
509
|
-
suppress(fiber_id)
|
|
510
|
-
end
|
|
648
|
+
stop_discovery(fiber_id) unless limit == :max_captured_subtree_spans && content_off_names?
|
|
511
649
|
return
|
|
512
650
|
end
|
|
513
651
|
|
|
@@ -518,9 +656,10 @@ module Bitfab
|
|
|
518
656
|
return
|
|
519
657
|
end
|
|
520
658
|
|
|
521
|
-
|
|
659
|
+
count_span(discovered: true, content_off:)
|
|
522
660
|
span_id = SecureRandom.uuid
|
|
523
|
-
|
|
661
|
+
without_content = content_off || plan_unavailable?
|
|
662
|
+
args, kwargs = without_content ? [[], {}] : inputs(event)
|
|
524
663
|
# A nested subtree root temporarily owns the ambient span context, but
|
|
525
664
|
# an outer session must never parent its spans across trace boundaries.
|
|
526
665
|
active_parent_span_id = if active_parent&.dig(:trace_id) == @trace_id
|
|
@@ -534,7 +673,8 @@ module Bitfab
|
|
|
534
673
|
name: metadata.name,
|
|
535
674
|
args:,
|
|
536
675
|
kwargs:,
|
|
537
|
-
started_at: now
|
|
676
|
+
started_at: now,
|
|
677
|
+
span_data: without_content ? {"content_off_by_simulation_plan" => true} : nil
|
|
538
678
|
)
|
|
539
679
|
end
|
|
540
680
|
|
|
@@ -590,7 +730,9 @@ module Bitfab
|
|
|
590
730
|
span_id: entry.span_id,
|
|
591
731
|
parent_span_id: entry.parent_span_id,
|
|
592
732
|
span_name: entry.name,
|
|
593
|
-
span_type: "function",
|
|
733
|
+
span_type: entry.span_type || "function",
|
|
734
|
+
instrumentation: "trace",
|
|
735
|
+
span_data: entry.span_data || {},
|
|
594
736
|
function_name: entry.name,
|
|
595
737
|
contexts: nil,
|
|
596
738
|
prompt: nil,
|
|
@@ -600,7 +742,7 @@ module Bitfab
|
|
|
600
742
|
error: entry.error,
|
|
601
743
|
started_at: entry.started_at,
|
|
602
744
|
ended_at: now,
|
|
603
|
-
test_run_id: @test_run_id,
|
|
745
|
+
test_run_id: entry.test_run_id || @test_run_id,
|
|
604
746
|
input_source_span_id: @input_source_span_id
|
|
605
747
|
)
|
|
606
748
|
rescue Exception # rubocop:disable Lint/RescueException
|
|
@@ -739,14 +881,60 @@ module Bitfab
|
|
|
739
881
|
@suppressed_depths[fiber_id] = 1
|
|
740
882
|
end
|
|
741
883
|
|
|
884
|
+
def limit_reached(discovered:, content_off: false)
|
|
885
|
+
return :max_spans if @spans_used >= @max_spans
|
|
886
|
+
|
|
887
|
+
:max_captured_subtree_spans if discovered && !content_off && @captured_subtree_count >= @max_captured_subtree_spans
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
def take_slot(discovered:)
|
|
891
|
+
return false if limit_reached(discovered:)
|
|
892
|
+
|
|
893
|
+
count_span(discovered:, content_off: false)
|
|
894
|
+
true
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
def count_span(discovered:, content_off:)
|
|
898
|
+
@spans_used += 1
|
|
899
|
+
@captured_subtree_count += 1 if discovered && !content_off
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
def stop_discovery(fiber_id)
|
|
903
|
+
@exhausted = true
|
|
904
|
+
if captured_calls_drained?
|
|
905
|
+
@drained = true
|
|
906
|
+
else
|
|
907
|
+
suppress(fiber_id)
|
|
908
|
+
end
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
def content_off?(name)
|
|
912
|
+
@client.send(:content_off_by_simulation_plan?, trace_id: @trace_id, trace_function_key: @trace_function_key,
|
|
913
|
+
span_name: name, instrumentation: "trace")
|
|
914
|
+
rescue
|
|
915
|
+
false
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
def content_off_names?
|
|
919
|
+
@client.send(:simulation_plan_content_off_names?, trace_id: @trace_id, trace_function_key: @trace_function_key)
|
|
920
|
+
rescue
|
|
921
|
+
false
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
def plan_unavailable?
|
|
925
|
+
@client.send(:simulation_plan_unavailable?, instrumentation: "trace")
|
|
926
|
+
rescue
|
|
927
|
+
false
|
|
928
|
+
end
|
|
929
|
+
|
|
742
930
|
def warn_if_truncated
|
|
743
931
|
return unless @truncated
|
|
744
932
|
|
|
745
933
|
Bitfab.warn_once(
|
|
746
934
|
"subtree-truncated:#{@trace_function_key}",
|
|
747
935
|
"'#{@trace_function_key}' hit a subtree capture limit (max_depth=#{@max_depth}, " \
|
|
748
|
-
"max_spans=#{@max_spans}) and its trace is incomplete.
|
|
749
|
-
"what is traced with exclude: [...]."
|
|
936
|
+
"max_captured_subtree_spans=#{@max_captured_subtree_spans}, max_spans=#{@max_spans}) and its trace is incomplete. " \
|
|
937
|
+
"Raise the limits, add bitfab_node to the calls that matter, or narrow what is traced with exclude: [...]."
|
|
750
938
|
)
|
|
751
939
|
end
|
|
752
940
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bitfab
|
|
4
|
+
module ThreadPropagation
|
|
5
|
+
ORIGIN_KEY = :__bitfab_submit_origin
|
|
6
|
+
SDK_PATH = File.expand_path(__dir__) + File::SEPARATOR
|
|
7
|
+
@installed = false
|
|
8
|
+
@mutex = Mutex.new
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def install
|
|
13
|
+
@mutex.synchronize do
|
|
14
|
+
Thread.singleton_class.prepend(ThreadMethods) unless Thread.singleton_class.ancestors.include?(ThreadMethods)
|
|
15
|
+
@installed = true
|
|
16
|
+
end
|
|
17
|
+
begin
|
|
18
|
+
require "concurrent-ruby"
|
|
19
|
+
Concurrent::ThreadPoolExecutor.prepend(PoolMethods) unless Concurrent::ThreadPoolExecutor.ancestors.include?(PoolMethods)
|
|
20
|
+
rescue LoadError
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def installed?
|
|
26
|
+
@installed
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def uninstall
|
|
30
|
+
@installed = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def runtime
|
|
34
|
+
current = Thread.current
|
|
35
|
+
data = {"thread_id" => current.object_id, "thread_name" => current.name || ((current == Thread.main) ? "main" : "thread-#{current.object_id}")}
|
|
36
|
+
if (origin = current.thread_variable_get(ORIGIN_KEY))
|
|
37
|
+
data["parent_thread_id"] = origin["thread_id"]
|
|
38
|
+
data["submit_thread_id"] = origin["thread_id"]
|
|
39
|
+
data["submit_thread_name"] = origin["thread_name"]
|
|
40
|
+
end
|
|
41
|
+
data
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def bind(block)
|
|
45
|
+
stack = SpanContext.stack.dup
|
|
46
|
+
stack << SpanContext.current if stack.empty? && SpanContext.current
|
|
47
|
+
replay = ReplayContext.current
|
|
48
|
+
seed = SeedContext.current
|
|
49
|
+
return block if stack.empty? && replay.nil? && seed.nil?
|
|
50
|
+
|
|
51
|
+
origin = runtime.slice("thread_id", "thread_name")
|
|
52
|
+
lambda do |*args, **kwargs|
|
|
53
|
+
current = Thread.current
|
|
54
|
+
previous_stack = current[SpanContext::STACK_KEY]
|
|
55
|
+
previous_bridge = current.thread_variable_get(SpanContext::FIBER_BRIDGE_STACK_KEY)
|
|
56
|
+
previous_replay = current.thread_variable_get(REPLAY_CONTEXT_KEY)
|
|
57
|
+
previous_seed = current.thread_variable_get(SeedContext::KEY)
|
|
58
|
+
previous_origin = current.thread_variable_get(ORIGIN_KEY)
|
|
59
|
+
local_replay = replay&.merge(span_stack: replay.fetch(:span_stack, []).dup)
|
|
60
|
+
current[SpanContext::STACK_KEY] = stack.dup
|
|
61
|
+
current.thread_variable_set(SpanContext::FIBER_BRIDGE_STACK_KEY, [])
|
|
62
|
+
current.thread_variable_set(REPLAY_CONTEXT_KEY, local_replay)
|
|
63
|
+
current.thread_variable_set(SeedContext::KEY, seed)
|
|
64
|
+
current.thread_variable_set(ORIGIN_KEY, origin)
|
|
65
|
+
begin
|
|
66
|
+
block.call(*args, **kwargs)
|
|
67
|
+
ensure
|
|
68
|
+
replay[:db_snapshot_accessed] = true if replay && local_replay[:db_snapshot_accessed]
|
|
69
|
+
current[SpanContext::STACK_KEY] = previous_stack
|
|
70
|
+
current.thread_variable_set(SpanContext::FIBER_BRIDGE_STACK_KEY, previous_bridge)
|
|
71
|
+
current.thread_variable_set(REPLAY_CONTEXT_KEY, previous_replay)
|
|
72
|
+
current.thread_variable_set(SeedContext::KEY, previous_seed)
|
|
73
|
+
current.thread_variable_set(ORIGIN_KEY, previous_origin)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def internal?(block)
|
|
79
|
+
path = block.source_location&.first
|
|
80
|
+
path && (path.start_with?(SDK_PATH) || path.include?("/opentelemetry") || path.include?("/concurrent-ruby"))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
module ThreadMethods
|
|
84
|
+
%i[new start fork].each do |method_name|
|
|
85
|
+
define_method(method_name) do |*args, &block|
|
|
86
|
+
return super(*args, &block) if !ThreadPropagation.installed? || block.nil? || ThreadPropagation.internal?(block)
|
|
87
|
+
|
|
88
|
+
bound = begin
|
|
89
|
+
ThreadPropagation.bind(block)
|
|
90
|
+
rescue
|
|
91
|
+
block
|
|
92
|
+
end
|
|
93
|
+
super(*args, &bound)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
module PoolMethods
|
|
99
|
+
def post(*args, &block)
|
|
100
|
+
return super unless ThreadPropagation.installed? && block
|
|
101
|
+
|
|
102
|
+
bound = begin
|
|
103
|
+
ThreadPropagation.bind(block)
|
|
104
|
+
rescue
|
|
105
|
+
block
|
|
106
|
+
end
|
|
107
|
+
super(*args, &bound)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|