Bitfab Ruby SDK
Ruby client library for Bitfab - trace and monitor your Ruby application's function execution with nested span support.
Installation
Add to your Gemfile:
gem 'bitfab'
Or install directly:
gem install bitfab
Requirements
- Ruby >= 3.4
- No external runtime dependencies (uses stdlib only)
Quick Start
require 'bitfab'
# Configure once at application startup
Bitfab.configure(
api_key: ENV.fetch('BITFAB_API_KEY')
)
# Add tracing to your classes
class OrderService
include Bitfab::Traceable
bitfab_function "order-processing"
bitfab_span :process_order, type: "function"
def process_order(order_id)
# Your code here
{ status: "completed" }
end
end
# Use your code normally - spans are sent automatically
service = OrderService.new
service.process_order("order-123")
# Flush traces before exit (automatic via at_exit hook)
Bitfab.flush_traces
Configuration
Basic Configuration
Bitfab.configure(
api_key: "your-api-key"
)
Custom Service URL
Bitfab.configure(
api_key: "your-api-key",
service_url: "https://custom.example.com"
)
Disabling Span Sending
The enabled option controls whether spans are sent to Bitfab. When disabled, your code executes normally but no spans are created or sent.
# Disable tracing (useful for development/test environments)
Bitfab.configure(
api_key: ENV.fetch('BITFAB_API_KEY', 'dummy-key'),
enabled: false
)
Common patterns:
# Rails: Enable only in production
Bitfab.configure(
api_key: ENV.fetch('BITFAB_API_KEY', 'dummy-key'),
enabled: Rails.env.production?
)
# Environment variable control
Bitfab.configure(
api_key: ENV.fetch('BITFAB_API_KEY', 'dummy-key'),
enabled: ENV.fetch('BITFAB_ENABLED', 'false') == 'true'
)
# Multi-environment control
Bitfab.configure(
api_key: ENV.fetch('BITFAB_API_KEY', 'dummy-key'),
enabled: ['production', 'staging'].include?(ENV['RACK_ENV'])
)
When enabled: false:
- ✅ Code executes normally with no performance impact
- ✅ Return values and errors work as expected
- ✅ Nested spans are properly skipped
- ❌ No HTTP requests are made
- ❌ No span data is collected or sent
Usage
Class-Level Trace Function Key
All spans in the class share the same trace function key:
class PaymentService
include Bitfab::Traceable
bitfab_function "payment-processing"
bitfab_span :charge_card, type: "function"
def charge_card(amount)
# Traced automatically
end
bitfab_span :refund, type: "function"
def refund(transaction_id)
# Also uses "payment-processing" key
end
end
Per-Span Trace Function Key
Each span can declare its own key:
class NotificationService
include Bitfab::Traceable
bitfab_span :send_email, trace_function_key: "email-notifications", type: "function"
def send_email(to, subject)
# Uses "email-notifications" key
end
bitfab_span :send_sms, trace_function_key: "sms-notifications", type: "function"
def send_sms(to, )
# Uses "sms-notifications" key
end
end
Span Types
Bitfab supports the following span types:
"llm"- LLM API calls"agent"- Agent decision loops"function"- Business logic functions"guardrail"- Validation and safety checks"handoff"- Human-in-the-loop interactions"custom"- Custom span types (default)
bitfab_span :validate_input, type: "guardrail"
bitfab_span :call_openai, type: "llm"
bitfab_span :agent_loop, type: "agent"
Custom Span Names
By default, the method name is used as the span name. Override it:
bitfab_span :process_order, name: "ProcessOrderV2", type: "function"
def process_order(order_id)
# Span will be named "ProcessOrderV2"
end
Nested Spans
Spans automatically track parent-child relationships:
class OrderPipeline
include Bitfab::Traceable
bitfab_function "order-pipeline"
bitfab_span :process, type: "function"
def process(order_id)
validate(order_id)
# More processing
end
bitfab_span :validate, type: "guardrail"
def validate(order_id)
check_fraud(order_id)
# More validation
end
bitfab_span :check_fraud, type: "guardrail"
def check_fraud(order_id)
# Fraud checking logic
end
end
# Creates 3 nested spans:
# process (parent)
# └─ validate (child)
# └─ check_fraud (grandchild)
Input/Output Capture
Positional and keyword arguments are automatically captured:
bitfab_span :process_order, type: "function"
def process_order(order_id, priority: :normal)
# Input captured: { "order_id" => "123", "priority" => "normal" }
{ status: "completed" }
# Output captured: { "status" => "completed" }
end
Metadata
Add custom metadata to spans:
# Definition-time metadata
bitfab_span :process_order,
type: "function",
metadata: { "region" => "us-east", "version" => "v2" }
def process_order(order_id)
# ...
end
# Runtime metadata (inside a span)
bitfab_span :process_order, type: "function"
def process_order(order_id)
Bitfab.current_span.(
"user_id" => current_user.id,
"request_id" => request.id
)
# Runtime metadata merges with definition-time metadata
end
Wrapping External Code
Wrap third-party library methods without modifying them:
class ExternalHttpClient
def get(url)
# Third-party code
end
end
# Add tracing via wrap
Bitfab::Traceable.wrap(
ExternalHttpClient,
:get,
trace_function_key: "http-client",
type: "function"
)
# Now traced automatically
client = ExternalHttpClient.new
client.get("https://api.example.com")
Fluent API: client.get_function
Bind a trace_function_key once and wrap multiple methods or classes against it. Mirrors client.get_function in the Python SDK and client.getFunction in TypeScript.
fn = Bitfab.client.get_function("openai")
fn.wrap(OpenAI::Client, :chat, name: "Chat", type: "llm")
fn.wrap(OpenAI::Client, :embeddings, name: "Embed", type: "llm")
#wrap accepts the same options as Bitfab::Traceable.wrap (name, type, mock_on_replay), but the trace_function_key is fixed to the one bound on the BitfabFunction.
Replay with Mock Strategies
Replay reruns historical traces through your code so you can compare outputs after an iteration. By default every child span runs real code — fine for offline traces, but expensive when children make paid LLM/API calls. Three strategies control whether child spans return their historical output instead of executing:
# "none" (default): everything runs real code
client.replay(pipeline, :process, trace_function_key: "my-fn", mock: "none")
# "all": every child span returns its historical output
client.replay(pipeline, :process, trace_function_key: "my-fn", mock: "all")
# "marked": only spans tagged with `mock_on_replay: true` return historical output
client.replay(pipeline, :process, trace_function_key: "my-fn", mock: "marked")
Tag the spans you want mocked at definition time:
class Pipeline
include Bitfab::Traceable
bitfab_function "my-fn"
# mock_on_replay: true → returns historical output under mock: "marked"
bitfab_span :call_llm, type: "llm", mock_on_replay: true
def call_llm(prompt)
# paid OpenAI call — skip during replay
end
bitfab_span :transform, type: "function"
def transform(text)
# cheap, deterministic — keep running real
end
bitfab_span :process, type: "agent"
def process(text)
call_llm(text)
transform(text)
end
end
Use mock: "marked" when you want to iterate on process's logic without paying for the LLM call each run. Use mock: "all" for the cheapest possible replay (every child span returns its recorded output).
Error Handling
Errors are automatically captured and re-raised:
bitfab_span :risky_operation, type: "function"
def risky_operation
raise StandardError, "Something went wrong"
end
begin
service.risky_operation
rescue StandardError => e
# Error is captured in span and re-raised
puts "Caught: #{e.}"
end
Lifecycle
Automatic Flush
Spans are sent in background threads and automatically flushed on exit via at_exit hook.
Manual Flush
Wait for all pending spans to be sent:
Bitfab.flush_traces
# Blocks until all background threads complete
Reset Client
Clear the global client (useful for testing):
Bitfab.reset!
Thread Safety
- Each thread has its own span context stack (using
Thread.current) - Nested spans only work within the same thread
- Background span sending is thread-safe
Examples
See bitfab-ruby-example/ for complete working examples:
- test_span.rb - Basic span creation
- test_nested_spans.rb - Nested span hierarchies
- test_wrap.rb - Wrapping external code
- test_enabled.rb - Using the enabled flag
- test_metadata.rb - Custom metadata
Development
See DEVELOPMENT.md for development setup, testing, and publishing instructions.
License
MIT