Module: Bitfab

Defined in:
lib/bitfab.rb,
lib/bitfab/otel.rb,
lib/bitfab/client.rb,
lib/bitfab/replay.rb,
lib/bitfab/version.rb,
lib/bitfab/constants.rb,
lib/bitfab/serialize.rb,
lib/bitfab/traceable.rb,
lib/bitfab/transport.rb,
lib/bitfab/warn_once.rb,
lib/bitfab/db_snapshot.rb,
lib/bitfab/http_client.rb,
lib/bitfab/span_context.rb,
lib/bitfab/mock_override.rb,
lib/bitfab/replay_branch.rb,
lib/bitfab/otel_collector.rb

Defined Under Namespace

Modules: DbSnapshot, MockOverride, Otel, Replay, ReplayContext, Serialize, SpanContext, TraceState, Traceable, Transport Classes: BitfabFunction, Client, CurrentSpan, CurrentTrace, HttpClient, NoOpCurrentSpan, NoOpCurrentTrace, ReplayBranch

Constant Summary collapse

NO_OP_SPAN =
NoOpCurrentSpan.new.freeze
NO_OP_TRACE =
NoOpCurrentTrace.new.freeze
MOCK_STRATEGIES =

Replay mock strategies. Mirrors the Python and TypeScript SDKs.

  • "marked" : only spans declared with mock_on_replay: true return historical output; everything else runs real code (default)
  • "none" : every child span runs real code
  • "all" : every child span returns its historical output
%w[none all marked].freeze
VERSION =
"0.33.6"
DEFAULT_SERVICE_URL =
"https://bitfab.ai"
REPLAY_CONTEXT_KEY =
:__bitfab_replay_context
BITFAB_PROGRESS_PREFIX =

Wire prefix the Bitfab plugin scans for on stderr to report live progress while a replay runs. One trailing space, then the progress object as JSON.

"@@bitfab:progress "

Class Method Summary collapse

Class Method Details

._reset_warn_onceObject

Test-only: clear the dedup set so a warning can fire again.



31
32
33
# File 'lib/bitfab/warn_once.rb', line 31

def _reset_warn_once
  @warn_once_mutex.synchronize { @warn_once_seen.clear }
end

.clientObject

Returns the global client, raising if not configured.



76
77
78
# File 'lib/bitfab.rb', line 76

def client
  @client or raise "Bitfab not configured. Call Bitfab.configure(api_key: '...') first."
end

.client_or_nilObject

Returns the global client, or nil if not configured. Used on the traced call path so a method invoked before Bitfab.configure runs untraced rather than crashing the host app with a "not configured" error.



83
84
85
# File 'lib/bitfab.rb', line 83

def client_or_nil
  @client
end

.configure(api_key: nil, service_url: nil, enabled: true, strict: false) ⇒ Object

Configure the global Bitfab client.

Examples:

Bitfab.configure(api_key: ENV["BITFAB_API_KEY"])

Parameters:

  • api_key (String) (defaults to: nil)

    API key for authentication

  • service_url (String, nil) (defaults to: nil)

    base URL (default: https://bitfab.ai)



71
72
73
# File 'lib/bitfab.rb', line 71

def configure(api_key: nil, service_url: nil, enabled: true, strict: false)
  @client = Client.new(api_key:, service_url:, enabled:, strict:)
end

.current_replay_branchReplayBranch?

Get the database branch the current replay item is running against.

Call this from inside a method being replayed with client.replay(db_branch: ...) and point your database client at branch.database_url so the replay reads the data as it was at trace time:

branch = Bitfab.current_replay_branch
url = branch ? branch.database_url : ENV["DATABASE_URL"]

Returns:

  • (ReplayBranch, nil)

    nil outside a replay item, and for an item whose source trace carried no DB snapshot reference



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/bitfab.rb', line 117

def current_replay_branch
  ctx = ReplayContext.current
  return nil unless ctx

  lease = ctx[:db_branch_lease]
  return nil unless lease

  # Surface the Bitfab trace ID (what the customer sees in the dashboard),
  # not the external one. Falling back to the external ID keeps replays from
  # external sources working until that path is fully wired.
  trace_id = ctx[:source_bitfab_trace_id] || ctx[:input_source_trace_id]
  return nil unless trace_id

  ReplayBranch.new(lease, trace_id, ctx)
end

.current_spanCurrentSpan, NoOpCurrentSpan

Get a handle to the current active span.

Call this from inside a traced method to get a span handle that allows setting metadata at runtime.

Returns:



98
99
100
101
102
103
# File 'lib/bitfab.rb', line 98

def current_span
  entry = SpanContext.current
  return NO_OP_SPAN unless entry

  CurrentSpan.new(entry)
end

.current_traceCurrentTrace, NoOpCurrentTrace

Get a handle to the current active trace.

Call this from inside a traced method to get a trace handle that allows setting trace-level context at runtime.

Returns:



139
140
141
142
143
144
# File 'lib/bitfab.rb', line 139

def current_trace
  entry = SpanContext.current
  return NO_OP_TRACE unless entry

  CurrentTrace.new(entry[:trace_id])
end

.flush_traces(timeout: 30) ⇒ Object

Wait for queued spans and trace completions to reach the server, within one total deadline. Returns true when everything landed in time.



299
300
301
# File 'lib/bitfab/http_client.rb', line 299

def flush_traces(timeout: 30)
  Transport.flush_trace_transports([timeout, 0].max)
end

.report_replay_progress(progress) ⇒ Object

A ready-made on_progress callback for replay: writes one @@bitfab:progress line per trace to stderr, which the Bitfab plugin polls to report live progress while the replay runs in the background, so replay scripts never hand-format the protocol. stdout stays the ReplayResult JSON. Never raises (progress must not crash a run). The progress hash's item is forwarded verbatim through to_json, so the plugin sees the source trace id, local replay trace id, outputs, error, and duration of the item that just settled.

Examples:

Bitfab.client.replay(..., on_progress: Bitfab.method(:report_replay_progress))

Parameters:

  • progress (Hash)

    running totals with keys completed, total, succeeded, errored, and item (the object replay already passes to on_progress)



160
161
162
163
164
# File 'lib/bitfab.rb', line 160

def report_replay_progress(progress)
  warn "#{BITFAB_PROGRESS_PREFIX}#{progress.to_json}"
rescue
  nil
end

.reset!Object

Reset the global client (primarily for testing).



88
89
90
# File 'lib/bitfab.rb', line 88

def reset!
  @client = nil
end

.warn_once(key, message) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/bitfab/warn_once.rb', line 17

def warn_once(key, message)
  @warn_once_mutex.synchronize do
    return if @warn_once_seen.include?(key)

    @warn_once_seen.add(key)
  end
  begin
    warn "Bitfab: #{message}"
  rescue Exception # rubocop:disable Lint/RescueException
    # Logging must never crash the host app (e.g. a closed $stderr).
  end
end