Showing console errors and alerts in a div inside the page - javascript

I'm building a debugging tool for my web app and I need to show console errors in a div. I know I can use my own made console like object and use it, but for future use I need to send all console errors to window. Actually I want to catch console events.

To keep the console working:
if (typeof console != "undefined")
if (typeof console.log != 'undefined')
console.olog = console.log;
else
console.olog = function() {};
console.log = function(message) {
console.olog(message);
$('#debugDiv').append('<p>' + message + '</p>');
};
console.error = console.debug = console.info = console.log

Here's a way using closure, containing the old console log function in the scope of the new one.
console.log = (function (old_function, div_log) {
return function (text) {
old_function(text);
div_log.value += text;
};
} (console.log.bind(console), document.getElementById("error-log")));

None of the answers here consider console messages that get passed multiple parameters. E.g. console.log("Error:", "error details")).
The function that replaces the default log function better regards all function arguments (e.g. by using the arguments object). Here is an example:
console.log = function() {
log.textContent += Array.prototype.slice.call(arguments).join(' ');
}
(The Array.prototype.slice.call(...) simply converts the arguments object to an array, so it can be concatenated easily with join().)
When the original log should be kept working as well:
console.log = (function (old_log, log) {
return function () {
log.textContent += Array.prototype.slice.call(arguments).join(' ');
old_log.apply(console, arguments);
};
} (console.log.bind(console), document.querySelector('#log')));
A complete solution:
var log = document.querySelector('#log');
['log','debug','info','warn','error'].forEach(function (verb) {
console[verb] = (function (method, verb, log) {
return function () {
method.apply(console, arguments);
var msg = document.createElement('div');
msg.classList.add(verb);
msg.textContent = verb + ': ' + Array.prototype.slice.call(arguments).join(' ');
log.appendChild(msg);
};
})(console[verb], verb, log);
});
(An example of a framework that emits messages with multiple parameters is Video.js. But there is certainly many others.)
Edit: Another use of multiple parameters is the formatting capabilities of the console (e.g. console.log("Status code: %d", code).
About errors that are not shown
(Update Dec. 2021)
If any code crashes with an uncaught error, in might not show up in the div. One solution could be, if possible, to wrap all code in a try block to catch such errors and log them manually to the div.
try {
// Code that might throw errors...
} catch(err) {
// Pass the error to the overridden error log handler
console.error(err);
}

Else, if you were concerned at keeping log, warn and error separate from one another, you could do something like this (adapted from MST's answer):
var log = document.querySelector('#log');
['log','warn','error'].forEach(function (verb) {
console[verb] = (function (method, verb, log) {
return function (text) {
method(text);
// handle distinguishing between methods any way you'd like
var msg = document.createElement('code');
msg.classList.add(verb);
msg.textContent = verb + ': ' + text;
log.appendChild(msg);
};
})(console[verb].bind(console), verb, log);
});
where #log is your HTML element. The variable verb is one of 'log', 'warn', or 'error'. You can then use CSS to style the text in a distinguishable way. Note that a lot of this code isn't compatible with old versions of IE.

How about something as simple as:
console.log = function(message) {$('#debugDiv').append('<p>' + message + '</p>');};
console.error = console.debug = console.info = console.log

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<div id="logger" class="web_console"></div>
<script type="text/javascript">
// Overriding console object
var console = {};
// Getting div to insert logs
var logger = document.getElementById("logger");
// Adding log method from our console object
console.log = function(text)
{
var element = document.createElement("div");
var txt = document.createTextNode(text);
element.appendChild(txt);
logger.appendChild(element);
}
// testing
console.log("Hello World...");
console.log("WOW");
/**
console.log prints the message in the page instead browser console, useful to programming and debugging JS using a Android phone
*/
</script>
</body>
</html>

I created a zero-dependency npm module for this case: console-events (surely if you're okay to use nodejs :P)
You can add event listener like that:
const { console } = require('console-events');
console.addEventListener('log', (e) => {
e.preventDefault(); //if you need to prevent normal behaviour e.g. output to devtools console
$('#debugDiv').append('<p>' + message + '</p>');
})

Related

How custom logs function print line no# & file name where it was called

I have made a following custom logs function to print all console log messages. Using this function I can control with a single flag variable to either print or not logs throughout the app.
var Utilities = {
showLogs: true,
printLog: function (msg) {
if (this.showLogs) {
console.log(msg);
}
}
};
and I call it as:
Utilities.printLog("a message to print on console");
It works fine as expected. But it has one limitation i.e. its not showing the correct line no# and file name where this was called to print the logs.
One solution is to provide extra parameters to print line no# & file name along with the message.
for instance:
Utilities.printLog("a message to print on console", "10","common.js");
Utilities.printLog("a message to print on console", "310","myLib.js");
I dont want these extra parameters and like to know if there is another option available.
Update:
I tried the V8's Stack Trace API http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi but it only helps in cases when an exception is generated inside try catch block.
First override the Error.prepareStackTrace and create a tracing function like this:
Error.prepareStackTrace = function(error, stack) {
return stack;
};
function getTrace(e) {
var stack = e.stack;
var trace = "";
for (var i = 0; i < stack.length; i++) {
trace += "\r" + stack[i];
}
return trace;
}
and created two sample js files.
libObj.js
var libObj = {
getCube: function(x){
return mathLib.cube( x );
}
};
mathLib.js
var mathLib = {
cube: function(x){
return evilObj * x * x; //see the undefined evilObj --- lets catch trace here
}
};
Now from a third js file (or in my case inside the HTML file) I call the function within the try catch block to see the precise trace of the vulnerable code.
<script type="text/javascript">
try {
var results;
results = libObj.getCube(2);
console.log( results );
} catch (e) {
console.log( getTrace(e));
}
</script>
Now I get below trace of the vulnerable code:
Note:- If you do not override the Error.prepareStackTrace then it gives, I think pretty formatted trace...though both have same info.
Without overriding Error.prepareStackTrace:
Now the question remains open, how I can capture similar trace for my custom logs function as defined above.
You could do this:
var Utilities=
{
showLogs:true,
printLog:function(msg){
if(!this.showLogs) return 0;
var k=new Error().stack.split("\n").slice(2);
k.unshift(msg);
console.log(k.join("\n"));
}
}

Message Manager API sendAsyncMessage callback

I can return a value if I send a sync message:
// frame script
var chromeBtnText = sendSyncMessage("getChromeToolbarButtonText");
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
// chrome script
messageManager.addMessageListener("getChromeToolbarButtonText", listener);
function listener(message) {
return document.getElementById('myChromeToolbarButton').label.value;
}
How do I achieve this with a callback with sendAsyncMessage?
I was hoping to do something like:
// frame script
function myCallback(val) {
var chromeBtnText = val;
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
}
var chromeBtnText = sendAsyncMessage("getChromeToolbarButtonText", null, myCallback);
There is no callback for replies. In fact, there is no reply at all. The return value from the chrome message listener is simply ignored for async messages.
To do fully async communication, you'd have to send another message containing the reply.
Frame script
addMessageListener("getChromeToolbarButtonTextReply", function(message) {
alert(message.data.btnText);
});
sendAsyncMessage("getChromeToolbarButtonText");
Chrome
messageManager.addMessageListener("getChromeToolbarButtonText", function(message) {
var btnText = document.getElementById('myChromeToolbarButton').label.value;
// Only send message to the frame script/message manager
// that actually asked for it.
message.target.messageManager.sendAsyncMessage(
"getChromeToolbarButtonTextReply",
{btnText: btnText}
);
});
PS: All messages share a namespace. So to avoid conflicts when another piece of code wants to use the same name getChromeToolbarButtonText, you better choose a more unique name in the first place, like prefixing your messages with your add-on name my-unique-addoon:getChromeToolbarButtonText or something like that. ;)
I was also hoping to do something similar:
messageManager.sendAsyncMessage("my-addon-framescript-message", null, myCallback);
I'm going the other direction so the myCallback would be in chrome but it's exactly the same principle.
I'd used similar approaches to #Noitidart and #nmaier before but in this new case I wanted to bind to some local data so myCallback can behave differently based on the application state at the time the first message was sent rather than at the time the callback is executed, all while allowing for the possibility of multiple message round-trips being in progress concurrently.
Chrome:
let someLocalState = { "hello": "world" };
let callbackName = "my-addon-somethingUnique"; // based on current state or maybe generate a UUID
let myCallback = function(message) {
messageManager.removeMessageListener(callbackName, myCallback);
//message.data.foo == "bar"
//someLocalState.hello == "world"
}.bind(this); // .bind(this) is optional but useful if the local state is attached to the current object
messageManager.addMessageListener(callbackName, myCallback);
messageManager.sendAsyncMessage("my-addon-framescript-message", { callbackName: callbackName } );
Framescript:
let messageHandler = function(message) {
let responseData = { foo: "bar" };
sendAsyncMessage(message.data.callbackName, responseData);
};
addMessageListener("my-addon-framescript-message", messageHandler);
There's a real-world example here: https://github.com/luckyrat/KeeFox/commit/c50f99033d2d07068140438816f8cc5e5e290da9
It should be possible for Firefox to be improved to encapsulate this functionality in the built-in messageManager one day but in the mean-time this approach works well and with a surprisingly small amount of boiler-plate code.
in this snippet below. i add the callback before sendAsyncMessage('my-addon-id#jetpack:getChromeToolbarbuttonText'... as i know it will send back. Then I remove it after callback executes. I know I don't have to but just to kind of make it act like real callback, just to kind of show people, maybe it helps someone understand.
Frame:
/////// frame script
function CALLBACK_getChromeToolbarButtonText(val) {
removeMessageListner('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage', CALLBACK_getChromeToolbarButtonText); //remove the callback
var chromeBtnText = val;
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
}
addMessageListener('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage', CALLBACK_getChromeToolbarButtonText); //add the callback
var chromeBtnText = sendAsyncMessage("my-addon-id#jetpack:getChromeToolbarButtonText", null);
Chrome:
////// chrome script
messageManager.addMessageListener("my-addon-id#jetpack:getChromeToolbarButtonText", listener);
function listener() {
var val = document.getElementById('myChromeToolbarButton').label.value;
sendAsyncMessage('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage',val);
}

How to read from Chrome's console in JavaScript

I would like to put a button in my app, if you press it it will get the contents of everything that was written to the console and email it to me (for reporting bugs). I know I can keep a variable around and every time I do a console.log also append the message to that variable but I am trying to keep the memory consumption of the app low so it would be much more efficient just to grab it from the console.
Is there a way to retrieve the console messages from javascript?
You can't. What's in the console can't be read from JavaScript.
What you can do is hook the console.log function so that you store when it logs :
console.stdlog = console.log.bind(console);
console.logs = [];
console.log = function(){
console.logs.push(Array.from(arguments));
console.stdlog.apply(console, arguments);
}
console.logs contains all what was logged. You can clean it at any time by doing console.logs.length = 0;.
You can still do a standard, non storing, log by calling console.stdlog.
get all console data
how to read browser console error in js?
How to read from Chrome's console in JavaScript
https://www.quora.com/How-do-I-read-console-window-errors-from-Chrome-using-JavaScript
logs
console.defaultLog = console.log.bind(console);
console.logs = [];
console.log = function(){
// default & console.log()
console.defaultLog.apply(console, arguments);
// new & array data
console.logs.push(Array.from(arguments));
}
error
console.defaultError = console.error.bind(console);
console.errors = [];
console.error = function(){
// default & console.error()
console.defaultError.apply(console, arguments);
// new & array data
console.errors.push(Array.from(arguments));
}
warn
console.defaultWarn = console.warn.bind(console);
console.warns = [];
console.warn = function(){
// default & console.warn()
console.defaultWarn.apply(console, arguments);
// new & array data
console.warns.push(Array.from(arguments));
}
debug
console.defaultDebug = console.debug.bind(console);
console.debugs = [];
console.debug = function(){
// default & console.debug()
console.defaultDebug.apply(console, arguments);
// new & array data
console.debugs.push(Array.from(arguments));
}
I have used this code in the past to capture all console activity and store it with types and timestamps in console.everything for sending back to the server for diagnosing form data entry issues. I run this code as early as possible in the <head> element.
if (console.everything === undefined)
{
console.everything = [];
console.defaultLog = console.log.bind(console);
console.log = function(){
console.everything.push({"type":"log", "datetime":Date().toLocaleString(), "value":Array.from(arguments)});
console.defaultLog.apply(console, arguments);
}
console.defaultError = console.error.bind(console);
console.error = function(){
console.everything.push({"type":"error", "datetime":Date().toLocaleString(), "value":Array.from(arguments)});
console.defaultError.apply(console, arguments);
}
console.defaultWarn = console.warn.bind(console);
console.warn = function(){
console.everything.push({"type":"warn", "datetime":Date().toLocaleString(), "value":Array.from(arguments)});
console.defaultWarn.apply(console, arguments);
}
console.defaultDebug = console.debug.bind(console);
console.debug = function(){
console.everything.push({"type":"debug", "datetime":Date().toLocaleString(), "value":Array.from(arguments)});
console.defaultDebug.apply(console, arguments);
}
}
QA Collective's solution is very nice but has a lot of repeated code and doesn't capture errors that are not printed via the console.log, console.error, etc.
Here's the DRY and extended version of his solution that captures more error messages that show up in the console:
if (console.everything === undefined) {
console.everything = [];
function TS(){
return (new Date).toLocaleString("sv", { timeZone: 'UTC' }) + "Z"
}
window.onerror = function (error, url, line) {
console.everything.push({
type: "exception",
timeStamp: TS(),
value: { error, url, line }
})
return false;
}
window.onunhandledrejection = function (e) {
console.everything.push({
type: "promiseRejection",
timeStamp: TS(),
value: e.reason
})
}
function hookLogType(logType) {
const original= console[logType].bind(console)
return function(){
console.everything.push({
type: logType,
timeStamp: TS(),
value: Array.from(arguments)
})
original.apply(console, arguments)
}
}
['log', 'error', 'warn', 'debug'].forEach(logType=>{
console[logType] = hookLogType(logType)
})
}
I also changed the timestamp format to use the ISO format in UTC timezone, to be able to compare time stamps in different time zones more easily.
If you're working on vue.js, you can actually do this:
data () {
return {
data: []
}
},
created () {
let current_log = console.log;
console.log = msg => {
if (msg !== undefined) this.data.push(msg);
current_log.apply(null, arguments);
}
}
All logs from console will be captured and stored in data
If you just want to catch windows errors (Browser's developer tool), you just need to use the window.onerror listener. and the most important thing is to keep returning it false because If you return true in your callback, then the propagation of the error will stop and won't be log in the console anymore .
window.onerror = function myErrorHandler(err, url, line) {
//Do some stuff
console.log(err) // Uncaught SyntaxError: Invalid or unexpected token at Line no:- 1
return false; // so you still log errors into console
}

Is there a way in JavaScript to listen console events?

I'm trying to write handler for uncaught exceptions and browser warnings in Javascript. All errors and warnings should be sent to server for later review.
Handled exceptions can be caught and easily logged with
console.error("Error: ...");
or
console.warn("Warning: ...");
So they are not problem if they are called from javascript code, even more, unhandled exceptions could be caught with this peace of code:
window.onerror = function(){
// add to errors Stack trace etc.
});
}
so exceptions are pretty covered but I've stuck with warnings which browser sends to console. For instance security or html validation warnings. Example below is taken from Google Chrome console
The page at https://domainname.com/ ran insecure content from
http://domainname.com/javascripts/codex/MANIFEST.js.
It would be great if there is some event like window.onerror but for warnings. Any thoughts?
You could just wrap the console methods yourself. For example, to record each call in an array:
var logOfConsole = [];
var _log = console.log,
_warn = console.warn,
_error = console.error;
console.log = function() {
logOfConsole.push({method: 'log', arguments: arguments});
return _log.apply(console, arguments);
};
console.warn = function() {
logOfConsole.push({method: 'warn', arguments: arguments});
return _warn.apply(console, arguments);
};
console.error = function() {
logOfConsole.push({method: 'error', arguments: arguments});
return _error.apply(console, arguments);
};
More Succint Way:
// this method will proxy your custom method with the original one
function proxy(context, method, message) {
return function() {
method.apply(context, [message].concat(Array.prototype.slice.apply(arguments)))
}
}
// let's do the actual proxying over originals
console.log = proxy(console, console.log, 'Log:')
console.error = proxy(console, console.error, 'Error:')
console.warn = proxy(console, console.warn, 'Warning:')
// let's test
console.log('im from console.log', 1, 2, 3);
console.error('im from console.error', 1, 2, 3);
console.warn('im from console.warn', 1, 2, 3);
I know it's an old post but it can be useful anyway as others solution are not compatible with older browsers.
You can redefine the behavior of each function of the console (and for all browsers) like this:
// define a new console
var console = (function(oldCons){
return {
log: function(text){
oldCons.log(text);
// Your code
},
info: function (text) {
oldCons.info(text);
// Your code
},
warn: function (text) {
oldCons.warn(text);
// Your code
},
error: function (text) {
oldCons.error(text);
// Your code
}
};
}(window.console));
//Then redefine the old console
window.console = console;
I needed to debug console output on mobile devices so I built this drop-in library to capture console output and category and dump it to the page. Check the source code, it's quite straightforward.
https://github.com/samsonradu/Consolify
In the same function that you are using to do console.log(), simply post the same message to a web service that you are recording the logs on.
You're going about this backwards. Instead of intercepting when an error is logged, trigger an event as part of the error handling mechanism and log it as one of the event listeners:
try
{
//might throw an exception
foo();
}
catch (e)
{
$(document).trigger('customerror', e);
}
function customErrorHandler(event, ex)
{
console.error(ex)
}
function customErrorHandler2(event, ex)
{
$.post(url, ex);
}
this code uses jQuery and is oversimplified strictly for use as an example.

Appending to External Browser Window

I have a Windows app that contains a browser control that loads pages from my website. However, due to the Windows app, I cannot debug Javascript in the usual ways (Firebug, console, alerts, etc).
I was hoping to write a jQuery plug-in to log to an external browser window such that I can simply do something like:
$.log('test');
So far, with the following, I am able to create the window and display the templateContent, but cannot write messages to it:
var consoleWindow;
function getConsoleWindow() {
if (typeof (consoleWindow) === 'undefined') {
consoleWindow = createConsoleWindow();
}
return consoleWindow;
}
function createConsoleWindow() {
var newConsoleWindow = window.open('consoleLog', '', 'status,height=200,width=300');
var templateContent = '<html><head><title>Console</title></head>' +
'<body><h1>Console</h1><div id="console">' +
'<span id="consoleText"></span></div></body></html>';
newConsoleWindow.document.write(templateContent);
newConsoleWindow.document.close();
return newConsoleWindow;
}
function writeToConsole(message) {
var console = getConsoleWindow();
var consoleDoc = console.document.open();
var consoleMessage = document.createElement('span');
consoleMessage.innerHTML = message;
consoleDoc.getElementById('consoleText').appendChild(consoleMessage);
consoleDoc.close();
}
jQuery.log = function (message) {
if (window.console) {
console.log(message);
} else {
writeToConsole(message);
}
};
Currently, getElementById('consoleText') is failing. Is what I'm after possible, and if so, what am I missing?
Try adding
consoleDoc.getElementById('consoleText');
right before
consoleDoc.getElementById('consoleText').appendChild(consoleMessage);
If the line you added is the one that fails, then that means consoleDoc is not right, if the next line is the only one that fails then ..ById('consoleText') is not matching up
If I don't close() the document, it appears to work as I hoped.

Categories