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"
});
Related
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).
Is there a way to set breakpoints when specific functions are about to execute?
It needn't be an explicit breakpoint - I just want execution to pause when a console.log() is about to be called.
Or should I resort to this method.
I prefer to accomplish this without modifying my code, or manually setting breakpoints at every console.log.
Yes that's the trick. Create a custom logging function, put debugger in it and call console.log and you have what you wanted:
function log(message) {
debugger;
console.log(message) ;
}
Edit:
You can also replace console.log by a similar fonction that calls the original:
var clog = console.log;
console.log = function(message) {
if(message == '...') {
debugger;
}
clog.apply(console, arguments);
}
This code will affect all log calls within your page. The check is to catch a certain message. Remove it to stop always.
How about call this at the very start that add a pause break for your every console.log?
This replace the original console.log, pause break first, then call the orignal console.log for you. And this will be apply on all console.log calls.
(function () {
var oldLog = console.log;
console.log = function() {
debugger;
oldLog.apply(console, arguments);
}
})();
console.log('hello');
I'm used to throwing an instance of some error class and having them be caught somewhere down the line in the app, to account for user error.
An example might be validating the username:
function validateUsername (username) {
if (!/^[a-z0-9_-]{3,15}$/.test(username)) {
throw new ValidationError('Please enter 3-15 letters, digits, -, and/or _.');
}
}
$('#username').blur(function () {
try {
validateUsername($(this).val());
} catch (x) {
$('<p></p>').addClass('error').text(x).insertAfter(this);
}
});
But now I'm realizing that I can't use these same practices for asynchronous calls. For example:
function checkUsernameAvailability (username) {
$.get('/users/' + username).done(function () {
// Done = user returned; therefore, username is unavailable
// But I can't catch this error without resorting to something gross
// like window.onerror
throw new ValidationError('The username you entered is already in use.');
});
}
I could make checkUsernameAvailability accept a callback and/or return a promise and have it execute said callback with the availability of the username.
$('#username').blur(function () {
checkUsernameAvailability(username, function (available) {
!available && alert('The username you entered is already in use.');
});
});
But part of what makes exceptions so powerful is that they can bubble up the stack until they get caught, whereas if I had another function that called another function that called checkUsernameAvailability, I'd need to pass the result of this callback manually all the way until I get to the place where I want to handle it.
What are some of the alternative methods for passing errors up the stack? I can think of some of these, but none of them are as clean as native exceptions:
Passing a flag, or the ValidationError instance, to a callback (Node.js approach could work too, passing an error or null as the first argument, and the data as the second); but then, if I don't want to handle it at that point in the stack, I need to pass the error up manually
Or passing 2 callbacks to the checkUsernameAvailability function, a success callback and an error callback; this seems to have the same drawbacks as the previous point
Triggering a "ValidationError" event so I can listen anywhere, but make sure to return false; in the handler so it doesn't execute higher in the stack; however, this pollutes the event namespace and could make it unclear as to which event listener will be executed first; plus, it's difficult to trace an event to its origin using the console
in principal it is like this
function Exception (errcode) {
this.code = errcode;
}
...
try {
...
throw new Exception('alpha');
...
} catch (e) {
if (e.code === {something}) {
}
}
If it helps, I recently took the first release of the Rogue game written for UNIX in C and rewrote it for javascript to work in a browser. I used a technic called continuation to be able to wait for key entry by the user because in javascript the are no interrupts.
So I would have a piece of code like this:
void function f() {
// ... first part
ch = getchar();
// ... second part
}
that would be transformed in
function f() {
// ... first part
var ch = getchar(f_cont1);
return;
// the execution stops here
function f_cont1 () {
// ... second part
}
}
the continuation is then stored to be reuse on a keypressed event. With closures everything would be restarted where it stoped.
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);
};
I've previously used the following based on other SO answers (without really understanding the need for (nor the workings of) the prototype.apply.apply
var mylogger = {
log: function () {
if (window.console) {
if (window.console.log) {
Function.prototype.apply.apply(console.log, [console, arguments]);
}
}
},
...
};
while this prevents IE from crapping on itself, it also make the line number reporting unusable (it always reports the apply.apply.. line.
I was playing around a little and discovered that the following seems to do exactly what I need, i.e. prevent IE from crapping on itself and report the line number where mylogger.log(..) was called from..
var mylogger = {
// function invocation returning a safe logging function..
log: (function () {
if (window.console && window.console.log && Function.prototype.bind) {
return window.console.log.bind(window.console);
} else {
return function () {};
}
}()),
...
};
I've done some basic testing on IE/FF/Chrome without seeing any issues.. is this reasonable or is there a better way?
What you're doing is fine I guess, but if you aren't adding any additional functionality, you could do something simple and in one line:
window.console = (window.console || {debug:function(){},log:function(){},error:function(){}});
You could, of course, add other console functions if you use them.