Is it possible using some javascript trickery to tell console.log which line number it should output?
Suppose, we the following simple example:
function Logger () {
this.debug = function(msg) {
// do some stuff with msg
console.log(msg);
};
}
var log = new Logger();
log.debug("Hello");
If you open the page, Chrome shows up this:
This indicates that the message was logged at main.js:4 but what I really want is that it shows main.js:9. Because line 9 is where the logger was called.
For this simple case it doesn't make much difference, but when the Logger is in a separate file, it always shows logger.js instead of the class which called the logger.
The Logger class should do some additional stuff with the logged message (e.g. sending it to the server), thus I can't just use this.debug = console.log.
EDIT:
There are already some similar questions, but all of them just add an additional line to the output, which isn't clickable to jump to the corresponding line:
Extending console.log without affecting log line
console.log wrapper that keeps line numbers and supports most methods?
Get console wrapper to log message on correct line
Getting Chrome's console.log to display the line that called a function
Custom console.log that keeps line number
Intercept console.log but keep stack
https://stackoverflow.com/questions/28457477/javascript-console-log-change-line-number-to-somewhere-higher-in-the-stack?rq=1
The first thing that comes to my mind is creating a new Error object to get the stack trace and then find the line that called your method, like this:
function Logger () {
this.debug = function(msg) {
// do some stuff with msg
//TODO: document this line
var callerLine = new Error().stack.split('\n')[2];
console.log(msg, callerLine);
};
}
var log = new Logger();
log.debug("Hello");
Basically I'm splitting the error's stack in each newline and ignoring the first and second lines (The error message and your own method in the stack, respectively).
Related
is there a way to send the line and file a console function has been called?
i got an app with many console log and console error in it. thats a good thing for me, the problem is i need to know from where they came from.
i tried override console.error and add console.trace inside but that just created a recursive call to console error(i guess they both triggered by process strerr)
here is what i have tried
const tempConsoleError = console.error.bind(console);
console.error = function (err) {
console.log('i am in error')
tempConsoleError(err);
console.trace()
};
console.error('error string ')
ended up writing a packages to do it. is uses the "stack-trace" and node global
console-from
The usual suggestion (Capturing javascript console.log?) for a js console.log interceptor falls short on two counts, both serious:
(i) all calls in the actual browser console now appear to come from the same line - where the new function is defined - rather then where the call is done
(ii) the intercepted arguments are not yet formatted the way console.log does (i.e. the % replacement goodies, %o, in particular):
(function() {
function send_stuff(){ /* Deal with console arguments. */ }
var oldLog=console.log;
console.log=function(msg) {
oldLog.apply(this,arguments);
send_stuff(Array.prototype.slice.call(arguments).join());
}
}())
console.log("test");
console.log("Hello %s", "Bob");
console.log("Here is an object %o", { stuff: "thing" });
Maybe something better (e.g. something able to capture the actual buffer content of console.log) has been devised.
Edit:
To clarify the multiple arguments failure: besides expanding the file/line info, console.log also does some clever replacements - a-la-printf - of % sequences. Of particular interest is the %o sequence, where a deep-ish dump of the subsequent object(s) is expanded, and put to good use by the browser's console window. This cannot be replicated, unless one is willing to basically reimplement its logic (unnecessarily and , in all likelyhood, unefficiently)
Cheers,
It looks like the behavior of console.log changed a little bit and now the masks are working but you are still getting the line of oldLog.apply(this,arguments); in the console. One thing you could do is to use console.error instead, this is going to show the call stack that you can use to see what line originally called your modified log method.
Another solution is to throw an error to get the line number from the call stack, like this
(function () {
function origin() {
try {
throw Error('');
} catch (err) {
return err.stack.split('\n').slice(-1)[0];
}
}
function send_stuff() { /* Deal with console arguments. */ }
var oldLog = console.log;
console.log = function (msg) {
oldLog.call(this, ...arguments, origin());
send_stuff(Array.prototype.slice.call(arguments).join());
};
})();
console.log("test");
console.log("Hello %s", "Bob");
console.log("Here is an object %o", {
stuff: "thing"
});
how would I use javascript to loop through the messages in the javascript console and display them to a user, for example adding them line by line to a container element like a div.
I understand how to add messages to the console with console.log but my question is, is there any way to then retrieve messages that have been added.
To do this the idea is to intercept anything being sent to the console. Here is a cross browser solution.
function takeOverConsole(){
var console = window.console
if (!console) return
function intercept(method){
var original = console[method]
console[method] = function(){
var message = Array.prototype.slice.apply(arguments).join(' ')
// do sneaky stuff
if (original.call){
// Do this for normal browsers
original.call(console, message)
}else{
// Do this for IE
original(message)
}
}
}
var methods = ['log', 'warn', 'error']
for (var i = 0; i < methods.length; i++)
intercept(methods[i])
}
Found this from taking-over-console-log/
Here I call the the function, and do a simple log "hey". It will interecept it and I will alert the intercepted message. http://jsfiddle.net/Grimbode/zetcpm1a/
To explain how this function works:
We declare a variable console with the actual console. If the console is undefined we just stop right away and leave the function.
We declare var methods = ['log', 'warn', 'error'] for the different console message types
We loop through the methods and call the intercept function and send the console type (string: 'log', 'warn', etc).
Intercept function simply applies a listener~ to that type of console message. In our case we are applying a listener to log, warn and error.
We retrieve the message and put it in the message variable.
Since we intercepted the message, it won't be shown in the console, so we go ahead and original.call() or original() to add it to the console.
If I receive an error from a framework or an error from the browser. Basically a runtime error of any kind. Without modifying the framework, is it possible for me to override the console logs that these frameworks make and the browser's errors. I want to use my own framework with own error handling system when informing the user of errors of practically anything runtime (not syntax errors). I don't know if you would class it all as runtime errors because of the way javascript is executed in the browser but hopefully you will get me?
Is this possible if all the frameworks are written in Javascript?
How is this achieved?
What considerations do I have to make between different browsers?
Thanks
You are probably looking for a try-catch block:
try {
alert(foo);
} catch(e) {
alert('The code got the following error: '+e.message);
}
Whenever the code between the try {} receives an error, the catch(e) {} block will execute, with the argument e being the error object for the error that occurred. In this case, the variable foo is not defined, so executing this code will result in an alert message saying "The code got the following error: foo is not defined"
While not over-riding console.log, you may be achieve the same effect by overriding window.onerror.
From the MDN documentation
window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
// Log the error here -- perhaps using an AJAX call
}
You could try overriding the console.log() function.
//Save original reference
var originalConsole = console;
//Override
console = {};
console.log = function()
{
//According to MDN the console.log function can receive a variable number of params
for(var i=0; i<arguments.length; i++)
{
//Make your changes here, then call the original console.log function
originalConsole.log("Change something: "+arguments[i]);
}
//Or maybe do something here after parsing all the arguments
//...
}
console.log("one", "two");
JSFiddle here.
You can override the console logs by creating a "console" object and overriding it's .log() function:
var console = {};
console.log = function(){};
Some browsers require this to be added to the window object directly; so, for browser compatibility, also add:
window.console = console;
Additionally, you can override other console functions (e.g. console.info, console.warn and console.error) if you're using those too.
Also, consider reading this blog post from Udi Talias on overriding the console functions. Good and quick read!
You can custom your console.log here
// copy the original
let originalConsole = Object.assign({}, console);
// do something with your log.
console.log = (value) => {
//some cool condition
if (true) {
value = "new_log : " + value
}
originalConsole.log(value);
};
To make debugging easier, I'm capturing all of the console logs in Chrome so that users who submit a feedback entry will also submit all of the logs to our server. When someone encounters a problem in production, I can first and foremost get them back to work so that I can then sit down and more thoroughly go through all of the logs to determine the root cause of whatever issue the user encountered in production.
The technique I use to capture the logs involves overriding console.log so that all text entered in the first argument gets stored in an array while simultaneously invoking the legacy function so that I can still see the logs in the console too.
The problem is when there's the occasional uncaught exception. These aren't included in the uploaded logs, so it's not always clear what caused the problem. So I tried overriding ReferenceError by writing a JavaScript function that takes a function as an argument, then returns a new function that does stuff with it, like storing data in a variable, and then invoking the legacy function as the last step:
function overrideException(legacyFn) {
/** arguments for original fn **/
return function() {
var args = [];
args[0] = arguments[0];
// pass in as arguments to original function and store result to
// prove we overrode the ReferenceError
output = ">> " + legacyFn.apply(this, args).stack;
return legacyFn.apply(this, arguments);
}
}
To test the overrideException function, I ran the following code on the console:
ReferenceError = overrideException(ReferenceError);
Afterwards, I tested the returned function, the new ReferenceError, by manually throwing a ReferenceError:
throw new ReferenceError("YES!! IT WORKS! HAHAHA!");
The resulting output on the console is:
ReferenceError: YES!! IT WORKS! HAHAHA!
And checking the global variable output from the overrideException function shows that it did indeed run:
output
">> ReferenceError: YES!! IT WORKS! HAHAHA!
at ReferenceError (<anonymous>)
at new <anonymous> (<anonymous>:18:35)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:562:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52)
at Object.InjectedScript.evaluate (<anonymous>:440:21)"
Now, here's where things start to fall apart. In our code, we're not going to know when an uncaught exception occurs, so I tested it by attempting to run a function that doesn't exist:
ttt();
Which results in:
ReferenceError: ttt is not defined
However, unlike the case where we explicitly throw an error, in this case, the function doesn't fire, and we're left with only the legacy functionality. The contents of the variable output is the same as in the first test.
So the question seems to be this: How do we override the ReferenceError functionality that the JavaScript engine uses to throw errors so that it's the same one we use when we throw a ReferenceError?
Keep in mind that my problem is limited only to Chrome at this time; I'm building a Chrome Packaged app.
I have done quite a bit of research for the same reason: I wanted to log errors and report them.
"Overriding" a native type (whether ReferenceError, String, or Array) is not possible.
Chrome binds these before any Javascript is run, so redefining window.ReferenceError has no effect.
You can extend ReferenceError with something like ReferenceError.prototype.extension = function() { return 0; }, or even override toString (for consistency, try it on the page, not the Dev Tools).
That doesn't help you much.
But not to worry....
(1) Use window.onerror to get file name, 1-indexed line number, and 0-indexed position of uncaught errors, as well as the error itself.
var errorData = [];
onerror = function(message, file, line, position, error) {
errorData.push({message:message, file:file, line:line, position:position, error:error});
};
See the fiddle for an example. Since the OP was Chrome-specific, this has only been tested to work in Chrome.
(2) Because of improvements to (1), this is no longer necessary, but I leave this second technique here for completeness, and since onerror is not guaranteed to work for all errors on all browsers. You will also sometimes see the following:
var errors = [];
function protectedFunction(f) {
return function() {
try {
f.apply(this, arguments);
} catch(e) {
errors.push(e);
throw e;
}
};
}
setTimeout = protectedFunction(setTimeout);
setInterval = protectedFunction(setInterval);
etc...
FYI, all this is very similar to what has been done in the Google Closure Compiler library, in goog.debug, created during Gmail development with the intent of doing exactly this. Of particular interest is goog.debug.ErrorHandler and goog.debug.ErrorReporter.