4

General, high-level question for people who work with code regularly.

I was repurposing some old, outdated Javascript code into a Chrome extension, and to help me understand it, I wrote some quick code to help (see below). I regularly put messages in my code such as dbmsg('Begin function loadDefaults()'); or dbmsg('Settings saved to storage'); and the message is sent to the console. Whenever I need to debug something where the standard console messages aren't helping, I just turn on debugging. I figure this method is pretty simple and can be ported to other languages.

What other ways do you build debugging into your programs? I'm self-taught, so I don't know the standard practices, but I'm more interested in unique ways you've found to do this sort of thing.

/** Debugging Tools
 *
 * Set debugMode to 1 to show error messages, 0 to hide messages.
 */
var debugMode = 1;
function dbmsg(message) {
    if ( debugMode == 1 ) {
        var date = new Date();
        var time = date.getHours() + ':' 
            + date.getMinutes() + ':' 
            + date.getSeconds();
        console.log('DBM - ' + time + ': ' + message);
        if ( chrome.runtime.lastError ) {
            console.log('DBM lastError - ' + chrome.runtime.lastError);
        }
    } else if ( debugMode == 0 ) {
        return;
    }
}
if ( debugMode == 1 ) {
    console.log('Debug mode is currently ON. Each debug message is preceeded with "DBM" and the time the message was generated.');

    // Listen for changes in storage
    chrome.storage.onChanged.addListener(function(changes, namespace) {
        for (key in changes) {
            var storageChange = changes[key];
            dbmsg('Storage key "%s" in namespace "%s" changed. ' +
                  'Old value was "%s", new value is "%s".',
                  key,
                  namespace,
                  storageChange.oldValue,
                  storageChange.newValue);
        }
    });
} 
  • 4
    debugger; is usually all I use, that or set a breakpoint and examine from there, 1 step at a time. It's much more efficient then placing console.log's and judging by which one was fired when. – DJSpud Dec 6 '13 at 19:01
  • debugger is not a debugging message as much as it is a development tool. It's fine for when you want your code to stop somewhere, but is hardly the end-all. – DougM Dec 6 '13 at 19:22
  • @DougM The question is "how do you build debugging into your programs." Debugging messages (logging) are one tool, but breakpoints, leading to stepping-through manually, are far more useful for most people. – Katana314 Dec 6 '13 at 19:26
  • Do you not see the word "messages" in the question text @Katana314? – DougM Dec 6 '13 at 19:38
  • 2
    @DougM He used messages as an example, I believe because he wasn't aware of other methods of debugging. The bolded question is "What other ways do you build debugging in?" Just like if somebody lists their tricks of formatting an HTML page using tables, then asks how others can manage to use tables to make such complex sites, someone might suggest CSS as a much better way of accomplishing the task. – Katana314 Dec 6 '13 at 20:10
2

Debugging by creating execution traces is a much undervalued technique. It is a valuable tool for the programmers tool kit. Like any tool, it has its area of application; at other times a break point debugger is more useful, or generating a stack trace.

Debugging by traces is most use

  • when no other debugger is available, for various reasons.
  • when stopping the program would mess with timers.
  • when debugging a multithreaded program where you need to see actual interleaving of threads.
  • when a (subtle) bug develops over a long execution sequence.

The difference is that a break point debugger gives you a snapshot of the program state, and the trace shows what happens over time. Bugs that are hard to solve with one tool can be easier to crack with another tool.

The way to build trace tools varies with the target language. What is constant is the featutes to provide. Here is my list of basic features

  • Able to be turned on and off by setting a flag. The flag value can be hard-coded or set at run-time by debugging code.
  • For compiled languages the effect of the debugging code should be able to be completely removed. When turned off the debugging code should have no effect on the program. Debugging code can have bugs too, and always gives a performance hit.
  • Support a way of logging that a function or method has been entered. The log should contain the filename / class name, the method / function name, and each argument value.
  • Support a way of logging method or function exit, including return value.
  • Support for logging an arbitrary message.
  • Support logging to file or console.

More advanced features include

  • Indenting each log message according to the call depth.
  • Prefixing each log message with a date and time stamp.
  • Support multiple log files where N are retained.
  • Support for a maximum log file size, either by wrapping to the start or switching to a new file.
  • Support for logging levels, from none to extremely detailed.
  • Ability to select logging by source file / class name or feature name.

In C or C++, this can all be achieved with conditional compilation of macros in include files. By setting a compiler flag the macros can be completely turned off without touching the source code, an important ability to have. The code can be much more compact in C++ be because we can take advantage of polymorphism.

In Java, we can implement the debug code in a pair of jar files, one with all the debugging methods implemented, and the other with empty methods. This takes advantage of the JVM's optimisation abilities. Of course Log4J should be considered.

In scripting languages you can mix and match the above techniques to achieve the same effect. For html / javascript you can use the script tag to include the required javascript debugging source. If the page is generated from PHP you can conditionally include the debugging code in the page.

The basic features can be built in about 50 lines of code (as I recall, since I'm not as my desk machine), and the advanced features can be another 100 or so lines.

The function trace features described above can be implemented in Javascript (using your dbmsg function) as (not tested):

function dbcall( /* name, args */ ) {
  var msg = arguments[0] + '( ';
  for (var i = 1; i < arguments.length - 1; i++)
    msg += arguments[i] + ', ';
  msg += arguments[arguments.length - 1] + ' )';
  dbmsg( msg );
}

function dbretn( /* name, value */ ) {
  var msg = arguments[0] + ' returns ';
  msg += arguments[1];
  dbmsg( msg );
}

In a language that supports macros we can set up a macro that calls dbretn that does the return, but in javascript we need to add the call in front of each return as in

...
dbretn( `MyFunction`, value );
return value;

The storage.changed listener you show in the question is a good example of how such debugging facilities should be adapted the each environment.

  • Things might be slightly different with a browser-side language like Javascript. – rwong Dec 7 '13 at 6:18
  • @rwong The main difference being in logging to file. That's the kind of thing we have to account for in each environment. Thanks for the point, I'll update the answer later. – andy256 Dec 7 '13 at 6:21
1

I have a random bias against enabling or disabling "debug state" by way of a runtime variable or constant. At best, you could have a global "log level" setting that says how SEVERE messages have to be in order to log, but even that's an implementation detail.

My actual preference is to have each entry-point or major method report its existence to some part of the application specialized for logging, and do so again on errors, warnings, or significant results. What that part is and how it works will vary based on the specific stack you're using.

When I was writing T-SQL scripts, I made liberal use of the PRINT command.

CREATE PROCEDURE doStuff(@arg1 int, @arg2 char)
  PRINT 'doStuff(' + @arg1 + ', ' + @arg2 + ')'
  ...
ENDPROC

For JavaScript, I made a habit of a simple wrapper as you did, but I just tested for the presence or absence of console and let the browser decide if it had to actually keep the log or not.

function log() {
  if(console && console.log) {
    var msg = "";
    for(var a in arguments) {
      if(msg != "") {
        msg += " "
      }
      msg += a;
    }
    console.log(msg)
  }
}

function doStuff(arg1, arg2) {
  log('doStuff(', arg1, ', ', arg2, ')');
}

With the work I'm doing now, one of the additions I made to our next major release was a new reporting class, for all of this sort of thing. But that's a client-side project in a largely dead language, and wouldn't be entirely useful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.