Running Code Only in Debug Mode
Problem
You want to print out debugging messages or run some sanity-checking code, but only while you e developing your application;, not when you e running it in production.
Solution
Run the code only if the global variable $DEBUG is true. You can trigger debug mode by passing in the --debug switch to the Ruby interpreter, or you can set the variable $DEBUG to true within your code.
Heres a Ruby program to divide two random numbers. It contains a trivial bug. It usually runs to completion, but sometimes it crashes. A line of debug code has been added to give some more visibility into the internal workings of the program:
#!/usr/bin/env ruby # divide.rb numerator = rand(100) denominator = rand(10) $stderr.puts "Dividing #{numerator} by #{denominator}" if $DEBUG puts numerator / denominator
When run with the --debug flag, the debug message is printed to standard error:
$ ./divide.rb --debug Dividing 64 by 9 7 $ ./divide.rb --debug Dividing 93 by 2 46 $ ./divide.rb --debug Dividing 54 by 0 Exception eroDivisionError at divide_buggy.rb:6 - divided by 0 divide_buggy.rb:6:in /: divided by 0 (ZeroDivisionError) from divide_buggy.rb:6
Once the bug is fixed, you can go back to running the script normally, and the debug message won show up:
Discussion
This is a common technique when a "real" debugger is too much trouble. Its usually used to send debug messages to standard error, but you can put any code at all within a $DEBUG conditional. For instance, many Ruby libraries have their own "verbose", " debug level", or " debug mode" settings: you can choose to set these other variables appropriately only when $DEBUG is true.
require fileutils FileUtils.cp(source, destination, $DEBUG)
If your code is running deep within a framework, you may not have immediate access to the standard error stream of the process. You can always have your debug code write to a temporary logfile, and monitor the file.
Use of $DEBUG costs a little speed, but except in tight loops its not noticeable. At the cost of a little more speed, you can save yourself some typing by defining convenience methods like this one:
def pdebug(str) $stderr.puts(DEBUG: + str) if $DEBUG end pdebug "Dividing #{numerator} by #{denominator}"
Once youve fixed the bug and you no longer need the debugging code, its better to put it into a conditional than to simply remove it. If the problem recurs later, youll find yourself adding the debugging code right back in.
Sometimes commenting out the debugging code is better than putting it into a conditional. Its more difficult to hunt down all the commented-out code, but you can pick and choose which pieces of code to uncomment. With the $DEBUG technique, its all or nothing.
It doesn have to be all or nothing, though. $DEBUG starts out a boolean but it doesn have to stay that way: you can make it a numeric "debug level". Instead of doing something if $DEBUG, you can check whether $DEBUG is greater than a certain number. A very important piece of debug code might be associated with a debug level of 1; a relatively unused piece might have a debug level of 5. Setting $DEBUG to zero would turn off debugging altogether.
Here are some convenience methods that make it easy to use $DEBUG as either a boolean or a numeric value:
def debug(if_level) yield if ($DEBUG == true) || ($DEBUG && $DEBUG >= if_level) end def pdebug(str, if_level=1) debug(if_level) { $stderr.puts "DEBUG: " + str } end
One final note: make sure that you put the --debug switch on the command line before the name of your Ruby script. Its an argument to the Ruby interpreter, nottoyour script.
See Also
- Recipe 17.5, "Adding Logging to Your Application," demonstrates a named system of debug levels; in fact, if your debug messages are mainly diagnostic, you might want to implement them as log messages
Категории