bitfab 0.57.8 → 0.58.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: 187464e848086662f6d74efe91dfe95476bdae7284d7ba5668f49b2a4af4c606
4
- data.tar.gz: 16fce40beaf7dc062e5d6d31624d6393730847b1eaee578b60459b5f0d27897a
3
+ metadata.gz: 49c5627d0b0880adf5971614d2b017bf97a1886313776b757996d1bdab1d1f92
4
+ data.tar.gz: cce67f5a022711dc884d9b391d2daa4547b5fc11fdff1ab159c7a5b82fb404bf
5
5
  SHA512:
6
- metadata.gz: 7a5f7f9cac1900dca07228819c1c40965cf020deb5adba9f9827c36c76265911e79c5ce3aca16db5e486c354a436f63e9ac487896af9c2cf522581886424887d
7
- data.tar.gz: 560d27de6a0766f9625b82bcf33053ebfffd3792dbaf2a21cd59ddd7a129742d265965d086d68138566dee51a5ba02893fdcf0bf67a95ebc8d24bc4d37ecf6ff
6
+ metadata.gz: 28f813f00802eda3f55615bf5acba0264d6e9a80130117522436eefb943be3fbd17753289a6a2f7e6295ed9772380221a89a9c3e8477041b974f5d3f4cde917f
7
+ data.tar.gz: a15a81f27f2ef548b854a7b53cdd32a813583b31066a0cba8a40aa364c1bdf9e771f9534bd0aab8b5355bc146fbff18ec0a1cfb9a417634e28ab9691e6cdc6c7
data/README.md CHANGED
@@ -235,7 +235,7 @@ class OrderPipeline
235
235
  end
236
236
  ```
237
237
 
238
- Capture uses `TracePoint` only during the root call. It skips Ruby/runtime code, dependency gems, Bitfab SDK internals, blocks, lambdas, and forwarding wrappers. When the root belongs to an installed application gem, that gem is the first-party boundary; neighboring dependency gems remain excluded. It follows recursion, modules, inheritance, `define_method`, and same-thread child Fibers; aliases are recorded and excluded by the name that was invoked. Child Fibers created during capture inherit node policies, trace identity, parenting, and depth limits while retaining separate call stacks. Pre-existing Fibers, `Fiber.new(storage: nil)`, and child threads do not inherit subtree context. When an inner capture ends, child Fibers retain any still-active outer capture; inherited context expires once all enclosing captures end. While a root remains active, hitting a span limit stops new capture but allows already-open spans in every Fiber to finish. `max_depth:` (default `30`), `max_captured_subtree_spans:` (default `500`), `max_spans:` (default `10000`), `exclude:`, and `include_wrappers:` control the subtree. `max_spans:` is a strict cap on the total number of spans one trace root sends. It counts the root span itself, discovered calls, `bitfab_node` calls, nested trace roots, and spans whose capture is on or off, including spans sent without inputs and outputs because the sim plan could not be read. Nothing past it is sent. It must be at least 1, and a lower value raises `ArgumentError`. `max_captured_subtree_spans:` caps discovered calls sent with their inputs and outputs. It does not count `bitfab_node` calls, nested trace roots, or spans the loaded sim plan turned capture off for, so those keep recording past it up to `max_spans:`. Spans sent without inputs and outputs only because the sim plan could not be read still count toward it. Past it, further discovered calls with capture on record nothing. It is never higher than `max_spans:`. Once the plan has loaded, the SDK never builds the inputs or outputs of a span whose capture it turned off. The client starts reading the sim plan when it is built. A `bitfab_trace` root that starts while that first read is still running waits for it, for at most five seconds. Once the first read has finished, whether it succeeded or failed, no later root waits. Nested roots and the calls beneath a root never wait, and nothing waits when the plan is disabled. If that first read fails, spans other than each trace's root span are sent without inputs and outputs until a read succeeds, and they still count toward both limits. Automatically captured calls are not replay mock targets. Declare a call with `bitfab_node` when replay must substitute it. The configured node records once in each enclosing trace. Mixing explicit spans with subtree tracing raises `Bitfab::MixedTracingError`.
238
+ Capture uses `TracePoint` only during the root call. It skips Ruby/runtime code, dependency gems, Bitfab SDK internals, blocks, lambdas, and forwarding wrappers. When the root belongs to an installed application gem, that gem is the first-party boundary; neighboring dependency gems remain excluded. It follows recursion, modules, inheritance, `define_method`, and same-thread child Fibers; aliases are recorded and excluded by the name that was invoked. Child Fibers created during capture inherit node policies, trace identity, parenting, and depth limits while retaining separate call stacks. Pre-existing Fibers, `Fiber.new(storage: nil)`, and child threads do not inherit subtree context. When an inner capture ends, child Fibers retain any still-active outer capture; inherited context expires once all enclosing captures end. While a root remains active, hitting a span limit stops new capture but allows already-open spans in every Fiber to finish. `max_depth:` (default `30`), `max_captured_subtree_spans:` (default `500`), `exclude:`, and `include_wrappers:` control the subtree. There is no cap on the total number of spans one trace root sends. `max_spans:` was removed, and passing it raises `ArgumentError`. `max_captured_subtree_spans:` caps discovered calls sent with their inputs and outputs. It does not count `bitfab_node` calls, nested trace roots, or spans the loaded sim plan turned capture off for, so those keep recording past it. Spans sent without inputs and outputs only because the sim plan could not be read still count toward it. Past it, further discovered calls with capture on record nothing. Once the plan has loaded, the SDK does not send a span whose capture it turned off, and that span's children move up to the nearest visible parent. The trace's root span, a framework integration span, and a span that recorded an error are still sent, and the SDK never builds their inputs or outputs. A `bitfab_node` call and a nested trace root ignore the plan and always keep their inputs and outputs. The client starts reading the sim plan when it is built. A `bitfab_trace` root that starts while that first read is still running waits for it, for at most five seconds. Once the first read has finished, whether it succeeded or failed, no later root waits. Nested roots and the calls beneath a root never wait, and nothing waits when the plan is disabled. If that first read fails, spans other than each trace's root span, `bitfab_node` calls, and nested trace roots are sent without inputs and outputs until a read succeeds, and they still count toward `max_captured_subtree_spans:`. Automatically captured calls are not replay mock targets. Declare a call with `bitfab_node` when replay must substitute it. The configured node records once in each enclosing trace. Mixing explicit spans with subtree tracing raises `Bitfab::MixedTracingError`.
239
239
 
240
240
  When capture ends, already-open automatic calls in suspended Fibers are emitted once with a nil output and `Subtree capture ended before the call returned` as the error, unless a real error was already observed. Their inputs and parenting are retained. The SDK does not resume Fibers, and later resumption cannot add spans to the ended capture. If hot reload moves the root to an unresolved source, the last valid first-party boundary and its exclusions remain in use.
241
241
 
data/lib/bitfab/client.rb CHANGED
@@ -376,7 +376,7 @@ module Bitfab
376
376
  capture_when: "always", mock_on_replay: false, independent_root: false,
377
377
  explicit_span_receiver: nil, explicit_span_method_name: nil, test_run_id: nil, finalize: nil,
378
378
  surface: nil, on_start: nil, on_complete: nil, around_body: nil, span_data: {}, managed_root: false,
379
- result_recorded_elsewhere: false)
379
+ result_recorded_elsewhere: false, declared_node: false)
380
380
  return yield unless should_record?
381
381
 
382
382
  @http_client.refresh_simulation_plan
@@ -477,7 +477,8 @@ module Bitfab
477
477
  test_run_id: resolved_test_run_id,
478
478
  input_source_span_id: resolved_input_source_span_id,
479
479
  mock_source:,
480
- instrumentation: (surface == :trace) ? "trace" : "span"
480
+ instrumentation: (surface == :trace) ? "trace" : "span",
481
+ declared_node:
481
482
  )
482
483
  on_complete&.call(result: mocked_output, error: nil, ended_at: Bitfab.now_iso_timestamp)
483
484
  return mocked_output
@@ -509,6 +510,8 @@ module Bitfab
509
510
  @http_client.trace_completion.start(trace_id, span_id)
510
511
  recording_override = managed_root ? {} : nil
511
512
  content_withheld = lambda do
513
+ next false if declared_node
514
+
512
515
  instrumentation = recording_override&.dig(:instrumentation) || ((surface == :trace) ? "trace" : "span")
513
516
  content_off_by_simulation_plan?(
514
517
  trace_id:, trace_function_key:, span_name: recording_override&.dig(:name) || span_name, instrumentation:
@@ -544,6 +547,7 @@ module Bitfab
544
547
  send_span(
545
548
  span_data: content_off ? span_data.merge("content_off_by_simulation_plan" => true) : span_data,
546
549
  instrumentation: recorded_instrumentation,
550
+ declared_node:,
547
551
  trace_function_key:,
548
552
  trace_id:,
549
553
  span_id:,
@@ -921,7 +925,7 @@ module Bitfab
921
925
  def send_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
922
926
  span_name:, span_type:, function_name:, contexts:, prompt:, args:, kwargs:, result:, error:,
923
927
  started_at:, ended_at:, test_run_id: nil, input_source_span_id: nil, mocked: false,
924
- mock_target: nil, mock_source: nil, span_data: {}, instrumentation: "span")
928
+ mock_target: nil, mock_source: nil, span_data: {}, instrumentation: "span", declared_node: false)
925
929
  extra_span_data = span_data
926
930
  # If drop() was called on this trace, suppress the span PAYLOAD upload for
927
931
  # every span that completes after the flag was set. The trace completion
@@ -933,9 +937,9 @@ module Bitfab
933
937
  # Human-readable JSON (input/output fields). The *_with_report variants
934
938
  # also report what could not be faithfully captured so a lossy span is
935
939
  # marked non-replayable below instead of shipping as if it round-trips.
936
- content_off = extra_span_data["content_off_by_simulation_plan"] == true ||
940
+ content_off = !declared_node && (extra_span_data["content_off_by_simulation_plan"] == true ||
937
941
  content_off_by_simulation_plan?(trace_id:, trace_function_key:, span_name:, instrumentation:) ||
938
- (!parent_span_id.nil? && simulation_plan_unavailable?(instrumentation:))
942
+ (!parent_span_id.nil? && simulation_plan_unavailable?(instrumentation:)))
939
943
  if content_off
940
944
  dropped = []
941
945
  span_data = {"name" => span_name, "type" => span_type, "function_name" => function_name}
@@ -1011,7 +1015,7 @@ module Bitfab
1011
1015
  }]
1012
1016
  end
1013
1017
 
1014
- @http_client.send_external_span(payload) # Returns the background thread
1018
+ @http_client.send_external_span(payload, declared_node)
1015
1019
  end
1016
1020
 
1017
1021
  # Advance the per-(key, name) call counter when this invocation is a
@@ -1141,7 +1145,7 @@ module Bitfab
1141
1145
  # output as the result and no error.
1142
1146
  def send_mocked_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
1143
1147
  span_name:, span_type:, function_name:, args:, kwargs:, mocked_output:,
1144
- started_at:, test_run_id:, input_source_span_id:, mock_source:, instrumentation: "span")
1148
+ started_at:, test_run_id:, input_source_span_id:, mock_source:, instrumentation: "span", declared_node: false)
1145
1149
  ended_at = Bitfab.now_iso_timestamp
1146
1150
  send_span(
1147
1151
  trace_function_key:,
@@ -1163,6 +1167,7 @@ module Bitfab
1163
1167
  input_source_span_id:,
1164
1168
  mocked: true,
1165
1169
  instrumentation:,
1170
+ declared_node:,
1166
1171
  # Both paths short-circuit the call, so the target is always the
1167
1172
  # output; only where the value came from differs.
1168
1173
  mock_target: "output",
@@ -1223,7 +1228,7 @@ module Bitfab
1223
1228
 
1224
1229
  # Experimentally trace an existing method and its first-party call subtree.
1225
1230
  def trace(klass, method_name, name: nil, type: "custom",
1226
- max_depth: Subtree::DEFAULT_MAX_DEPTH, max_spans: Subtree::DEFAULT_MAX_SPANS,
1231
+ max_depth: Subtree::DEFAULT_MAX_DEPTH,
1227
1232
  max_captured_subtree_spans: Subtree::DEFAULT_MAX_CAPTURED_SUBTREE_SPANS,
1228
1233
  exclude: [], include_wrappers: false, mock_on_replay_default: false)
1229
1234
  Bitfab::Traceable.trace(
@@ -1232,7 +1237,6 @@ module Bitfab
1232
1237
  name:,
1233
1238
  type:,
1234
1239
  max_depth:,
1235
- max_spans:,
1236
1240
  max_captured_subtree_spans:,
1237
1241
  exclude:,
1238
1242
  include_wrappers:,
@@ -188,7 +188,7 @@ module Bitfab
188
188
  end
189
189
 
190
190
  # Queue an external span on this client's trace transport (fire-and-forget).
191
- def send_external_span(payload)
191
+ def send_external_span(payload, declared_node = false)
192
192
  if (data = payload.dig("rawSpan", "span_data")).is_a?(Hash) && !data.key?("runtime")
193
193
  payload = payload.merge("rawSpan" => payload["rawSpan"].merge("span_data" => data.merge("runtime" => ThreadPropagation.runtime)))
194
194
  end
@@ -204,7 +204,11 @@ module Bitfab
204
204
  recorded_meta("external_span", record, ref)
205
205
  )
206
206
  }
207
- @simulation_plan ? @simulation_plan.send_span(payload, &submit) : submit.call(payload)
207
+ discard = ->(record) {
208
+ ref = carrier_ref(record)
209
+ @trace_completion.discard(ref.trace_id, ref.span_id) if ref&.span_id
210
+ }
211
+ @simulation_plan ? @simulation_plan.send_span(payload, declared_node:, discard:, &submit) : submit.call(payload)
208
212
  end
209
213
 
210
214
  def send_internal_trace(function_id, payload)
@@ -69,7 +69,7 @@ module Bitfab
69
69
  @mutex.synchronize { start_reader(0) if @thread.nil? && now >= @refresh_after }
70
70
  end
71
71
 
72
- def send_span(payload, &submit)
72
+ def send_span(payload, declared_node: false, discard: nil, &submit)
73
73
  refresh
74
74
  data = payload.dig("rawSpan", "span_data")
75
75
  key = payload["rootTraceFunctionKey"] || payload["traceFunctionKey"]
@@ -77,7 +77,7 @@ module Bitfab
77
77
  submit.call(payload)
78
78
  return
79
79
  end
80
- entry = [payload, key, submit]
80
+ entry = [payload, key, submit, discard, declared_node]
81
81
  ready = @mutex.synchronize { reading? ? hold(entry) : entry }
82
82
  deliver(*ready) if ready
83
83
  end
@@ -164,6 +164,7 @@ module Bitfab
164
164
  names = @mutex.synchronize { @content_off&.fetch(key, nil) }
165
165
  data = payload.dig("rawSpan", "span_data")
166
166
  return payload unless data.is_a?(Hash) && names&.include?(data["name"])
167
+ return nil unless root?(payload) || data["error"]
167
168
 
168
169
  without_content(payload, data)
169
170
  end
@@ -196,12 +197,13 @@ module Bitfab
196
197
  @held.shift if @held.length > MAX_HELD
197
198
  end
198
199
 
199
- def deliver(payload, key, submit)
200
- submit.call(prepare(payload, key))
200
+ def deliver(payload, key, submit, discard = nil, declared_node = false)
201
+ record = declared_node ? payload : prepare(payload, key)
202
+ record ? submit.call(record) : discard&.call(payload)
201
203
  end
202
204
 
203
- def deliver_safely(payload, key, submit)
204
- deliver(payload, key, submit)
205
+ def deliver_safely(payload, key, submit, discard = nil, declared_node = false)
206
+ deliver(payload, key, submit, discard, declared_node)
205
207
  rescue => error
206
208
  Bitfab.warn_once("sim-plan-held-span-dropped", "Held record could not be sent: #{error}")
207
209
  end
@@ -10,9 +10,8 @@ module Bitfab
10
10
 
11
11
  module Subtree
12
12
  DEFAULT_MAX_DEPTH = 30
13
- DEFAULT_MAX_SPANS = 10_000
14
13
  DEFAULT_MAX_CAPTURED_SUBTREE_SPANS = 500
15
- LIMIT_ORDER = %i[max_depth max_spans max_captured_subtree_spans].freeze
14
+ LIMIT_ORDER = %i[max_depth max_captured_subtree_spans].freeze
16
15
  SESSION_KEY = :bitfab_subtree_session
17
16
  DISPATCHER_KEY = :bitfab_subtree_dispatcher
18
17
  FIBER_CONTEXT_KEY = :bitfab_subtree_fiber_context
@@ -53,6 +52,7 @@ module Bitfab
53
52
  :span_type,
54
53
  :test_run_id,
55
54
  :span_data,
55
+ :declared_node,
56
56
  keyword_init: true
57
57
  )
58
58
 
@@ -175,7 +175,7 @@ module Bitfab
175
175
  CaptureConfig.new(root: nil, root_path: nil, exclude: {}.freeze).freeze
176
176
  end
177
177
 
178
- def capture(client:, trace_function_key:, config:, max_depth:, max_spans:, include_wrappers:,
178
+ def capture(client:, trace_function_key:, config:, max_depth:, include_wrappers:,
179
179
  root_receiver:, root_method_name:, max_captured_subtree_spans: DEFAULT_MAX_CAPTURED_SUBTREE_SPANS, mock_on_replay_default: false)
180
180
  return yield unless config.root
181
181
 
@@ -193,7 +193,6 @@ module Bitfab
193
193
  input_source_span_id: replay&.dig(:input_source_span_id),
194
194
  config:,
195
195
  max_depth:,
196
- max_spans:,
197
196
  max_captured_subtree_spans:,
198
197
  include_wrappers:,
199
198
  mock_on_replay_default:,
@@ -418,7 +417,7 @@ module Bitfab
418
417
  end
419
418
 
420
419
  def initialize(client:, trace_function_key:, trace_id:, root_span_id:, test_run_id:,
421
- input_source_span_id:, config:, max_depth:, max_spans:, include_wrappers:,
420
+ input_source_span_id:, config:, max_depth:, include_wrappers:,
422
421
  root_receiver:, root_method_name:, max_captured_subtree_spans: DEFAULT_MAX_CAPTURED_SUBTREE_SPANS, mock_on_replay_default: false)
423
422
  @mock_on_replay_default = mock_on_replay_default
424
423
  @client = client
@@ -433,8 +432,7 @@ module Bitfab
433
432
  @root_method_name = root_method_name.to_sym
434
433
  @skip_root_call = true
435
434
  @max_depth = max_depth
436
- @max_spans = max_spans
437
- @max_captured_subtree_spans = [max_captured_subtree_spans, max_spans].min
435
+ @max_captured_subtree_spans = max_captured_subtree_spans
438
436
  @exclude = config.exclude
439
437
  @include_wrappers = include_wrappers
440
438
  @stacks = {}
@@ -443,7 +441,6 @@ module Bitfab
443
441
  @suppressed_depths = {}
444
442
  @owned_paths = {}
445
443
  @method_metadata = Hash.new { |owners, owner| owners[owner] = {} }
446
- @spans_used = 1
447
444
  @captured_subtree_count = 0
448
445
  @limits_fired = Set.new
449
446
  @dropped_spans = 0
@@ -524,14 +521,9 @@ module Bitfab
524
521
 
525
522
  def reserve_node
526
523
  stack = current_stack(Fiber.current.object_id)
527
- if captured_depth(stack) >= @max_depth
528
- drop(:max_depth)
529
- return false
530
- end
531
- return true if take_slot(discovered: false)
524
+ return true if captured_depth(stack) < @max_depth
532
525
 
533
- drop(:max_spans)
534
- @exhausted = true
526
+ drop(:max_depth)
535
527
  false
536
528
  end
537
529
 
@@ -547,7 +539,7 @@ module Bitfab
547
539
  span_id: SecureRandom.uuid,
548
540
  parent_span_id: current_parent_span_id,
549
541
  name:, span_type: type, args:, kwargs:, test_run_id:, started_at: now,
550
- span_data: {}
542
+ span_data: {}, declared_node: true
551
543
  )
552
544
  @configured_entries[entry.object_id] = entry
553
545
  entry
@@ -652,9 +644,9 @@ module Bitfab
652
644
  return
653
645
  end
654
646
  content_off = content_off?(metadata.name)
655
- if (limit = limit_reached(discovered: true, content_off:))
656
- drop(limit)
657
- stop_discovery(fiber_id) unless limit == :max_captured_subtree_spans && content_off_names?
647
+ if !content_off && @captured_subtree_count >= @max_captured_subtree_spans
648
+ drop(:max_captured_subtree_spans)
649
+ stop_discovery(fiber_id) unless content_off_names?
658
650
  return
659
651
  end
660
652
 
@@ -665,7 +657,7 @@ module Bitfab
665
657
  return
666
658
  end
667
659
 
668
- count_span(discovered: true, content_off:)
660
+ @captured_subtree_count += 1 unless content_off
669
661
  span_id = SecureRandom.uuid
670
662
  without_content = content_off || plan_unavailable?
671
663
  args, kwargs = without_content ? [[], {}] : inputs(event)
@@ -741,6 +733,7 @@ module Bitfab
741
733
  span_name: entry.name,
742
734
  span_type: entry.span_type || "function",
743
735
  instrumentation: "trace",
736
+ declared_node: entry.declared_node == true,
744
737
  span_data: entry.span_data || {},
745
738
  function_name: entry.name,
746
739
  contexts: nil,
@@ -890,24 +883,6 @@ module Bitfab
890
883
  @suppressed_depths[fiber_id] = 1
891
884
  end
892
885
 
893
- def limit_reached(discovered:, content_off: false)
894
- return :max_spans if @spans_used >= @max_spans
895
-
896
- :max_captured_subtree_spans if discovered && !content_off && @captured_subtree_count >= @max_captured_subtree_spans
897
- end
898
-
899
- def take_slot(discovered:)
900
- return false if limit_reached(discovered:)
901
-
902
- count_span(discovered:, content_off: false)
903
- true
904
- end
905
-
906
- def count_span(discovered:, content_off:)
907
- @spans_used += 1
908
- @captured_subtree_count += 1 if discovered && !content_off
909
- end
910
-
911
886
  def stop_discovery(fiber_id)
912
887
  @exhausted = true
913
888
  if captured_calls_drained?
@@ -968,7 +943,7 @@ module Bitfab
968
943
  Bitfab.warn_once(
969
944
  "subtree-truncated:#{@trace_function_key}",
970
945
  "'#{@trace_function_key}' hit a subtree capture limit (max_depth=#{@max_depth}, " \
971
- "max_captured_subtree_spans=#{@max_captured_subtree_spans}, max_spans=#{@max_spans}) and its trace is incomplete. " \
946
+ "max_captured_subtree_spans=#{@max_captured_subtree_spans}) and its trace is incomplete. " \
972
947
  "Raise the limits, add bitfab_node to the calls that matter, or narrow what is traced with exclude: [...]."
973
948
  )
974
949
  end
@@ -31,6 +31,10 @@ module Bitfab
31
31
  end
32
32
  end
33
33
 
34
+ def discard(trace_id, span_id)
35
+ synchronize { @traces[trace_id]&.dig(:span_ids)&.delete(span_id) }
36
+ end
37
+
34
38
  def finish(trace_id, span_id)
35
39
  ready = synchronize do
36
40
  entry = @traces[trace_id]
@@ -82,10 +82,9 @@ module Bitfab
82
82
  # The root records with the requested type; first-party Ruby methods called
83
83
  # beneath it record automatically as function spans.
84
84
  def self.trace(klass, method_name, trace_function_key:, name: nil, type: "custom",
85
- max_depth: Subtree::DEFAULT_MAX_DEPTH, max_spans: Subtree::DEFAULT_MAX_SPANS,
85
+ max_depth: Subtree::DEFAULT_MAX_DEPTH,
86
86
  max_captured_subtree_spans: Subtree::DEFAULT_MAX_CAPTURED_SUBTREE_SPANS,
87
87
  exclude: [], include_wrappers: false, mock_on_replay_default: false, client: nil)
88
- validate_trace_options!(max_spans:)
89
88
  capture_config = Subtree.prepare(root_source: source_of(klass, method_name), exclude:)
90
89
  method_name_str = method_name.to_s
91
90
  bound_client = client
@@ -106,7 +105,7 @@ module Bitfab
106
105
  SpanContext.current[:surface] = :trace
107
106
  next Subtree.capture(
108
107
  client: target_client, trace_function_key:, config: capture_config,
109
- max_depth:, max_spans:, max_captured_subtree_spans:, include_wrappers:, mock_on_replay_default:,
108
+ max_depth:, max_captured_subtree_spans:, include_wrappers:, mock_on_replay_default:,
110
109
  root_receiver: self, root_method_name: method_name
111
110
  ) { super(*args, **kwargs, &block) }
112
111
  end
@@ -120,7 +119,7 @@ module Bitfab
120
119
  around_body: call.method(:run)) do
121
120
  Subtree.capture(
122
121
  client: target_client, trace_function_key:, config: capture_config,
123
- max_depth:, max_spans:, max_captured_subtree_spans:, include_wrappers:, mock_on_replay_default:,
122
+ max_depth:, max_captured_subtree_spans:, include_wrappers:, mock_on_replay_default:,
124
123
  root_receiver: self, root_method_name: method_name
125
124
  ) { super(*args, **kwargs, &block) }
126
125
  end
@@ -165,7 +164,7 @@ module Bitfab
165
164
  span_name:, span_type: type, function_name: method_name_str,
166
165
  args:, kwargs:, capture_when: "nested", mock_on_replay: policy,
167
166
  explicit_span_receiver: self, explicit_span_method_name: method_name,
168
- test_run_id:, finalize:, surface: :trace, result_recorded_elsewhere: call.recorded_elsewhere?,
167
+ test_run_id:, finalize:, surface: :trace, result_recorded_elsewhere: call.recorded_elsewhere?, declared_node: true,
169
168
  on_start: call.method(:start), on_complete: call.method(:complete),
170
169
  around_body: call.method(:run)) { super(*args, **kwargs, &block) }
171
170
  end
@@ -193,14 +192,6 @@ module Bitfab
193
192
  end
194
193
  private_class_method :preserve_visibility
195
194
 
196
- def self.validate_trace_options!(max_spans:)
197
- return if max_spans.is_a?(Integer) && max_spans >= 1
198
-
199
- raise ArgumentError,
200
- "bitfab_trace max_spans must be an integer of at least 1 because the root span takes one slot, got #{max_spans.inspect}."
201
- end
202
- private_class_method :validate_trace_options!
203
-
204
195
  def self.validate_node_options!(capture:, mock_on_replay:)
205
196
  return unless !capture && mock_on_replay
206
197
 
@@ -304,10 +295,9 @@ module Bitfab
304
295
  # EXPERIMENTAL. Trace a method and every first-party Ruby method it calls.
305
296
  # Descendants require no Bitfab declarations and record as function spans.
306
297
  def bitfab_trace(method_name, trace_function_key: nil, name: nil, type: "custom",
307
- max_depth: Subtree::DEFAULT_MAX_DEPTH, max_spans: Subtree::DEFAULT_MAX_SPANS,
298
+ max_depth: Subtree::DEFAULT_MAX_DEPTH,
308
299
  max_captured_subtree_spans: Subtree::DEFAULT_MAX_CAPTURED_SUBTREE_SPANS,
309
300
  exclude: [], include_wrappers: false, mock_on_replay_default: false)
310
- Traceable.send(:validate_trace_options!, max_spans:)
311
301
  trace_function_key ||= @bitfab_function_key
312
302
  unless trace_function_key
313
303
  raise "No trace function key provided. Pass `trace_function_key:` to `bitfab_trace` " \
@@ -319,7 +309,6 @@ module Bitfab
319
309
  name:,
320
310
  type:,
321
311
  max_depth:,
322
- max_spans:,
323
312
  max_captured_subtree_spans:,
324
313
  exclude:,
325
314
  include_wrappers:,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.57.8"
4
+ VERSION = "0.58.0"
5
5
  end
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.57.8
4
+ version: 0.58.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team