Is it possible to write a JavaScript log class like this? - javascript

I want to write some log class that can be used like the following code.
// short-hand for logging.
log('log message', [other parameter]);
// full path for writing log.
log.write('log message', [other parameter]);
// print all log
log.print([optional parameter]);
Moreover, it must able to be written as fluent pattern.
log.write('log message').print();
Finally, it should be resetted by using the following code.
log = new log();
Thanks,

Let's implement it as a normal object first, then add some other syntaxes after:
var log = {};
log.write = function() {
// stuff...
return this;
};
log.print = function() {
// stuff...
return this;
};
log.reset = function() {
// stuff
return this;
};
As function is also an object, it can have properties, so you can replace var log = {};, with a function that redirects to log.write instead.
function log() {
return log.write.apply(log, arguments);
}
Finally, for the self-reset syntax, you can detect for a new instance, but instead of creating a new object, you reset the log and hand the same object back!
So now the log function will look like this:
function log() {
if (this instanceof log) {
return log.reset.apply(log, arguments);
}
return log.write.apply(log, arguments);
}
You can look at jsFiddle to see that it works. Warning: lot of alert()s on that page!

var Logger = function(msg,p){
this.msg = typeof msg != 'undefined' ? msg : '';
this.p = typeof p != 'undefined' ? p : '';
}
Logger.prototype = {
write : function(msg,p){
this.msg = typeof msg != 'undefined' ? msg : '';
this.p = typeof p != 'undefined' ? p : '';
},
print : function(p){
this.p = typeof p == 'undefined' ? this.p : p;
if(this.p)
alert(this.msg);
else
console.log(this.msg);
return this;
},
reset : function(){
return new Logger();
}
}
function log(msg,p){
return new Logger(msg,p).print();
}
And then you can use :
log("alert me",true);
log("log me in console!");
log().write("a msg",false).print();
var l = log();
l.write().print().reset();

Doesn't console.log in firebug serve the purpose of logging? Though, one can write their own logger implementation. But the question is when should the logger to be used? Implementation similar like you have proposed could be useful in javascript running in server side like Rhino
But i have written some code, pls try this
<html>
<head>
<script>
var log = function() {
var p_this = this;
this.write = function(p_msg){
p_this.msg = p_msg;
return this.write;
},
this.write.print = function(){
alert(p_this.msg);
}
};
var log1 = new log();
log1.write('test_message').print();
</script>
</head>
<body>
</body>
</html>
This may be helpful. This a conceptual code for the pattern u are looking for. You have to modify this or improve this. You may provide the logic for resetting and all.

You could try this glorious masterpiece:
var log = (function(out){
var msgs = [];
function log(msg){
if (typeof msg == 'string'){
msgs.push(msg);
return log;}
else{
msgs = [];
return log;}}
log.write = function(msg){
log(msg);
return log;};
log.print = function(){
for(var i=0, l=msgs.length; i<l; i++){
out(msgs[i]);}};
return log;})(function(s){console.log(s);});
The real output function is injected at the end. You should test for console.log to exist, and use an alternative otherwise (I do not know what you would prefer).
I tried the following things:
log('First message');
log.write('Second message')('Third message').write('Fourth message');
log.write('Fifth message').print();
log.print(); // Prints all messages again.
log = new log(); // Reset.
log('Lonely!').print(); // Prints only 'Lonely!'.
Beware: log(); or log(undefined); (with undefined undefined) will reset the thing as well, while new log('Foobar'); will add the message 'Foobar'. But this was not in your test cases, so I ignored it.
This is also possible:
(new log()).write('Message');
(new log())('Message');
(new log()).print(); // Prints empty log.

Try to look at JSLog javascript logging framework:
https://github.com/dingyonglaw/JSLog
It supports all browser, and also firebug-lite;
==============
Example Usage:
==============
Logging:
--------
// Register a module logger, p is now logger for 'App.Login'
var p = JSLog.Register('App.Login');
// Log something, and it will display "[App.Login] something" in your console;
p.log('something');
// You can do something as usual:
p.warn('warning!');
p.info('info msg');
Log Level:
----------
The Logger comes with 5 level of logging access: 1-5
which coresponding to: 'error', 'warn', 'info', 'debug', 'log'
// You can set your level like this, to display only info message in console
p.SetLevel(3);
// You can get your current logging level with:
p.GetLevel();
Logging History:
----------------
The logger records your logs and you can Dump it later!
// Dump by Module Name
JSLog.Dump('App.Login');
// Dump all modules in nested view
JSLog.Dump();
// Selective Dump by filtering module name:
JSLog.SetFilter('module-name');
JSLog.Dump(); // Will exclude 'module-name' module
// Clear filter by module name:
JSLog.UnsetFilter('module-name');
// Get assigned filters
JSLog.GetFilter();

Related

In Javascript using try / catch, besides the error message how can I catch function name and all arguments names/values passed?

I'd like to create a generic debugging routine using try / catch, within the catch segment I'd like a piece of code which will log the function name and all params passed (name => value).
Is this possible?
try{
// code
} catch(e) {
var function_name = ''; // how can I get this?
var function_params = ''; // how can I get this?
var errorText = 'UNEXPECTED ERROR: \n\n Error Details: '+e.toString();
errorText = errorText+' Called:'+function_name+'('+function_params+')';
}
In Chrome and Firefox you can use e.stack but no such luck in Internet Explorer.
The stack property is an array that you can loop over. It might differ a bit from browser to browser but it shouldn't be too hard to make a readable stacktrace.
You currently can't catch the arguments in all browsers.
In Chrome you might need to use:
Error.prepareStackTrace = function (error, stack) {
return stack;
}; // augments Chrome's Error.stack property with context data
Try using arguments.callee for the function name
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee
This isn't an exact answer because I don't give the function Name but this returns the entire function body...
Working sample: http://jsfiddle.net/sS6sY/
Object.prototype.getArgs = function(){
//returns the arguments passed to a function.
var els = [];
for(i=0; i< this.arguments.length; i++){
els.push(this.arguments[i]);
}
return els.join(',')
}
Object.prototype.getMethod = function(){
//returns the function as a string.
return this.constructor;
}
var Foo = function(){
this.arguments = arguments;
try{
throw {
name: 'Oops',
message: 'Didn\'t mean to make a Foo!'
}
}
catch(e){
console.log(this.getArgs());
console.log(this.getMethod());
}
}

Backbone.js -- Can't access another model's methods from a model?

Given these snippets (hopefully complete enough for this question)...
ModelA.js (has many modelBs):
ModelBs = (function(_super) {
ModelB.prototype.bWithName = function(name) {
return this.find(function (b) {
return b.name == name;
});
}
})(Backbone.Collection);
return ModelA = (function(_super) {
...
ModelA.prototype.initialize = function() {
this.modelbs = new ModelBs(this.modelbs, {});
};
ModelA.prototype.bWithName = function(name) {
return this.modelbs.bWithName(name);
};
return modelA;
})(BaseModel);
ModelC.js (has one modelA):
ModelC.prototype.toString = function(opts) {
...
console.log(this.modelA); // defined...
console.log(this.modelA.modelBs); // defined...
console.log(this.modelA.bWithName("foo")); // undefined
...
}
In ModelC.js, why are this.modelA and this.modelA.modelBs defined, but this.modelA.bWithName() undefined, and how can I fix it?
This works: this.modelA.modelBs.first().
This returns undefined: this.modelA.modelBs.where({name:"foo"}).
In the web console, this works: modelZ.modelAs.first().bWithName("foo").attributes.
Are accessors or methods in general not available through other models?
Thanks-
Bah. The method was in fact available, but the strict equals (===) was killing the search. In one instance I was trying to search with the wrong typeof name.
Thanks for the input though!

Can I extend the console object (for rerouting the logging) in javascript?

Is it possible to extend the console object?
I tried something like:
Console.prototype.log = function(msg){
Console.prototype.log.call(msg);
alert(msg);
}
But this didn't work.
I want to add additional logging to the console object via a framework like log4javascript and still use the standard console object (in cases where log4javascript is not available) in my code.
Thanks in advance!
Try following:
(function() {
var exLog = console.log;
console.log = function(msg) {
exLog.apply(this, arguments);
alert(msg);
}
})()
You Can Also add log Time in This Way :
added Momentjs or use New Date() instead of moment.
var oldConsole = console.log;
console.log = function(){
var timestamp = "[" + moment().format("YYYY-MM-DD HH:mm:ss:SSS") + "] ";
Array.prototype.unshift.call(arguments, timestamp);
oldConsole.apply(this, arguments);
};
It's really the same solution some others have given, but I believe this is the most elegant and least hacky way to accomplish this. The spread syntax (...args) makes sure not a single argument is lost.
var _console={...console}
console.log = function(...args) {
var msg = {...args}[0];
//YOUR_CODE
_console.log(...args);
}
For ECMAScript 2015 and later
You can use the newer Proxy feature from the ECMAScript 2015 standard to "hijack" the global console.log.
Source-Code
'use strict';
class Mocker {
static mockConsoleLog() {
Mocker.oldGlobalConsole = window.console;
window.console = new Proxy(window.console, {
get(target, property) {
if (property === 'log') {
return function(...parameters) {
Mocker.consoleLogReturnValue = parameters.join(' ');
}
}
return target[property];
}
});
}
static unmockConsoleLog() {
window.console = Mocker.oldGlobalConsole;
}
}
Mocker.mockConsoleLog();
console.log('hello'); // nothing happens here
Mocker.unmockConsoleLog();
if (Mocker.consoleLogReturnValue === 'hello') {
console.log('Hello world!'); // Hello world!
alert(Mocker.consoleLogReturnValue);
// anything you want to do with the console log return value here...
}
Online Demo
Repl.it.
Node.js users...
... I do not forget you. You can take this source-code and replace window.console by gloabl.console to properly reference the console object (and of course, get rid of the alert call). In fact, I wrote this code initially and tested it on Node.js.
// console aliases and verbose logger - console doesnt prototype
var c = console;
c.l = c.log,
c.e = c.error,
c.v = c.verbose = function() {
if (!myclass || !myclass.verbose) // verbose switch
return;
var args = Array.prototype.slice.call(arguments); // toArray
args.unshift('Verbose:');
c.l.apply(this, args); // log
};
// you can then do
var myclass = new myClass();
myclass.prototype.verbose = false;
// generally these calls would be inside your class
c.v('1 This will NOT log as verbose == false');
c.l('2 This will log');
myclass.verbose = true;
c.v('3 This will log');
I noted that the above use of Array.prototype.unshift.call by nitesh is a better way to add the 'Verbose:' tag.
You can override the default behavior of the console.log function using the below approach, the below example demonstrates to log the line number using the overridden function.
let line = 0;
const log = console.log;
console.log = (...data) => log(`${++line} ===>`, ...data)
console.log(11, 1, 2)
console.log(11, 1, 'some')

Intercept calls to console.log in Chrome

I have a script that I can't change that makes a lot of console.log calls. I want to add another layer and respond if the calls contain certain strings. This works in Firefox, but throws an "Illegal invocation" error in Chrome on the 4th line:
var oldConsole = {};
oldConsole.log = console.log;
console.log = function (arg) {
oldConsole.log('MY CONSOLE!!');
oldConsole.log(arg);
}
Any ideas how to get around that? I also tried cloning the console...
You need to call console.log in the context of console for chrome:
(function () {
var log = console.log;
console.log = function () {
log.call(this, 'My Console!!!');
log.apply(this, Array.prototype.slice.call(arguments));
};
}());
Modern language features can significantly simplify this snippet:
{
const log = console.log.bind(console)
console.log = (...args) => {
log('My Console!!!')
log(...args)
}
}
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;
You can also use the same logic, but call it off the console object so the context is the same.
if(window.console){
console.yo = console.log;
console.log = function(str){
console.yo('MY CONSOLE!!');
console.yo(str);
}
}
With ES6 new spread operator you can write it like this
(function () {
var log = console.log;
console.log = function () {
log.call(this, 'My Console!!!', ...arguments);
};
}());
Can be simply:
console.log = (m) => terminal.innerHTML = JSON.stringify(m)
#terminal {background: black; color:chartreuse}
$ > <span id="terminal"></span>
<hr>
<button onclick="console.log('Hello world!!')">3V3L</button>
<button onclick="console.log(document)">3V3L</button>
<button onclick="console.log(Math.PI)">3V3L</button>
To fully intercept the console, we can override every methods :
const bindConsole=function(onMessage){
Object.keys(console)
.filter(type=>typeof(console[type])==='function')// *1
.forEach(type=>{
let _old=console[type];
console[type] = function (...args) {
_old.apply(console,args);
onMessage(type,args);// *2
};
});
};
For old browsers :
var bindOldConsole=function(onMessage){
for(var k in console){// *1
if(typeof(console[k])=='function')(function(type){
var _old=console[type];
console[type] = function () {
_old.apply(console,arguments);
onMessage(type,arguments);
};
})(k);
}
};
*1 Looks like console has only methods but better be sure.
*2 You may block cyclic calls to console from onMessage by replacing this line with :
if(!isCyclic())onMessage(type,args);
// es6. Not sure concerning old browsers :(
const isCyclic=function (){
let erst=(new Error()).stack.split('\n');
return erst.includes(erst[1],2);
};
Since I cannot comment (yet) on #ludovic-feltz answer, here is his answer corrected to allow string interpolation in the console :
// 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;

What happened to console.log in IE8?

According to this post it was in the beta, but it's not in the release?
console.log is only available after you have opened the Developer Tools (F12 to toggle it open and closed).
Funny thing is that after you've opened it, you can close it, then still post to it via console.log calls, and those will be seen when you reopen it.
I'm thinking that is a bug of sorts, and may be fixed, but we shall see.
I'll probably just use something like this:
function trace(s) {
if ('console' in self && 'log' in console) console.log(s)
// the line below you might want to comment out, so it dies silent
// but nice for seeing when the console is available or not.
else alert(s)
}
and even simpler:
function trace(s) {
try { console.log(s) } catch (e) { alert(s) }
}
Even better for fallback is this:
var alertFallback = true;
if (typeof console === "undefined" || typeof console.log === "undefined") {
console = {};
if (alertFallback) {
console.log = function(msg) {
alert(msg);
};
} else {
console.log = function() {};
}
}
This is my take on the various answers. I wanted to actually see the logged messages, even if I did not have the IE console open when they were fired, so I push them into a console.messages array that I create. I also added a function console.dump() to facilitate viewing the whole log. console.clear() will empty the message queue.
This solutions also "handles" the other Console methods (which I believe all originate from the Firebug Console API)
Finally, this solution is in the form of an IIFE, so it does not pollute the global scope. The fallback function argument is defined at the bottom of the code.
I just drop it in my master JS file which is included on every page, and forget about it.
(function (fallback) {
fallback = fallback || function () { };
// function to trap most of the console functions from the FireBug Console API.
var trap = function () {
// create an Array from the arguments Object
var args = Array.prototype.slice.call(arguments);
// console.raw captures the raw args, without converting toString
console.raw.push(args);
var message = args.join(' ');
console.messages.push(message);
fallback(message);
};
// redefine console
if (typeof console === 'undefined') {
console = {
messages: [],
raw: [],
dump: function() { return console.messages.join('\n'); },
log: trap,
debug: trap,
info: trap,
warn: trap,
error: trap,
assert: trap,
clear: function() {
console.messages.length = 0;
console.raw.length = 0 ;
},
dir: trap,
dirxml: trap,
trace: trap,
group: trap,
groupCollapsed: trap,
groupEnd: trap,
time: trap,
timeEnd: trap,
timeStamp: trap,
profile: trap,
profileEnd: trap,
count: trap,
exception: trap,
table: trap
};
}
})(null); // to define a fallback function, replace null with the name of the function (ex: alert)
Some extra info
The line var args = Array.prototype.slice.call(arguments); creates an Array from the arguments Object. This is required because arguments is not really an Array.
trap() is a default handler for any of the API functions. I pass the arguments to message so that you get a log of the arguments that were passed to any API call (not just console.log).
Edit
I added an extra array console.raw that captures the arguments exactly as passed to trap(). I realized that args.join(' ') was converting objects to the string "[object Object]" which may sometimes be undesirable. Thanks bfontaine for the suggestion.
It's worth noting that console.log in IE8 isn't a true Javascript function. It doesn't support the apply or call methods.
Assuming you don't care about a fallback to alert, here's an even more concise way to workaround Internet Explorer's shortcomings:
var console=console||{"log":function(){}};
I really like the approach posted by "orange80". It's elegant because you can set it once and forget it.
The other approaches require you to do something different (call something other than plain console.log() every time), which is just asking for troubleā€¦ I know that I'd eventually forget.
I've taken it a step further, by wrapping the code in a utility function that you can call once at the beginning of your javascript, anywhere as long as it's before any logging. (I'm installing this in my company's event data router product. It will help simplify the cross-browser design of its new admin interface.)
/**
* Call once at beginning to ensure your app can safely call console.log() and
* console.dir(), even on browsers that don't support it. You may not get useful
* logging on those browers, but at least you won't generate errors.
*
* #param alertFallback - if 'true', all logs become alerts, if necessary.
* (not usually suitable for production)
*/
function fixConsole(alertFallback)
{
if (typeof console === "undefined")
{
console = {}; // define it if it doesn't exist already
}
if (typeof console.log === "undefined")
{
if (alertFallback) { console.log = function(msg) { alert(msg); }; }
else { console.log = function() {}; }
}
if (typeof console.dir === "undefined")
{
if (alertFallback)
{
// THIS COULD BE IMPROVEDā€¦ maybe list all the object properties?
console.dir = function(obj) { alert("DIR: "+obj); };
}
else { console.dir = function() {}; }
}
}
If you get "undefined" to all of your console.log calls, that probably means you still have an old firebuglite loaded (firebug.js). It will override all the valid functions of IE8's console.log even though they do exist. This is what happened to me anyway.
Check for other code overriding the console object.
The best solution for any browser that lack a console is:
// Avoid `console` errors in browsers that lack a console.
(function() {
var method;
var noop = function () {};
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
}());
There are so many Answers. My solution for this was:
globalNamespace.globalArray = new Array();
if (typeof console === "undefined" || typeof console.log === "undefined") {
console = {};
console.log = function(message) {globalNamespace.globalArray.push(message)};
}
In short, if console.log doesn't exists (or in this case, isn't opened) then store the log in a global namespace Array. This way, you're not pestered with millions of alerts and you can still view your logs with the developer console opened or closed.
Here is my "IE please don't crash"
typeof console=="undefined"&&(console={});typeof console.log=="undefined"&&(console.log=function(){});
if (window.console && 'function' === typeof window.console.log) {
window.console.log(o);
}
I found this on github:
// usage: log('inside coolFunc', this, arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function f() {
log.history = log.history || [];
log.history.push(arguments);
if (this.console) {
var args = arguments,
newarr;
args.callee = args.callee.caller;
newarr = [].slice.call(args);
if (typeof console.log === 'object') log.apply.call(console.log, console, newarr);
else console.log.apply(console, newarr);
}
};
// make it safe to use console.log always
(function(a) {
function b() {}
for (var c = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","), d; !! (d = c.pop());) {
a[d] = a[d] || b;
}
})(function() {
try {
console.log();
return window.console;
} catch(a) {
return (window.console = {});
}
} ());
I'm using Walter's approach from above (see: https://stackoverflow.com/a/14246240/3076102)
I mix in a solution I found here https://stackoverflow.com/a/7967670 to properly show Objects.
This means the trap function becomes:
function trap(){
if(debugging){
// create an Array from the arguments Object
var args = Array.prototype.slice.call(arguments);
// console.raw captures the raw args, without converting toString
console.raw.push(args);
var index;
for (index = 0; index < args.length; ++index) {
//fix for objects
if(typeof args[index] === 'object'){
args[index] = JSON.stringify(args[index],null,'\t').replace(/\n/g,'<br>').replace(/\t/g,' ');
}
}
var message = args.join(' ');
console.messages.push(message);
// instead of a fallback function we use the next few lines to output logs
// at the bottom of the page with jQuery
if($){
if($('#_console_log').length == 0) $('body').append($('<div />').attr('id', '_console_log'));
$('#_console_log').append(message).append($('<br />'));
}
}
}
I hope this is helpful:-)
I like this method (using jquery's doc ready)... it lets you use console even in ie... only catch is that you need to reload the page if you open ie's dev tools after the page loads...
it could be slicker by accounting for all the functions, but I only use log so this is what I do.
//one last double check against stray console.logs
$(document).ready(function (){
try {
console.log('testing for console in itcutils');
} catch (e) {
window.console = new (function (){ this.log = function (val) {
//do nothing
}})();
}
});
Here is a version that will log to the console when the developer tools are open and not when they are closed.
(function(window) {
var console = {};
console.log = function() {
if (window.console && (typeof window.console.log === 'function' || typeof window.console.log === 'object')) {
window.console.log.apply(window, arguments);
}
}
// Rest of your application here
})(window)
Make your own console in html .... ;-)
This can be imprved but you can start with :
if (typeof console == "undefined" || typeof console.log === "undefined") {
var oDiv=document.createElement("div");
var attr = document.createAttribute('id'); attr.value = 'html-console';
oDiv.setAttributeNode(attr);
var style= document.createAttribute('style');
style.value = "overflow: auto; color: red; position: fixed; bottom:0; background-color: black; height: 200px; width: 100%; filter: alpha(opacity=80);";
oDiv.setAttributeNode(style);
var t = document.createElement("h3");
var tcontent = document.createTextNode('console');
t.appendChild(tcontent);
oDiv.appendChild(t);
document.body.appendChild(oDiv);
var htmlConsole = document.getElementById('html-console');
window.console = {
log: function(message) {
var p = document.createElement("p");
var content = document.createTextNode(message.toString());
p.appendChild(content);
htmlConsole.appendChild(p);
}
};
}
It works in IE8. Open IE8's Developer Tools by hitting F12.
>>console.log('test')
LOG: test

Categories