Project

observable

0.0
The project is in a healthy, maintained state
A Ruby gem that provides automated OpenTelemetry instrumentation for method calls with configurable serialization, PII filtering, and argument tracking
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 5.14
~> 13.0
~> 0.22
~> 1.40

Runtime

 Project Readme

Observable

Automatic OpenTelemetry instrumentation for Ruby methods with configurable serialization, PII filtering, and argument tracking.

Getting Started

bundle add observable

Or

# Gemfile
gem 'observable'

Basic usage:

require 'observable'

class UserService
  def initialize
    @instrumenter = Observable::Instrumenter.new
  end

  def create_user(name, email)
    @instrumenter.instrument(binding) do
      User.create(name: name, email: email)
    end
  end
end

OpenTelemetry spans are automatically created with method names, arguments, return values, and exceptions.

Configuration

Configure globally or per-instrumenter:

# Global configuration
Observable::Configuration.configure do |config|
  config.tracer_name = "my_app"
  config.transport = :otel
  config.app_namespace = "my_app"
  config.attribute_namespace = "my_app"
  config.track_return_values = true
  config.serialization_depth = {default: 2, "MyClass" => 3}
  config.formatters = {default: :to_h, "MyClass" => :to_formatted_h}
  config.pii_filters = [/password/i, /secret/i]
end

# Per-instrumenter configuration
config = Observable::Configuration.new
config.track_return_values = false
instrumenter = Observable::Instrumenter.new(config: config)

Configuration Options

  • tracer_name: "observable" - Name for the OpenTelemetry tracer
  • transport: :otel - Uses OpenTelemetry SDK
  • app_namespace: "app" - Namespace for application-specific attributes
  • attribute_namespace: "app" - Namespace for span attributes
  • track_return_values: true - Captures method return values in spans
  • serialization_depth: {default: 2} - Per-class serialization depth limits (Hash or Integer for backward compatibility)
  • formatters: {default: :to_h} - Object serialization methods by class name
  • pii_filters: [] - Regex patterns to filter sensitive data from spans

OpenTelemetry Integration

This library seamlessly integrates with OpenTelemetry, the industry-standard observability framework. Spans are automatically created with standardized naming (Class#method or Class.method) and include rich metadata about method invocations, making your Ruby applications immediately observable without manual instrumentation.

Custom Formatters

Control how domain objects are serialized in spans by configuring custom formatters.

Observable::Configuration.configure do |config|
  config.formatters = {
    default: :to_h,
    'YourCustomClass' => :to_formatted_h
  }
  config.serialization_depth = {
    default: 2,
    'YourCustomClass' => 3
  }
end

Example

A domain object Customer has an Invoice.

Objective

Only send the invoice ID to the trace to save data.

Background

Imagine domain objects are Dry::Struct value objects:

class Customer < Dry::Struct
  attribute :id, Dry.Types::String
  attribute :name, Dry.Types::String
  attribute :Invoice, Invoice
end

class Invoice < Dry::Struct
  attribute :id, Dry.Types::String
  attribute :status, Dry.Types::String
  attribute :line_items, Dry.Types::Array
end

Solution

  1. Define custom formatting method - #to_formatted_h
 class Customer < Dry::Struct
   attribute :id, Dry.Types::String
   attribute :name, Dry.Types::String
   attribute :Invoice, Invoice

+  def to_formatted_h
+    {
+      id: id,
+      name: name,
+      invoice: {
+       id: invoice.id
+      }
+    }
+  end
 end
  1. Configure observable:
Observable::Configuration.configure do |config|
  config.formatters = {
    default: :to_h,
    'Customer' => :to_formatted_h
  }
  config.serialization_depth = {
    default: 2,
    'Customer' => 3
  }
end

The instrumenter tries class-specific formatters first, then falls back to the default formatter, then to_s.

Benefits

Why use this library? Why not write Otel attributes manually?

  • Zero-touch instrumentation - Wrap any method call without modifying existing code or manually creating spans
  • Production-ready safety - Built-in PII filtering, serialization depth limits, and exception handling prevent common observability pitfalls
  • Standardized telemetry - Consistent span naming, attribute structure, and OpenTelemetry compliance across your entire application

License

MIT License. See LICENSE file for details.