View Source ExDbug

Hex.pm Docs

Debug utility for Elixir applications, inspired by the Node.js 'debug' package. Version 2.0 introduces decorator-based function tracing while maintaining compatibility with 1.x style debugging.

Features

  • 🎯 Decorator-based function tracing - Zero-cost, compile-time instrumentation
  • 🔄 1.x Compatibility Mode - Seamless upgrade path from earlier versions
  • 🔍 Namespace-based filtering - Filter debug output by context
  • 📊 Rich metadata support - Attach and format detailed debug information
  • Zero runtime cost when disabled - Compile-time optimization
  • 🌍 Environment variable-based filtering - Easy runtime control
  • 📝 Automatic metadata truncation - Smart handling of large values
  • 🔧 Hierarchical configuration - Global, module, and function-level settings
  • 📈 Value tracking - Monitor values through pipelines
  • ⏱️ Optional timing and stack traces - Deep insights when needed

Installation

Add ex_dbug to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_dbug, "~> 2.0"}
  ]
end

Usage (2.0 Style)

The new decorator-based approach makes debugging more elegant and maintainable:

defmodule MyApp.Worker do
  use ExDbug, enabled: true

  # Simple debug trace
  @decorate dbug()
  def process(data) do
    # Implementation
  end

  # Configured debug trace
  @decorate dbug(context: :important)
  def process_important(data) do
    # Implementation
  end

  # Debug all functions in module
  @decorate_all dbug()
  
  def bulk_1(arg), do: arg
  def bulk_2(arg), do: arg
end

Compatibility Mode (1.x Style)

For existing projects or gradual migration, use compatibility mode:

defmodule MyApp.LegacyWorker do
  use ExDbug, compatibility_mode: true

  def process(data) do
    dbug("Processing data", size: byte_size(data))
    # ... processing logic
    dbug("Completed processing", status: :ok)
  end
end

Configuration

Compile-Time Configuration

In your config.exs:

config :ex_dbug,
  enabled: true,
  config: [
    max_depth: 3,
    include_timing: true,
    include_stack: true,
    truncate: 100,
    levels: [:debug, :error]
  ]

Module-Level Configuration

use ExDbug,
  enabled: true,
  max_depth: 5,
  include_timing: true,
  include_stack: false,
  levels: [:debug, :error]

Function-Level Configuration (2.0)

@decorate dbug(
  context: :important,
  include_timing: true,
  include_stack: true
)
def critical_function(arg) do
  # Implementation
end

Runtime Configuration

Control debug output using the DEBUG environment variable:

# Enable all debug output
DEBUG="*" mix run

# Enable specific namespace
DEBUG="myapp:worker" mix run

# Enable multiple patterns
DEBUG="myapp:*,other:thing" mix run

# Enable all except specific namespace
DEBUG="*,-myapp:secret" mix run

Migrating from 1.x to 2.0

Replace debug calls with decorators:

# Before (1.x)
def process(arg) do
  dbug("Processing", value: arg)
  # Implementation
end

# After (2.0)
@decorate dbug()
def process(arg) do
  # Implementation
end

Option 2: Compatibility Mode

For gradual migration, enable compatibility mode:

use ExDbug, compatibility_mode: true
# All 1.x code continues to work

Configuration Updates

Update your configuration to use the new hierarchical structure:

# Before (1.x)
config :ex_dbug, enabled: true

# After (2.0)
config :ex_dbug,
  enabled: true,
  config: [
    max_depth: 3,
    include_timing: true
  ]

Best Practices

  1. Use descriptive context names matching your application structure
  2. Prefer decorator-based debugging for new code
  3. Use compatibility mode for gradual migration
  4. Set appropriate DEBUG patterns for different environments
  5. Configure hierarchically (global → module → function)
  6. Disable in production for zero overhead

Production Use

While ExDbug has minimal overhead when disabled, it's recommended to set config :ex_dbug, enabled: false in production unless debugging is specifically needed. This ensures zero runtime cost as debug calls are compiled out completely.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

License

MIT License - see LICENSE.md for details.