Feb
21
2010

Debugging Firefox Extension Using Log File

Problog

In this post I will cover the following topics:

  • Understanding debug requirements
  • Using the Mozilla preferences service to store debug mode
  • Using Mozilla file I/O to write debug messages into a file

In one of me recent post I’ve talked about how to setup a development environment in Firefox. One of the problems in developing firefox extensions and i think software in general is how to debug a problem occurring on client machine that is unreproducible at the development side.

It’s hard enough to debug firefox extensions locally;  to try and understand what happened on a remote machine is very difficult. To debug on the local side I usually prefer to log information to the Firebug console as a mechanism to trace what is going on inside my Firefox extension. I know many developers just use an alert as the best way to debug but it’s synchronous.  Also it doesn’t scale, for example to debug a loop of 1000 elements. It’s not that I never use the alert function for debugging.  I often use it, but only for unit testing and occasionally functional testing.  In order to understand what’s going on at a system-wide level, I think the console is the best choice, at least for now.

I’m working on a cool firefox extension right now, and wanted to share the debug object i wrote – DebugObj.

Debug Object Requirements

First of all, I wanted to control the mode of the debug object. i decided to implement 3 modes:

  • console – writing debug string into the console. I will use this mode for my own debugging.
  • file – writing debug string into a local file. This mode will be used when a remote machine is having a problem and we want to see some inside stuff going on.
  • none – the debug object is not doing anything. this will be the default release version in order to save unnecessary calls to the console and file.

I also wanted to a way to change the debug mode in an easy and persistent way.

Debug Object Implementation

First let’s start by defining the debug object and setting some properties to it:


var debugObj = {
    mode: "none",
    isInit: false,
    consoleService: null,
    file: null,
    foStream: null,
    converter: null
};

The mode property states the debug mode of the object and the isInit states if we are ready to start writing debug messages. We will get into the other properties later.
Now let’s take a look on the init method:


init: function(){
    if( debugObj.isInit ) return;
     var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("extensions.myextensionname.");
    debugObj.mode = pref.getCharPref("DebugMode");
    debugObj.consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);

    if( debugObj.mode == "file" ){
        debugObj.file = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("Home", Components.interfaces.nsIFile);
        debugObj.file.append("myextension_debug.log");

        debugObj.foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);

        // use 0x02 | 0x10 to open file for appending.
        debugObj.foStream.init(debugObj.file, 0x02 | 0x08 | 0x20, 0666, 0);

        // if you are sure there will never ever be any non-ascii text in data you can
         // also call foStream.writeData directly
        debugObj.converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
        debugObj.converter.init(debugObj.foStream, "UTF-8", 0, 0);
        debugObj.converter.writeString("------------------------------------------------------------------------\r\nDEBUG STARTS - "+Date()+"\r\nBrowser Info:"+window.navigator.userAgent+"; running on "+window.navigator.oscpu+" "+window.navigator.platform+";\r\nlanguage: "+window.navigator.language+"; "+window.navigator.appVersion+"\r\n------------------------------------------------------------------------\r\n------------------------------------------------------------------------\r\n");

    }
    debugObj.isInit = true;
},

First i try and see if we are already init, then nothing needs to be done. Then i try to read from the preferences service what the current mode is.
I used the preferences service because i wanted to allow the end user to be able to turn on the debug mechanism using the about:config in firefox. Next i get the interface for the console service and store it in the object properties. The i check if the mode is file, i print a welcome debug msg about the browser environment, so i will know when reading the file the OS and version of the browser (usually its very helpful).
Now lets see the main funtion – log:


log: function(msg){
    if( debugObj.isInit == false ) debugObj. init();
    if( debugObj.mode == "console" ) debugObj.logConsole(msg);
    if( debugObj.mode == "file" ) {
        debugObj.logConsole(msg);
        debugObj.logFile(msg);
    }
},
logConsole: function(msg){
    debugObj.consoleService.logStringMessage(msg);
},
logFile: function(msg){
    var d = new Date();
    debugObj.converter.writeString("["+d.toLocaleTimeString()+":"+d.getMilliseconds()+"]"+msg+"\r\n--------------------------------------------------------------------------\r\n");
}

as you can see, the log method make sure we are initialized, and according to the mode call the proper method.

I also added a release method to be call when the application is going down, for proper cleanups:


release: function(){
    if( debugObj.converter )
        debugObj.converter.close();
},

Thats it!

Using Debug Object

Now just call the log file wherever you think is needed like so:


debugObj.log("something interesting that shows a variable="+my_variable);

If you get a bug from a client, just tell him to change the preferences in the about:config to mode=file and to send you the log file with all your debug messages planted in the code.  Since i created an nsIFile interface with the “Home” parameter, the file will be written to “root” directory. I think this is OS specific, so in my case – WS7 – the file was located at c:\user\amir

Bookmark and Share
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • E-mail this story to a friend!
  • FriendFeed
  • LinkedIn
  • StumbleUpon
  • Twitter

tags: , , , , ,
posted in Blog by Amir Harel

Follow comments via the RSS Feed | Leave a comment | Trackback URL

  • Me
    Hi,

    How to use this debug object? Any details? Should I copy them to a js file or something?
blog comments powered by Disqus
 
Powered by Wordpress and MySQL. Theme by openark.org