Gavin Sinclair's Ruby Projects whitestone col debuglog

Debuglog – a zero-conf debug.log file

Synopsis

Debuglog gives you debug, trace and time methods that write their output to the file ./debug.log.

    require 'debuglog'     # or require 'debuglog/auto'

    debug "Creating #{n} connections"
    trace "names.first", binding
    time('Process config file') { Subsystem.configure(ARGV.shift) }

The log file (default ./debug.log) will look something like this:

DebugLog -- 2011-12-28 18:58:22 +1000
-------------------------------------
[00.3] Creating 14 connections
[00.8] names.first == "Sandy White"
[01.9] Process config file: 1.0831 sec

The [00.3] etc. is the number of seconds (rounded) since the program started (well, since require 'debuglog', anyway).

You can use different method names (to avoid a clash) and a different filename with some initial configuration.

    require 'debuglog/manual'

    DebugLog.configure(
      :debug => :my_debug,
      :trace => :my_trace,
      :time  => :my_time,
      :filename => 'log/xyz.log'
    )

    my_debug "Creating #{n} connections"
    my_trace "names.first", binding
    my_time('Process config file') { Subsystem.configure(ARGV.shift) }

More nuanced configuration is possible; see Configuration.

Installation

$ [sudo] gem install debuglog

Source code is hosted on Github. See Project details.

Description

Debuglog allows you easy access to a log file named debug.log in the current directory. In that file, you can record:

Of course, any or all of those methods names might be used by another library or by your own code. You can choose different method names and a different filename; see Configuration. Debuglog will raise an exception (DebugLog::Error) if it detects a method clash.

debug

The debug method is straightforward. It calls to_s on its argument(s) and writes them to the log file.

trace

trace emits the value of an expression. You are required to pass the binding with the binding method.

The two following lines are equivalent:

    trace :radius, binding
    debug "radius == #{radius.pretty_inspect}"

Tip: You may choose to alias _b binding for convenience; DebugLog doesn’t do that for you.

If you want the output truncated, pass an integer argument:

    trace :text, binding, 30

The above examples use a symbol to trace a variable. You can, however, pass in any expression:

    trace "names.find { |n| n.length > 7 }", binding

time

time calculates the amount of time taken to execute the code in the block given and records it in the log file.

    time("Process configuration file") { @options = parse_config }

It requires a single string (#to_s) argument and a block.

Notes

Debuglog is a synonym for DebugLog, so you don’t have to trouble yourself to remember the capitalisation.

The text written to the log file has some nice touches:

Configuration

The Synopsis gave a good example of configuration:

    require 'debuglog/manual'

    DebugLog.configure(
      :debug => :my_debug,
      :trace => :my_trace,
      :time  => :my_time,
      :filename => 'log/xyz.log'
    )

This changes the names of the methods that Debuglog defines. The motivation for that, of course, is to avoid a name clash with another library or your own code. Debuglog will raise an exception (DebugLog::Error) if it detects a method name clash. (Of course, you might load the other library after Debuglog, in which case it won’t detect the clash.) This precaution is taken because they are common method names at the top-level, and it’s just not right for a debugging library to cause bugs.

If you omit a method name from the configuration, that method will not be defined. The following code defines the method d instead of debug, but does not define trace or time. The standard filename debug.log is used.

    DebugLog.configure(
      :debug => :d,
    )

If you want to change one or two methods but leave the rest as standard, simply do:

    DebugLog.configure(
      :debug => :d,
      :trace => :trace,
      :time  => :time
    )

Once you have called DebugLog.configure, any further calls to it will be ignored with a message on STDERR. That includes this case:

    require 'debuglog'           # should be 'debuglog/manual'

    DebugLog.configure(...)

The code require 'debuglog' is equivalent to the following code, meaning your one shot at calling configure has been taken.

    require 'debuglog/manual'

    DebugLog.configure(
      :debug => :debug,
      :trace => :trace,
      :time  => :time,
      :file  => 'debug.log'
    )

Final note: if you specify a file that is not writable, an error (DebugLog::Error) will be raised.

Endnotes

Motivation

Debugging to a log file is very useful, even if your program doesn’t do “logging” as such. Years ago I released dev-utils/debug which did this and intended to do more. That’s outdated now, only working on 1.8, so a revisit was worthwhile with a better name. (If anyone wants the name dev-utils for something else, they’re welcome to it.)

Dependencies and requirements

Debuglog does not depend on any other libraries. It is tested on Ruby versions 1.8.[67] and 1.9.[123]. It should continue to work on future 1.9 releases.

Unit tests are implemented in Whitestone.

Project details

History

See History.txt for more details.

Possible future enhancements

Color. For instance, debug "!! Object pool overloaded" could print the message (minus the exclamation marks) in red. Traces could be in yellow. Times could be in dark cyan, etc.

Further to the above: symbol arguments to debug could provide some color using the Col library. E.g. debug "blah...", :yb for yellow bold.

Method to turn it off and on: DebugLog.off and DebugLog.on or something.

Indenting via DebugLog.indent and DebugLog.outdent.

Options for trace output: p for :inspect; y for :to_yaml etc. I don’t see why the current :pretty_inspect would ever be insufficient, but of course there may be cases.