DebugMe
Quick Start
gem install debug_me
require 'debug_me'
include DebugMe
my_variable = 42
debug_me { :my_variable } # Use a Symbol for the variable name
# or if you have several variables you want to see ...
debug_me { %i[ var1 var2 var3 var4 ]}DebugMe::debug_me(){} works with local, instance and class variables.
Installation
Add this line to your application's Gemfile:
bundle add 'debug_me'Or install it yourself:
gem install debug_meExamples Usage
require 'debug_me'
include DebugMe
debug_me # Prints only the header banner consisting of tag, method name, file name and line number
debug_me('INFO') # Also prints only the header but with a different tag
debug_me(levels: 5) # Along with the header, show the call stack back this many levels
debug_me{:backtrace} # show the entire call stack
debug_me{[ :backtrace ]} will # show the entire call stack
debug_me(levels: 5){[ :backtrace ]} # will only show the first 5 levels of the backtrace - ie. the levels parameter supersedes :backtrace
debug_me {} # prints the default header and __ALL__ variables
debug_me {:just_this_variable} # prints the default header and the value of only one specific variable
debug_me { %i[ this_one that_one that_other_one ]} # prints default header and three specific variables
# Use an array of symbols and strings to pass multiple variables for output
# Each element of the array is 'eval'ed with the context binding of the caller
debug_me(){[ :my_var, 'my_complex_var_or_method[my_var]' ]}
debug_me(header: false) {} # disables the printing of the header; prints all variables
debug_me(tag: 'MyTag', :header => false) {} # disables header, sets different tag, prints all variables
debug_me('=== LOOK ===') {} # changes the tag and prints all variables with a header line
debug_me('=== LOOK ===') {:@foo} # changes the tag, prints a header line and a specific instance variable
debug_me('=== LOOK ===') {:@@foo} # changes the tag, prints a header line and a specific class variable
debug_me(ivar: false, cvar: false) {} # print only the local variables with the default tag and a header lineMost of the examples above use symbols to designate the variables that you want to be shown with their name as a label. You can also use strings. With strings you are not limited to just variables. Consider these examples:
debug_me {[ 'some_array.size', 'SomeDatabaseModel.count' ]}
# What a backtrace with your variables?
debug_me {[
:my_variable,
'some_hash.keys.reject{|k| k.to_s.start_with?('A')}',
'caller' ]} # yes, caller is a kernel method that will give a backtrace
# You can also get into trouble so be careful. The symbols and strings
# are evaluated in the context of the caller. Within the string any
# command or line of code can be given. SO DO NOT try to use
# something silly like debug_me{ 'system("rm -fr /")'}Security Warning
IMPORTANT: debug_me uses eval() to evaluate the symbols and strings you pass to it in the context of the caller. This provides powerful flexibility but comes with significant security implications:
-
Never pass untrusted input to
debug_me -
Never use
debug_mewith user-supplied data or parameters - Only use
debug_meduring development and testing - Remove or disable
debug_mecalls in production code (use$DEBUG_ME = false)
The ability to execute arbitrary code is by design for debugging flexibility, but it means any string passed to debug_me will be evaluated as Ruby code. Treat this tool with the same caution you would any eval() statement.
For production environments, consider using a proper logging framework with structured logging instead of debug_me.
Disabling Debug Output with $DEBUG_ME
The $DEBUG_ME global variable provides a convenient way to enable or disable all debug_me output throughout your application without removing the code. This is particularly useful for QA environments.
Environment Variable Control (Recommended)
The DEBUG_ME environment variable is automatically detected when the gem is loaded. This is the recommended approach for controlling debug output across environments without code changes.
# Disable debug output (QA)
export DEBUG_ME=false
# Enable debug output (development)
export DEBUG_ME=true
# Or set inline when running your app
DEBUG_ME=false bundle exec rails serverSupported values:
-
Truthy (enables output):
true,yes,1,on, or any other value -
Falsy (disables output):
false,no,0,off, or empty string -
Not set: Defaults to
true(enabled)
Runtime Control
You can also control $DEBUG_ME programmatically at runtime:
# Enable debug output (default)
$DEBUG_ME = true
# Disable all debug_me output
$DEBUG_ME = falseWhen $DEBUG_ME is set to false:
- All
debug_mecalls returnnilimmediately - No output is produced (even with custom file or logger options)
- The block is not evaluated, so there's zero performance overhead
Production Setup Examples
Docker/Container deployment:
# Dockerfile
ENV DEBUG_ME=falseSystemd service:
[Service]
Environment="DEBUG_ME=false"Heroku:
heroku config:set DEBUG_ME=falseFor Rails with fallback (only if ENV not set):
# config/initializers/debug_me.rb
require 'debug_me'
# Override only if not already set by environment
$DEBUG_ME = Rails.env.development? || Rails.env.test? unless ENV.key?('DEBUG_ME')Benefits
-
Leave debug code in place: No need to remove
debug_mecalls before deployment - Zero overhead: When disabled, blocks aren't evaluated, so no performance impact
- Easy toggling: Can enable debugging in production temporarily if needed
- Security: Prevents accidental debug output exposure in production
Example
# Your code with debug_me calls
def process_data(items)
debug_me { :items } # Only outputs in development/test
result = items.map do |item|
debug_me { :item } # Only outputs in development/test
transform(item)
end
debug_me { :result } # Only outputs in development/test
result
end
# In production with $DEBUG_ME = false:
# - No output produced
# - No performance overhead
# - Code works normallyDefault Options
The default options is a global constant DebugMeDefaultOptions that is outside of the DebugMe name space. I did that so that if you do include DebugMe to make access to the method easier you could still have the constant with a function specific name that would be outside of anything that you may have already coded in you program.
Notice that this constant is outside of the DebugMe's module namespace.
DebugMeDefaultOptions = {
tag: 'DEBUG', # A tag to prepend to each output line
time: true, # Include a time-stamp in front of the tag
strftime: '%Y-%m-%d %H:%M:%S.%6N', # timestamp format
header: true, # Print a header string before printing the variables
levels: 0, # Number of additional backtrack entries to display
skip1: false, # skip 1 blank line in front of a new output block
skip2: false, # skip 2 blank lines in front of a new output block
lvar: true, # Include local variables
ivar: true, # Include instance variables in the output
cvar: true, # Include class variables in the output
cconst: true, # Include class constants
logger: nil, # Pass in a logger class instance like Rails.logger
file: $stdout # The output file
}If you want the output of the method to always go to STDERR then do this:
require 'debug_me'
DebugMeDefaultOptions[:file] = $stderr # or STDERRIf you want the debug_me output to go to a real file:
DebugMeDefaultOptions[:file] = File.open('debug_me.log', 'w')Using a Logger class instance
If you are working in Rails and want all the debug_me output to go to the Rails.logger its as easy as:
DebugMeDefaultOptions[:logger] = Rails.loggerOr while working in rails you only want to add a marker to the Rails.logger do this:
debug_me(logger: Rails.logger, tag: 'Hello World')If you are working in Rails and want to use both the standard debug_me functions and put stuff into the Rails.logger but do not want to always remember the option settings then do something line this in a config/initializers/aaaaa_debug_me.rb file:
# config/initializers/aaaaa_debug_me.rb
require 'debug_me'
module DebugMe
def log_me(message, options={}, &block)
options = {logger: Rails.logger, time: false, header: false, tag: message}
block_given? ? debug_me(options, block) : debug_me(options)
end
end
include DebugMe
# Just setup the base name. The parent path will be added below ...
debug_me_file_name = 'debug_me'
# NOTE: you could add a timestamp to the filename
# debug_me_file_name += '_' + Time.now.strftime('%Y%m%d%H%M%S')
debug_me_file_name += '_' + Process.argv0.gsub('/',' ').split(' ').last
# NOTE: by using the Process.argv0 ... you get multiple log files for
# rails, rake, sidekiq etc.
debug_me_file_name += '.log'
debug_me_filepath = Rails.root + 'log' + debug_me_file_name
debug_me_file = File.open(debug_me_filepath, 'w')
debug_me_file.puts <<~RULER
==================== Starting New Test Run --------->>>>>>
RULER
# Set application wide options in the DebugMeDefaultOptions hash
DebugMeDefaultOptions[:file] = debug_me_file
debug_me{['ENV']}What that does for your Rails application is dump all of your system environment variables and their values to a debug_me log file in the log directory of the application.
It also adds a new method log_me which you can use to send stuff to the Rails.logger instance. The method used by debug_me for the logger instance is always the debug method.
Conclusion
The rest of the default options are obvious.
You can always over-ride the default options on a case by case basis like this:
debug_me {...}
# ...
debug_me(header: false){...}Contributing
- Fork it ( https://github.com/[my-github-username]/debug_me/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
