24

How do I change the string representation of an object instance in nodejs debug console. Is there a method (like toString() in .NET) I can override?

enter image description here

Consider the following code:

class SomeObject{
    constructor(){
        this._varA = "some text";
        this._varB = 12345;
        this._varC = "some more text";
        this._varD = true;
        this._varE = 0.45;
    }

    toString(){
        return "custom textual rapresentation of my object";
    }
}

var array = [];

array.push(new SomeObject());
array.push(new SomeObject());
array.push(new SomeObject());

console.log(array);

This produces the following: enter image description here

However in other environments and programming languages I have worked on, overriding the toString() method would show the result of toString() (in the example above "custom textual representation of my object") instead of the dynamic textual representation created by the debugger (in the example code above this is: SomeObject {_varA: "some text", _varB: 12345, _varC: "some more text", …}) - which I don't doubt for one minute it is very useful when a custom alternative is not defined.

I also realise that console.log(array.toString()); or even console.log(array.map(t=>t.toString())); will produce something similar to what I am after, however this then prevents me to navigate through the objects using the debug navigation ie. drill into the object graph.

If this is not possible, would others benefit from this? If there is enough interest, I can look into defining and implementing it as a feature.

Leo
  • 5,013
  • 1
  • 28
  • 65
  • Added this as an issue on GitHub - https://github.com/Microsoft/vscode/issues/46829 – Leo Mar 28 '18 at 09:58
  • And something like this? `array.forEach(t=>{console.log(t.toString() + ":",t)});`. It is not ideal, but we could create a node module to handle this kind of situations. For instance, create a module that checks the argument and acts upon the type :) – GuyT Mar 28 '18 at 10:11
  • 1
    @GuyT - thanks for the suggestion but it is not ideal. In my opinion the debug console (as shown in the second picture in my post) should display: `SomeObject { "custom textual rapresentation of my object" }` instead of `SomeObject {_varA: "some text", _varB: 12345, _varC: "some more text", …}` – Leo Mar 28 '18 at 10:16
  • I get your point, another not ideal solution, would be `array.forEach( el => {console.groupCollapsed(el.toString()); console.log(el); console.groupEnd();});`. Ps. I do understand what your envisioned solution is.. ;) – GuyT Mar 28 '18 at 11:07

5 Answers5

12

When you do console.log it internally calls formatValue in util.js, which has a below check

const maybeCustomInspect = value[customInspectSymbol] || value.inspect;

Which means if your value has a inspect method it gets called and then you can return the toString in the same. So change your code to

class SomeObject{
    constructor(){
        this._varA = "some text";
        this._varB = 12345;
        this._varC = "some more text";
        this._varD = true;
        this._varE = 0.45;
    }

    inspect(depth, opts) {
        return this.toString();
    }

    toString(){
        return "custom textual rapresentation of my object";
    }
}

var array = [];

array.push(new SomeObject());
array.push(new SomeObject());
array.push(new SomeObject());

console.log(array);

Makes it print

[ custom textual rapresentation of my object,
  custom textual rapresentation of my object,
  custom textual rapresentation of my object ]

Nodejs has documentation on the same too

https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_custom_inspection_functions_on_objects

The inspect method I used is deprecated as per documentation and the correct way to do it is below

const util = require('util');

class SomeObject{
    constructor(){
        this._varA = "some text";
        this._varB = 12345;
        this._varC = "some more text";
        this._varD = true;
        this._varE = 0.45;
    }

    [util.inspect.custom](depth, options) {
        return this.toString();
    }

    toString(){
        return "custom textual rapresentation of my object";
    }
}

var array = [];

array.push(new SomeObject());
array.push(new SomeObject());
array.push(new SomeObject());

console.log(array);

Edit: 28th-Mar-2018

So I launched the script using below

$ node --inspect-brk=54223 test.js 
Debugger listening on ws://127.0.0.1:54223/81094440-716b-42a5-895e-4ea2008e0dff
For help see https://nodejs.org/en/docs/inspector

And then ran a socat forwarder using below

$ socat -v TCP-LISTEN:54222,fork TCP:127.0.0.1:54223

When you debug the variable array in debugger, you get below output on the socat terminal

socat

The information is that reconstructed by debugger to give you a meaningful representation.

Now the v8 debug api has no idea that we want to represent it differently, like we did for console.log. Now there may be something similar in V8 code that does something similar, but looking at the source code, I was not able to figure out there exists such a thing. So you may need to confirm from someone who has V8 debugger api knowledge, if something of this sort exists

If not the you need to construct something at the IDE level, which again is not a a easy thing to do

Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • I tried this and it does not make any difference to the way the debug console display the objects... – Leo Mar 24 '18 at 12:21
  • Your edit kind of confused me that you were looking for `Console.log` only, I am checking the debugger part now – Tarun Lalwani Mar 24 '18 at 12:22
  • thank you for the answer and edit - I am only interested in getting this working in vscode / vscode debugger. Sorry I should have mentioned that in the body of my question instead of only relying on tags... Anyhow, you have a +1 from me :) – Leo Mar 28 '18 at 09:27
4

There has been a new option added to VS Code to enable tweaking the debugger output: simply add the below to your launch configuration

"customDescriptionGenerator": "function (def) { if (this.toString) { const _v = this.toString(); if (_v.indexOf(\"[object Object]\") < 0) return _v; } return def; }",

Viola: your entities viewed with "toString" in watch, retaining the ability to drill down etc.

FrozenKiwi
  • 1,362
  • 13
  • 26
0

My two cents: How about overriding the console.log function to do what you want to. Here is the POC which needs a _toString function in any object to change the way it appears in the log.

create a file logger.js with the following content:

const getUpdatedLogObj = function(x){


    if(x && typeof x == 'object'){
            if(typeof x._toString === 'function'){
                return x._toString()
            } else {
                for(let i in x){
                    x[i] = getUpdatedLogObj(x[i])
                }
            }
        }
        return x;
    }



    console._log = console.log
    console.log = function(x){console._log(getUpdatedLogObj({...x}))}

import it in index.js

require('./logger')

console.log({a: 1, b: 2, c: {d: 5, e: 6, _toString: function(){return 'fromToString'}}})

And you still get the navigaion: enter image description here

thunder
  • 230
  • 1
  • 8
  • > I also realise that console.log(array.toString()); or even console.log(array.map(t=>t.toString())); will produce something similar to what I am after, however this then prevents me to navigate through the objects using the debug navigation ie. drill into the object graph. – Leo Mar 28 '18 at 15:45
  • the name _toString might be misleading, but you still get the navigation with above implementation: Updated the answer with a snapshot – thunder Mar 28 '18 at 15:57
  • thank you for the answer but this does not provide a suitable solution to my question. – Leo Mar 28 '18 at 16:05
-1
const util = require('util');
class SomeObject{
constructor(){
    this._varA = "some text";
    this._varB = 12345;
    this._varC = "some more text";
    this._varD = true;
    this._varE = 0.45;
}

[util.inspect.custom](depth, options) {
    return this.toString();
}

toString(){
    return "custom textual rapresentation of my object";
}
}

var array = [];

array.push(new SomeObject());
array.push(new SomeObject());
array.push(new SomeObject());

console.log(array);
-2

There is a toString() method that you call on another string.

terms[200]._text.toString()

You may also be looking for JSON.stringify() which I find extremely useful in debugging. Since JavaScript objects are literally JSON, this will make printing them out to the console simpler.

console.log(JSON.stringify(terms[200]))

Alec O
  • 1,697
  • 1
  • 18
  • 31
  • 1
    If you are going to down-vote, leave a reason why so I can improve my answer. – Alec O Mar 23 '18 at 10:19
  • I'm not sure but I think it is probably because your answer is not a valid answer to my question... That is probably partly my bad too, I will update the question to be clearer... Sorry. – Leo Mar 23 '18 at 11:00