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, message)
    # 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")

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.message}"
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:

Development

See DEVELOPMENT.md for development setup, testing, and publishing instructions.

License

MIT