adamschwartz / log

Console.log with style.
https://adamschwartz.co/log
MIT License
3.01k stars 140 forks source link

Make the stack trace and/or line number available #1

Open adamschwartz opened 11 years ago

adamschwartz commented 11 years ago

Possibly do something like this: https://github.com/eriwen/javascript-stacktrace

Or like this: __LINE__ = (new Error).stack.split("\n")[1].match(/:([0-9]+):/)[1] https://news.ycombinator.com/item?id=5540763

See https://news.ycombinator.com/item?id=5540716 for more information.

adamschwartz commented 11 years ago

We definitely want to add line numbers. However, unfortunately, the nature of wrapping console.log means we'll lose the ability to display the line number on the right.

There are a number of possible solutions we are considering to address this. For example, if we change the exports at the bottom of the script to something like the following:

window.log = console.log.bind(console)
window.log.format = stringToArgs

Then you could call the following and still get line numbers:

log(log.format('this is _bold_'))

If you didn't want the text to be formatted, you could still call:

log('this is _just some text with two underscores around it_')

Of course, having to wrap your string in a log.format is certainly less sexy than we'd like. So we're considering other possible solutions as well.

On a related note, there may be some potential changes coming to console.log. We're actively following and participating in the discussions here:

https://code.google.com/p/chromium/issues/detail?id=167911 https://bugs.webkit.org/show_bug.cgi?id=20141

fleon commented 11 years ago

Hi Adam,

I came up with a simple hack to enable logging line numbers. I have only tested it in Chrome though. Here's how it works:

I could've also added two console.log statements: one for logging, the other for the file name and line number, but I thought that would be obtrusive and figured using console.group would be a much elegant solution.

Limitations:

See source here: https://github.com/fleon/log/blob/master/log.coffee jsFiddle: http://jsfiddle.net/fleon/b6etu/1/

adamschwartz commented 11 years ago

This is fantastic work. Very clever.

However, before merging I'd like to take the following steps.

  1. First, let's make sure we test in every one of the currently supported browsers. We may be able to feature test for console.groupCollapsed. If all else fails we can use another userAgent test.
  2. Next, let's run some benchmarks on jsperf comparing this to the current version of log. If the performance is dramatically slower, we may release this as an alternative in the repo without wholesale replacing the original. Or we may want to refactor such that there's a way to call either version (log, log.l, and log.e perhaps).
  3. Finally, I'd like to spend a few more days brainstorming on possible ways to avoid the groups altogether. Your use of eval to remove the line number was very clever. I'm not so sure there aren't other clever tricks like that out there waiting to be found.

Thanks for your contribution!

fleon commented 11 years ago

I managed to get it working in Firefox+Firebug as well, but the hack is very ugly. It works without console.group though.

Each log statement now shows up the filename and actual line number overlayed on top of the log.js line number (thanks to Firebug allowing position:absolute in log styles). It's not clickable though, so I logged arguments.callee.caller to its left, clicking which will take you to the function definition (close enough). To make sure it is positioned correctly with correct padding, I did some DOM insertion hacks (which would sorta make it slower, but we can get around that). Two console.logs added an additional hr, which I managed to hide by overlaying with a white 1px high, 100% wide absolute box.

Nevertheless, this was a lot of fun!

Source: https://github.com/fleon/log/commit/0713275eae0f2ccfcd7f963016c4febd0e659cb7 (warning: ugly code) jsFiddle: http://jsfiddle.net/fleon/b6etu/1/

6twenty commented 11 years ago

Another option, although not as independent, is to combine log.js with stacktrace.js. Here's the modified _log function from log.js source (this is also only tested in Chrome and only with one simple test case, but we could test further if you want to go down this road):

  _log = function() {
    if (typeof window.printStackTrace === 'function') {
      var trace = printStackTrace();
      var callerLine = trace[trace.length-1];
      var index = callerLine.indexOf("at ");
      var clean = callerLine.slice(index+2, callerLine.length);
    }
    var array = makeArray(arguments);
    if (clean) { array.push(clean); }
    return console.log.apply(console, array);
  };

Assuming you have included stacktrace.js in your page and you call log('hello') from line 4 of script.js, the output will be: hello path/to/script.js:4:1. If you don't have stacktrace.js, everything still works fine -- just without the correct line number displayed.

6twenty commented 11 years ago

See my fork at https://github.com/6twenty/log/commit/249864da2a39d80f7abd4774a11d8858a40af37c for a working example (coffeescript version updated too, but with a typo which is fixed in https://github.com/6twenty/log/commit/5f0f16820eb35c060e42dbf41a6ef8db4a5266d6).

SamHasler commented 10 years ago

you could do something like

if (Function.prototype.bind) {
   //Preserves line numbers
   window.log = Function.prototype.bind.call(console.log, console);
} else {
   //Fallback
}

window.md = function(){
  var args = Array.prototype.slice.call(arguments);
  //style markdown in args
  return args;
}

log(md("This is *italic*"));

or this:

function log(){
  var args = Array.prototype.slice.call(arguments);
  //style markdown in args
  return Function.prototype.apply.bind(window.console.log,window.console, args)
}

log("This is *italic*")();

which would output nothing if you missed of the trailing () but is very concise otherwise.

adamschwartz commented 10 years ago

@SamHasler Thanks! Those are both very interesting options.

The log(md("This is *italic*")); is great as it's very clear what it's doing (assuming md is understood). However, I'm not excited about adding another global to the scope. There's already a log.l which attempts to serve this purpose, so perhaps the syntax would be log.l(log.md("This is *italic*));. Not as good as yours but may be a good compromise. Then the library would say "If you care about line numbers, use log.l log.md, and if you don't, just use log".

The other approach is very tempting, but I think it's a little too tricky for our average user. I would worry too many people would miss the subtlety of the extra () and be frustrated with the library. Ironically, this could be mitigated with a console.log message if the user tried log("This is *italic*") but unfortunately we A) can't know what happens later in the call chain and B) wouldn't want to log anything additional in a log library ;). Nevertheless a similar option as above could be taken of making a log.something which does this.

One of these will likely get us there though, so thanks again!

SamHasler commented 10 years ago

The user can turn on Chrome Dev Tools Settings > General > Sources > Skip stepping through sources with particular names, and set pattern to match log.min.js. It's worth documenting in the Readme.

However it doesn't help if they concatenate log.min.js into thier own code. See also: jsbin/jsbin#1833

adamschwartz commented 10 years ago

Thanks @SamHasler. Didn’t know about that setting. :+1:

W-prog commented 7 years ago

@SamHasler thanks for the tips with md() I have question When i code log(md('[c="color: #32c5d2"] Mytexte in color : [c]')); The console write a line in a array like this capture d ecran 2017-04-05 a 11 07 04

how we can remove the bracket and first " ?