IDbg
is a debugging toolkit for Ruby and Ruby on Rails projects. It aims to increase the velocity and accuracy of a mid-level debugging workflow:
- isolated durable log stream
- various break conditions
- hit metrics and reactors
- call inspectors
- and more
It's a single file non intrusive (no need to commit) addition to any Ruby projects.
For docs/setup best to read the file:
Lines 3 to 187 in f59b7ea
############################################################################### | |
# DOCUMENTATION | |
############################################################################### | |
# | |
# IDbg is an opinionated debug helper toolkit for the world where Ruby | |
# debugging is destroyed so much we need to back to `puts`. | |
# | |
# There are specific aspects this toolkit is trying to address: | |
# - debuggers are not reliable (JetBrain, debug-ide, pry, etc) | |
# - breaking is not always the best or desired way | |
# - updating code (even debugging code) can trigger a sluggish auto source- | |
# -reload mechanism | |
# - debugging sometimes requires tricks | |
############################################################################### | |
# How to install? | |
# | |
# IDbg was designed for a Ruby backend workspace where things should stay | |
# separate (out of git) and low footprint. Hence my workflow is the following: | |
# - copy `i_dbg.rb` into one of the project folder that is autoloading this | |
# file | |
# - add it to your global gitignore | |
# - edit the configurations (best is via environment variables, but if that | |
# does not work, just hardcode it in the section below) | |
# - make a folder for aid scripts and temp files and set it in the config: | |
# `IDBG_SCRIPTS_FOLDER` | |
############################################################################### | |
# How to use? | |
# | |
# Simplest example is to log messages uninterrupted. In a desired place insert | |
# a log: | |
# | |
# ```ruby | |
# # Some backend file you're debugging. | |
# ... | |
# class AppController < ApplicationController | |
# def update | |
# IDbg.log("Received params", params, @user, @ctx) | |
# end | |
# end | |
# ``` | |
# | |
# Then open the log and watch: | |
# | |
# ```bash | |
# $> tail -F /tmp/idbg.log | |
# ``` | |
############################################################################### | |
# Components | |
# | |
# Component: logger | |
# | |
# Logger is logging all input to a semi-structured file, so it's both isolated | |
# and convenient for a watcher, without stopping code execution. | |
# | |
# ```ruby | |
# IDbg.log(@user, "was logged in with", @user_access) | |
# IDbg << "Or simply this." | |
# ``` | |
# | |
# On Apple OS-X only the system notification can be used too: | |
# | |
# ```ruby | |
# IDbg.flash("User is deleted", @user) | |
# ``` | |
# | |
# --- | |
# Component: instance call tracker | |
# | |
# Say you're interested in knowing the order of execution and what functions | |
# were executed in a class during a flow. IDbg allows logging all calls and | |
# with or without arguments: | |
# | |
# ```ruby | |
# class SomeClass | |
# # Insert before closing `end`: | |
# include(IDbg.function_logger.with_args) | |
# end | |
# ``` | |
# | |
# --- | |
# Component: complex debug script reactors | |
# | |
# Sometimes a debugging is just so convoluted or maybe it's even evolving into | |
# its own code that it's better to keep it somewhere else. As well - these | |
# scripts act as a signal. | |
# This has two flavors: execution of a custom script which breaks the flow | |
# with `pry` when it results truthy - and the other one that yields to a block | |
# when evals to truthy. | |
# Said scripts must be placed in `IDBG_SCRIPTS_FOLDER/break.rb` as functions. | |
# | |
# ```ruby | |
# # Inside IDBG_SCRIPTS_FOLDER/break.rb | |
# def my_script | |
# # do some things | |
# return true # in case we need a reaction | |
# end | |
# | |
# # Inside application code (will block with `binding.pry`, since its true): | |
# IDbg.break_if(:my_script) | |
# | |
# # Or a custom block version: | |
# IDbg.yield_if(:my_script) { Rails.cache.clear ; @user.reload } | |
# ``` | |
# An expected side effect of these is that they do not trigger source-code | |
# reload when the script is updated. | |
# | |
# Call params can be passed/inspected too: | |
# | |
# ```ruby | |
# # Inside IDBG_SCRIPTS_FOLDER/break.rb | |
# def my_script | |
# args = IDbg::DataBank.data | |
# end | |
# | |
# # Inside application code (will block with `binding.pry`, since its true): | |
# IDbg.break_if(:my_script, "arg1", { arg2: "foo" }) | |
# ``` | |
# | |
# It's also possible to run whole script files without expected reaction when | |
# the logic deserves its own file. These files are expected to exist in | |
# `IDBG_SCRIPTS_FOLDER/<NAME>.rb`: | |
# | |
# ```ruby | |
# IDbg.run("user_registration_script") | |
# ``` | |
# | |
# --- | |
# Component: backtrace | |
# | |
# Often you want to know where you are in the execution. IDbg's backtrace | |
# can be customized with length and levels. | |
# | |
# ```ruby | |
# # Log a backtrace to the log file. | |
# IDbg.backtrace(level: 2) | |
# # Dump it right on the current output: | |
# IDbg.dump_backtrace | |
# # Combine backtrace and logging | |
# IDbg.backtrace(@user, @ctx) | |
# ``` | |
# | |
# Levels are generally used by gradually filtering out external components, | |
# such as: gems > external libs > internal libs > components > ... Level 0 | |
# is always the full backtrace. | |
# For configuration see: `IDBG_BACKTRACE_LEVEL_FILTERS` | |
# Example of a level setting where level-1 is filtering to all-except-gems and | |
# level-2 is only-rails-app: | |
# `export IDBG_BACKTRACE_LEVEL_FILTERS="my_project,my_project/app"` | |
# | |
# --- | |
# Component: counter | |
# | |
# Counter is counting each call. | |
# | |
# ```ruby | |
# IDbg.count("user-reload") | |
# ``` | |
# | |
# --- | |
# Component: once-calls | |
# | |
# Sometimes a debugging or testing code snippet only should be called once | |
# only. | |
# | |
# ```ruby | |
# # Hypothetical loop where we only care about the first iteration. | |
# IDbg.reset_once(:cache_check) | |
# loop do | |
# IDbg.once(:cache_check, @cache) | |
# end | |
# ``` | |
# | |
# --- | |
# Component: measure | |
# | |
# If you need to measure the time spent in a block: | |
# | |
# ```ruby | |
# IDbg.measure(:my_critical_block) do | |
# # code ... | |
# end | |
# ``` |