I use angularjs $log in chrome, but it shows the line like: angular.js:9037
. I want to show the line number where I call this method. (Show my js name and the correct line). Does anyone know how to do it? Angular doesn't have this feature.
In Chrome there is a feature called Blackboxing. You can use it to exclude / bypass (library) sources from your debug sessions or development workflow.
So if you blackbox angular the internals of the $log service get bypassed and the console prints the correct line number!
https://developer.chrome.com/devtools/docs/blackboxing
You can access it by applying a decorator to the $log
service:
module.config(function logConfig($provide, $logProvider) {
$provide.decorator('$log', function ($delegate) {
var originalFns = {};
// Store the original log functions
angular.forEach($delegate, function (originalFunction, functionName) {
originalFns[functionName] = originalFunction;
});
var functionsToDecorate = ['debug', 'warn'];
// Apply the decorations
angular.forEach(functionsToDecorate, function (functionName) {
$delegate[functionName] = logDecorator(originalFns[functionName]);
});
return $delegate;
});
function logDecorator(fn) {
return function () {
var args = [].slice.call(arguments);
// Insert a separator between the existing log message(s) and what we're adding.
args.push(' - ');
// Use (instance of Error)'s stack to get the current line.
var stack = (new Error()).stack.split('\n').slice(1);
// Throw away the first item because it is the `$log.fn()` function,
// but we want the code that called `$log.fn()`.
stack.shift();
// We only want the top line, thanks.
stack = stack.slice(1, 2);
// Put it on the args stack.
args.push(stack);
// Call the original function with the new args.
fn.apply(fn, args);
};
}
});
I do this as an includable module, but I believe it could be done within the app's .config()
as well.
I built this (along with some additional logic) by gluing together a number of different sources online; I'm usually really good at keeping references to them, but I guess I didn't when I built this, so unfortunately I can't reference my inspiration. If someone replies with it, I'll put it in here.
NOTE 1: this is a slightly stripped-down version of what I actually use, so you'll have to double-check the
logDecorator()
s stack pushy-shifty magic, though it should work as presented.NOTE B: MDN says that
Error.prototype.stack
is non-standard (requires IE10 and may not be supported on many mobile browsers) so you might want to look at augmenting this with something like stacktracejs to get the stack itself.
I have combined a couple of solutions from this page, as well others to build a simple demo in JSFiddle - to demonstrate use of $log service, enhancing it with decorators to add line number (line number from where $log call was made). I have also made a slightly more comprehensive solution in Plunker, demonstrating the use of $log service, enhancing it with decorators to add line number, caller file name and instance name. Hopefully, this will be useful to others.
JSFiddle URL - https://jsfiddle.net/abhatia/6qnz0frh/
This fiddle has been tested with following browsers:
The results are good. But, please note that line number in Chrome will be off by 1, when compared to FF or IE, i.e. because JSFiddle's javascript's code first line number differs between FF/IE and Chrome, as listed above.
Plunker URL - https://embed.plnkr.co/YcfJ7V/
This plunk demonstrates the concept really well, with detailed explanation and also provides the console output with Angular's official example of default $log service, so the two could be contrasted. Plunk has also been tested with browsers listed above.
Below screenshot is the console output from the Plunk example above. There are 3 highlighted areas:
This will become very clear when you review the Plunk code.
Here is the getLineNumber function used in JSFiddle (slightly enhanced version is used Plunker example to return caller file name):
function getLineNumber(newErr, sliceIndex1, sliceIndex2)
{
var lineNumber = -1;
var lineLocation;
var stack = newErr.stack.split('\n').slice(2);
if (navigator.userAgent.indexOf("Chrome") > -1) {
stack.shift();
}
stack = stack.slice(sliceIndex1, sliceIndex2);
var stackInString = stack + '';
var splitStack;
if (navigator.userAgent.indexOf("Chrome") > -1) {
splitStack = stackInString.split(" ");
}
else {
splitStack = stackInString.split("@");
}
lineLocation = splitStack[splitStack.length - 1];
//console.log(lineLocation);
lineNumber = lineLocation.split(":")[2];
return lineNumber;
}
The line number comes from the runtime. You can not set it in general case.
But not all is lost. In places where the line number is really important you can use a different call.
Remember to inject the $window
and then:
$window.console.log("test1");
You loose some things this way like formatting, cross browsers filler code etc, but you do get line numbers correct for free without any per runtime specific code to do so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With