Search code examples
javascriptfirefox-addon

How to console.log from ChromeWorker (alternative to dump)


How to use "Browser Console" from Worker?

Edit: I hate the dum pconsole:

I just started using ChromeWorkers and couldn't use the awesome console.log. But the console window is soooo bad, things are cut off, you cant scroll etc etc. Is there any alternative to dump from ChromeWorker? I know of throw but that interrupts the code.

Picture of the firefox.exe -console (the dump console) im talking about:


Solution

  • Alternatives to dump() or throw() in a Worker (Your question is not clear as to you wanting a different method to use for output to the System Console, or if you are wanting alternatives to using the System Console as the display for text output from a Worker):

    1. If you are desiring to use the System Console (i.e. "Native Console", "Dump Console"), there does not appear to be an alternative to dump() and throw() from JavaScript, or at least I did not find one in searching the docs and trying a variety of things. You can, however, control the properties of the system console to alleviate some of the issues you mentioned in your question. How to do so depends on your OS (see below).

    2. The MDN docs indicate that you can redirect the output which is directed to the System Console with the -console option to a file by redirecting stderr. Effectively, this implies that you should be able to redirect that output to whatever you desire. This would include any display/paging/console like application/process you desire. Obviously, how to do this and what alternatives are available depend on your OS. There are many different options and possibilities.

    3. Using the JavaScript Console from a Worker: The MDN docs indicate that the "normal" console (e.g. Web Console, Browser Console, JavaScript Console, etc.) is available to Workers through the WorkerGlobalScope property. Thus, the normal console is accessed through WorkerGlobalScope.console, or perhaps self.console or a GlobalScope specialized by worker type. So, the normal console.log() should be self.console.log(). As of Firefox 32.0.1 self.console.log and multiple other self.console.* methods are defined as functions in the worker's context, but produce no output in any of the consoles. Thus, we have to fake it (see Polyfill).

    Re: Your issues with the System Console: [Pulling info from our comments to my previous answer into this one to consolidate what should actually be relevant into one answer]:

    The console to which you are referring in your question is the "System Console", also referred to as the "Native Console". The characteristics of this console will depend on the operating system on which you are running Firefox. Thus, we need to know what OS you are using.

    On Windows (you previously asked a Windows based FF question elsewhere, so the info may be helpful): The properties of the window are accessed through the menu opened by right-clicking on the window title-bar -->properties. You then want the "Layout" tab. You probably also want to change the font under the "Font" tab. You can change the width and height of the window that is displayed as well as the width and height of the buffer. Changing the size of the buffer allows you to have a larger, scrollable area that is not cropped. You can then assign the changes to the shortcut that started the window and have your changes come up the next time your start Firefox (at least it does if you have created a shortcut to access a particular profile and added the -console to it). This was tested with a shortcut that contained the following in the "Target" field: "C:\Program Files\Mozilla Firefox\firefox.exe" -purgecaches -no-remote -P "<your profile name here>".


    Polyfill:

    There were a significant number of methods described in the MDN Console documentation which do not actually exist in any context in which I tried them. These are commented out from the polyfill. The polyfill does not attempt to make either the source file or the line number displayed in the console be correct. It would be possible to make at least the errors and warnings show the correct file, but that already happens correctly for actual errors and warnings (i.e. not the ones written into the code).

    This polyfill relies on hijacking very specific messages posted back to the calling process. If the passed message is an object with {type : "Console message", subType : <one of the console types>} then it is assumed to be a message that is to be forwarded to the console via the method specified in the subType.

    The following code has been tested in the worker code you provided.

    There are two alternates for the code in bootstrap.js. Alternate 1 should be used if you are really only going to use the uncomplicated printing of incoming messages. Alternate #2: If you were already doing complex processing of messages, it might be conceptually easier to make the console message handling a wrapper function.

    Code added to your myWorker.js:

    var console = {
        log : function (msg) {
            var toSend = { type : "Console message", subType : "log",            message : msg};
            postMessage(toSend);
        },
        error : function (msg) {
            var toSend = { type : "Console message", subType : "error",          message : msg};
            postMessage(toSend);
        },
        /* Not an actual function.
        _exception : function (msg) {
            var toSend = { type : "Console message", subType : "_exception",     message : msg};
            postMessage(toSend);
        },
        */
        info : function (msg) {
            var toSend = { type : "Console message", subType : "info",           message : msg};
            postMessage(toSend);
        },
        warn : function (msg) {
            var toSend = { type : "Console message", subType : "warn",           message : msg};
            postMessage(toSend);
        },
        /* Not a standard function, and did not want to deal with an arbitrary number of parameters. 
        assert : function (msg,stat) {
            var toSend = { type : "Console message", subType : "assert",         message : msg, status : stat};
            postMessage(toSend);
        },
        */
        /* Not an actual function.
        count : function (msg) {
            var toSend = { type : "Console message", subType : "count",          message : msg};
            postMessage(toSend);
        },
        */
        debug : function (msg) {
            var toSend = { type : "Console message", subType : "debug",          message : msg};
            postMessage(toSend);
        },
        dir : function (msg) {
            var toSend = { type : "Console message", subType : "dir",            message : msg};
            postMessage(toSend);
        },
        group : function (msg) {
            var toSend = { type : "Console message", subType : "group",          message : msg};
            postMessage(toSend);
        },
        /* Not an actual function.
        groupCollapsed : function (msg) {
            var toSend = { type : "Console message", subType : "groupCollapsed", message : msg};
            postMessage(toSend);
        },
        */
        groupEnd : function (msg) {
            var toSend = { type : "Console message", subType : "groupEnd",       message : msg};
            postMessage(toSend);
        },
        /* Not actual functions. Probably would not have worked correctly anyway.
        profile : function (msg) {
            var toSend = { type : "Console message", subType : "profile",        message : msg};
            postMessage(toSend);
        },
        profileEnd : function (msg) {
            var toSend = { type : "Console message", subType : "profileEnd",     message : msg};
            postMessage(toSend);
        },
        */
        /* Not an actual function.
        table : function (msg) {
            var toSend = { type : "Console message", subType : "table",          message : msg};
            postMessage(toSend);
        },
        */
        time : function (msg) {
            var toSend = { type : "Console message", subType : "time",           message : msg};
            postMessage(toSend);
        },
        timeEnd : function (msg) {
            var toSend = { type : "Console message", subType : "timeEnd",        message : msg};
            postMessage(toSend);
        },
        trace : function (msg) {
            var toSend = { type : "Console message", subType : "trace",          message : msg};
            postMessage(toSend);
        },
    }
    

    Alternate #1: Code added (with a small amount of context) in your bootstrap.js (just insert handling console messages inline):

    function handleMessageFromWorker(msg) {
        if (   typeof msg.data == "object"
            && typeof msg.data.type == "string"
            && typeof msg.data.subType == "string"
            && msg.data.type == "Console message"
            && typeof console[msg.data.subType] == "function"
        ){
            if(msg.data.subType == "trace") {
                //This will not work as desired.
                //Obviously doing a trace here will output a trace of this function, not the worker.
                console.error("WORKER STACK TRACE REQUESTED: This is NON-FUNCTIONAL.");
                return;
            } //else
            console[msg.data.subType](msg.data.message);
            return;
        }
        console.log('incoming message from worker, msg:', msg);
    }
    
    myWorker.addEventListener('message', handleMessageFromWorker);
    

    Alternate #2: Code added/changed (with a small amount of context) in your bootstrap.js (wrap the message handler with a function that handles the console messages, keeps this conceptually a bit more separate):

    function wrapHandleMessageFromWorker(msg) {
        if (   typeof msg.data == "object"
            && typeof msg.data.type == "string"
            && typeof msg.data.subType == "string"
            && msg.data.type == "Console message"
            && typeof console[msg.data.subType] == "function"
        ){
            if(msg.data.subType == "trace") {
                //This will not work as desired.
                //Obviously doing a trace here will output a trace of this function, not the worker.
                console.error("WORKER STACK TRACE REQUESTED: This is NON-FUNCTIONAL.");
                return;
            } //else
            console[msg.data.subType](msg.data.message);
            return;
        }
        handleMessageFromWorker(msg);
    }
    
    function handleMessageFromWorker(msg) {
        console.log('incoming message from worker, msg:', msg);
    }
    
    myWorker.addEventListener('message', wrapHandleMessageFromWorker);
    

    Test code added to myWorker.js:

    function sendOutput(){ 
        dump("\n\n\nSending console output....");
        //self.console.profile();
        self.console.time("timeTest");
        self.console.log("This is a worker test message.");
        self.console.error("This is a worker error message.");
        self.console.info("This is a worker info message.");
        self.console.warn("This is a worker warning message.");
        self.console.timeEnd("timeTest");
        self.console.trace();
        //self.console.profileEnd();
        self.console.group();
        self.console.log("This is a worker grouped message 1.");
        self.console.log("This is a worker grouped message 2.");
        self.console.groupEnd();
        //self.console.groupCollapsed();
        //self.console.log("This is a worker collapsed group message 1.");
        //self.console.log("This is a worker collapsed group message 2.");
        //self.console.groupEnd();
        //self.console.table({first:"first name",last:"last name"});
        self.console.dir({first:"first name",last:"last name"});
        //self.console.count("first name");
        //self.console.count("first name");
        //self.console.count("first name");
        //self.console.count("first name");
        //self.console.assert(false, "test assert false");
        //self.console.assert(true, "test assert true");
        self.console.debug("This is a worker debug message.");
        //self.console._exception("This is a worker _exception message.");
        dump("Sent console output.\n\n\n");
    }
    
    var timeoutID = setTimeout(sendOutput, 10000); //I wanted this well after all Firefox initialization so it was easier to see the output.