How do I create a custom Error in JavaScript? - javascript

For some reason it looks like constructor delegation doesn't work in the following snippet:
function NotImplementedError() {
Error.apply(this, arguments);
}
NotImplementedError.prototype = new Error();
var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")
Running this gives The message is: ''. Any ideas as to why, or if there is a better way to create a new Error subclass? Is there a problem with applying to the native Error constructor that I don't know about?

Update your code to assign your prototype to the Error.prototype and the instanceof and your asserts work.
function NotImplementedError(message = "") {
this.name = "NotImplementedError";
this.message = message;
}
NotImplementedError.prototype = Error.prototype;
However, I would just throw your own object and just check the name property.
throw {name : "NotImplementedError", message : "too lazy to implement"};
Edit based on comments
After looking at the comments and trying to remember why I would assign prototype to Error.prototype instead of new Error() like Nicholas Zakas did in his article, I created a jsFiddle with the code below:
function NotImplementedError(message = "") {
this.name = "NotImplementedError";
this.message = message;
}
NotImplementedError.prototype = Error.prototype;
function NotImplementedError2(message = "") {
this.message = message;
}
NotImplementedError2.prototype = new Error();
try {
var e = new NotImplementedError("NotImplementedError message");
throw e;
} catch (ex1) {
console.log(ex1.stack);
console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
console.log("ex1.name = " + ex1.name);
console.log("ex1.message = " + ex1.message);
}
try {
var e = new NotImplementedError2("NotImplementedError2 message");
throw e;
} catch (ex1) {
console.log(ex1.stack);
console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2));
console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
console.log("ex1.name = " + ex1.name);
console.log("ex1.message = " + ex1.message);
}
The console output was this.
undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message
This confirmes the "problem" I ran into was the stack property of the error was the line number where new Error() was created, and not where the throw e occurred. However, that may be better that having the side effect of a NotImplementedError.prototype.name = "NotImplementedError" line affecting the Error object.
Also, notice with NotImplementedError2, when I don't set the .name explicitly, it is equal to "Error". However, as mentioned in the comments, because that version sets prototype to new Error(), I could set NotImplementedError2.prototype.name = "NotImplementedError2" and be OK.

In ES2015, you can use class to do this cleanly:
class NotImplemented extends Error {
constructor(message = "", ...args) {
super(message, ...args);
this.message = message + " has not yet been implemented.";
}
}
This does not modify the global Error prototype, allows you to customize message, name, and other attributes, and properly captures the stack. It's also pretty readable.
Of course, you may need to use a tool like babel if your code will be running on older browsers.

All of the above answers are terrible awful - really. Even the one with 107 ups! The real answer is here guys:
Inheriting from the Error object - where is the message property?
TL;DR:
A. The reason message isn't being set is that Error is a function that returns a new Error object and does not manipulate this in any way.
B. The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way:
function MyError() {
var temp = Error.apply(this, arguments);
temp.name = this.name = 'MyError';
this.message = temp.message;
if(Object.defineProperty) {
// getter for more optimizy goodness
/*this.stack = */Object.defineProperty(this, 'stack', {
get: function() {
return temp.stack
},
configurable: true // so you can change it if you want
})
} else {
this.stack = temp.stack
}
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
constructor: {
value: MyError,
writable: true,
configurable: true
}
});
var myError = new MyError("message");
console.log("The message is: '" + myError.message + "'"); // The message is: 'message'
console.log(myError instanceof Error); // true
console.log(myError instanceof MyError); // true
console.log(myError.toString()); // MyError: message
console.log(myError.stack); // MyError: message \n
// <stack trace ...>
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/
You could probably do some trickery to enumerate through all the non-enumerable properties of the tmp Error to set them rather than explicitly setting only stack and message, but the trickery isn't supported in ie<9

If anyone is curious on how to create a custom error and get the stack trace:
function CustomError(message) {
this.name = 'CustomError';
this.message = message || '';
var error = new Error(this.message);
error.name = this.name;
this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);
try {
throw new CustomError('foobar');
}
catch (e) {
console.log('name:', e.name);
console.log('message:', e.message);
console.log('stack:', e.stack);
}

class NotImplementedError extends Error {
constructor(message) {
super(message);
this.message = message;
}
}
NotImplementedError.prototype.name = 'NotImplementedError';
module.exports = NotImplementedError;
and
try {
var e = new NotImplementedError("NotImplementedError message");
throw e;
} catch (ex1) {
console.log(ex1.stack);
console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
console.log("ex1.name = " + ex1.name);
console.log("ex1.message = " + ex1.message);
}
It is just a class representation of this answer.
output
NotImplementedError: NotImplementedError message
...stacktrace
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message

This section of the standard may explain why the Error.apply call doesn't initialize the object:
15.11.1 The Error Constructor Called as a Function
When Error is called as a function rather than as a constructor, it creates and
initialises a new Error object. Thus the function call Error(...) is
equivalent to the object creation expression new Error(...) with the
same arguments.
In this case the Error function probably determines that it's not being called as a constructor, so it returns a new Error instance rather than initializing the this object.
Testing with the following code seems to demonstrate that this is in fact what's happening:
function NotImplementedError() {
var returned = Error.apply(this, arguments);
console.log("returned.message = '" + returned.message + "'");
console.log("this.message = '" + this.message + "'");
}
NotImplementedError.prototype = new Error();
var nie = new NotImplementedError("some message");
The following output is generated when this is run:
returned.message = 'some message'
this.message = ''

I like to do it like this:
Make use of name so toString() throws "{code}: {message}"
Return same thing to super so it appears the same in the stacktrace
Attach code to error.code as checking/parsing a code is better in code than checking a message, which you might want to localize for example
Attach message to error.message as an alternative to error.toString()
class AppException extends Error {
constructor(code, message) {
const fullMsg = message ? `${code}: ${message}` : code;
super(fullMsg);
this.name = code;
this.code = code;
this.message = fullMsg;
}
toString() {
return this.message;
}
}
// Just a code
try {
throw new AppException('FORBIDDEN');
} catch(e) {
console.error(e);
console.error(e.toString());
console.log(e.code === 'FORBIDDEN');
}
// A code and a message
try {
throw new AppException('FORBIDDEN', 'You don\'t have access to this page');
} catch(e) {
console.error(e);
console.error(e.toString());
console.log(e.code === 'FORBIDDEN');
}

function InvalidValueError(value, type) {
this.message = "Expected `" + type.name + "`: " + value;
var error = new Error(this.message);
this.stack = error.stack;
}
InvalidValueError.prototype = new Error();
InvalidValueError.prototype.name = InvalidValueError.name;
InvalidValueError.prototype.constructor = InvalidValueError;

Accoring to Joyent you shouldn’t mess with the stack property (which I see in lots of answers given here), because it will have a negative impact on performance. Here is what they say:
stack: generally, don't mess with this. Don't even augment it. V8 only computes it if someone actually reads the property, which improves performance dramatically for handlable errors. If you read the property just to augment it, you'll end up paying the cost even if your caller doesn't need the stack.
I like and would like to mention their idea of wrapping the original error which is a nice replacement for passing on the stack.
So here is how I create a custom error, considering the above mentioned:
es5 version:
function RError(options) {
options = options || {}; // eslint-disable-line no-param-reassign
this.name = options.name;
this.message = options.message;
this.cause = options.cause;
// capture stack (this property is supposed to be treated as private)
this._err = new Error();
// create an iterable chain
this.chain = this.cause ? [this].concat(this.cause.chain) : [this];
}
RError.prototype = Object.create(Error.prototype, {
constructor: {
value: RError,
writable: true,
configurable: true
}
});
Object.defineProperty(RError.prototype, 'stack', {
get: function stack() {
return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n');
}
});
Object.defineProperty(RError.prototype, 'why', {
get: function why() {
var _why = this.name + ': ' + this.message;
for (var i = 1; i < this.chain.length; i++) {
var e = this.chain[i];
_why += ' <- ' + e.name + ': ' + e.message;
}
return _why;
}
});
// usage
function fail() {
throw new RError({
name: 'BAR',
message: 'I messed up.'
});
}
function failFurther() {
try {
fail();
} catch (err) {
throw new RError({
name: 'FOO',
message: 'Something went wrong.',
cause: err
});
}
}
try {
failFurther();
} catch (err) {
console.error(err.why);
console.error(err.stack);
console.error(err.cause.stack);
}
es6 version:
class RError extends Error {
constructor({name, message, cause}) {
super();
this.name = name;
this.message = message;
this.cause = cause;
}
[Symbol.iterator]() {
let current = this;
let done = false;
const iterator = {
next() {
const val = current;
if (done) {
return { value: val, done: true };
}
current = current.cause;
if (!val.cause) {
done = true;
}
return { value: val, done: false };
}
};
return iterator;
}
get why() {
let _why = '';
for (const e of this) {
_why += `${_why.length ? ' <- ' : ''}${e.name}: ${e.message}`;
}
return _why;
}
}
// usage
function fail() {
throw new RError({
name: 'BAR',
message: 'I messed up.'
});
}
function failFurther() {
try {
fail();
} catch (err) {
throw new RError({
name: 'FOO',
message: 'Something went wrong.',
cause: err
});
}
}
try {
failFurther();
} catch (err) {
console.error(err.why);
console.error(err.stack);
console.error(err.cause.stack);
}
I’ve put my solution into a module, here it is: https://www.npmjs.com/package/rerror

I had a similar issue to this. My error needs to be an instanceof both Error and NotImplemented, and it also needs to produce a coherent backtrace in the console.
My solution:
var NotImplemented = (function() {
var NotImplemented, err;
NotImplemented = (function() {
function NotImplemented(message) {
var err;
err = new Error(message);
err.name = "NotImplemented";
this.message = err.message;
if (err.stack) this.stack = err.stack;
}
return NotImplemented;
})();
err = new Error();
err.name = "NotImplemented";
NotImplemented.prototype = err;
return NotImplemented;
}).call(this);
// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented('I was too busy').message));
throw new NotImplemented("just didn't feel like it");
Result of running with node.js:
instanceof Error: true
instanceof NotImplemented: true
message: I was too busy
/private/tmp/t.js:24
throw new NotImplemented("just didn't feel like it");
^
NotImplemented: just didn't feel like it
at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
at Module._compile (module.js:449:26)
at Object.Module._extensions..js (module.js:467:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.runMain (module.js:487:10)
at process.startup.processNextTick.process._tickCallback (node.js:244:9)
The error passes all 3 of my criteria, and although the stack property is nonstandard, it is supported in most newer browsers which is acceptable in my case.

I used the Constructor Pattern to create the new error object. I defined the prototype chain such as an Error instance. See the MDN Error constructor reference.
You can check this snippet on this gist.
IMPLEMENTATION
// Creates user-defined exceptions
var CustomError = (function() {
'use strict';
//constructor
function CustomError() {
//enforces 'new' instance
if (!(this instanceof CustomError)) {
return new CustomError(arguments);
}
var error,
//handles the arguments object when is passed by enforcing a 'new' instance
args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments),
message = args.shift() || 'An exception has occurred';
//builds the message with multiple arguments
if (~message.indexOf('}')) {
args.forEach(function(arg, i) {
message = message.replace(RegExp('\\{' + i + '}', 'g'), arg);
});
}
//gets the exception stack
error = new Error(message);
//access to CustomError.prototype.name
error.name = this.name;
//set the properties of the instance
//in order to resemble an Error instance
Object.defineProperties(this, {
stack: {
enumerable: false,
get: function() { return error.stack; }
},
message: {
enumerable: false,
value: message
}
});
}
// Creates the prototype and prevents the direct reference to Error.prototype;
// Not used new Error() here because an exception would be raised here,
// but we need to raise the exception when CustomError instance is created.
CustomError.prototype = Object.create(Error.prototype, {
//fixes the link to the constructor (ES5)
constructor: setDescriptor(CustomError),
name: setDescriptor('JSU Error')
});
function setDescriptor(value) {
return {
configurable: false,
enumerable: false,
writable: false,
value: value
};
}
//returns the constructor
return CustomError;
}());
USAGE
The CustomError constructor can receive many arguments to build the message, e.g.
var err1 = new CustomError("The url of file is required"),
err2 = new CustomError("Invalid Date: {0}", +"date"),
err3 = new CustomError("The length must be greater than {0}", 4),
err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2");
throw err4;
And this is how the custom error looks:

This is my implementation:
class HttpError extends Error {
constructor(message, code = null, status = null, stack = null, name = null) {
super();
this.message = message;
this.status = 500;
this.name = name || this.constructor.name;
this.code = code || `E_${this.name.toUpperCase()}`;
this.stack = stack || null;
}
static fromObject(error) {
if (error instanceof HttpError) {
return error;
}
else {
const { message, code, status, stack } = error;
return new ServerError(message, code, status, stack, error.constructor.name);
}
}
expose() {
if (this instanceof ClientError) {
return { ...this };
}
else {
return {
name: this.name,
code: this.code,
status: this.status,
}
}
}
}
class ServerError extends HttpError {}
class ClientError extends HttpError { }
class IncorrectCredentials extends ClientError {
constructor(...args) {
super(...args);
this.status = 400;
}
}
class ResourceNotFound extends ClientError {
constructor(...args) {
super(...args);
this.status = 404;
}
}
Example usage #1:
app.use((req, res, next) => {
try {
invalidFunction();
}
catch (err) {
const error = HttpError.fromObject(err);
return res.status(error.status).send(error.expose());
}
});
Example usage #2:
router.post('/api/auth', async (req, res) => {
try {
const isLogged = await User.logIn(req.body.username, req.body.password);
if (!isLogged) {
throw new IncorrectCredentials('Incorrect username or password');
}
else {
return res.status(200).send({
token,
});
}
}
catch (err) {
const error = HttpError.fromObject(err);
return res.status(error.status).send(error.expose());
}
});

I just had to implement something like this and found that the stack was lost in my own error implementation. What I had to do was create a dummy error and retrieve the stack from that:
My.Error = function (message, innerException) {
var err = new Error();
this.stack = err.stack; // IMPORTANT!
this.name = "Error";
this.message = message;
this.innerException = innerException;
}
My.Error.prototype = new Error();
My.Error.prototype.constructor = My.Error;
My.Error.prototype.toString = function (includeStackTrace) {
var msg = this.message;
var e = this.innerException;
while (e) {
msg += " The details are:\n" + e.message;
e = e.innerException;
}
if (includeStackTrace) {
msg += "\n\nStack Trace:\n\n" + this.stack;
}
return msg;
}

The constructor needs to be like a factory method and return what you want. If you need additional methods/properties, you can add them to the object before returning it.
function NotImplementedError(message) { return new Error("Not implemented", message); }
x = new NotImplementedError();
Though I'm not sure why you'd need to do this. Why not just use new Error... ? Custom exceptions don't really add much in JavaScript (or probably any untyped language).

This is implemented nicely in the Cesium DeveloperError:
Docs
Source
In it's simplified form:
var NotImplementedError = function(message) {
this.name = 'NotImplementedError';
this.message = message;
this.stack = (new Error()).stack;
}
// Later on...
throw new NotImplementedError();

At the expense of not being able to use instanceof, the following preserves the original stack trace and doesn't use any non-standard tricks.
// the function itself
var fixError = function(err, name) {
err.name = name;
return err;
}
// using the function
try {
throw fixError(new Error('custom error message'), 'CustomError');
} catch (e) {
if (e.name == 'CustomError')
console.log('Wee! Custom Error! Msg:', e.message);
else
throw e; // unhandled. let it propagate upwards the call stack
}

Another alternative , might not work in all enviroments.Atleast assured it works in nodejs 0.8
This approach uses a non standard way of modifying the internal proto prop
function myError(msg){
var e = new Error(msg);
_this = this;
_this.__proto__.__proto__ = e;
}

If you are using Node/Chrome. The following snippet will get you extension which meets the following requirements.
err instanceof Error
err instanceof CustomErrorType
console.log() returns [CustomErrorType] when created with a message
console.log() returns [CustomErrorType: message] when created without a message
throw/stack provides the information at the point the error was created.
Works optimally in Node.JS, and Chrome.
Will pass instanceof checks in Chrome, Safari, Firefox and IE 8+, but will not have a valid stack outside of Chrome/Safari. I'm OK with that because I can debug in chrome, but code which requires specific error types will still function cross browser. If you need Node only you can easily remove the if statements and you're good to go.
Snippet
var CustomErrorType = function(message) {
if (Object.defineProperty) {
Object.defineProperty(this, "message", {
value : message || "",
enumerable : false
});
} else {
this.message = message;
}
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomErrorType);
}
}
CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";
Usage
var err = new CustomErrorType("foo");
Output
var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);
[CustomErrorType: foo]
CustomErrorType: foo
at Object.<anonymous> (/errorTest.js:27:12)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3
/errorTest.js:30
throw err;
^
CustomErrorType: foo
at Object.<anonymous> (/errorTest.js:27:12)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3

The following worked for me taken from the official Mozilla documentation Error.
function NotImplementedError(message) {
var instance = new Error(message);
instance.name = 'NotImplementedError';
Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
if (Error.captureStackTrace) {
Error.captureStackTrace(instance, NotImplementedError);
}
return instance;
}
NotImplementedError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});

Here is my solution for supporting pre-es2015 browsers. It does not do any fancy prototype tweaking and will not break debuggers.
/** Custom Errors
// Depends on underscore js
// This will declare an CustError() class in both 'this' and '_exports' namespaces
// ctor is optional
declare_cust_error(function CustError(){}, {ns: [this, _exports], ctor:
function cust_err_ctor(instance, clazz, name, msg, info){
q$.called(arguments)
}
})
// Usage:
// Second param (pojso) is optional
try {
throw CustError.create("foo", {k1: 'v1', k2: 'v2'})
}catch(ex){
if(CustError.is_inst(ex)){
console.error("its a CustError", ex)
} else {
throw ex
}
}
**/
function declare_cust_error(error_class, opts){
var p, c, cp
if(!error_class||!(p=error_class.prototype))throw new Error("error_class must be a Class")
try{
c = p.constructor; cp = c.toString()
}catch(ex){}
if(!cp || cp.indexOf('function ') != 0 || cp.indexOf('[native code]') > 0)
throw new Error("error_class must be a classic proto class (pre-es6) but got: " + error_class.toString())
opts=opts||{}
error_class.__is_cust_error__ = true
error_class.__cust_error_name__ = c.name
error_class.create = function cust_error_create(msg, info){
var instance = new Error(msg)
instance.info = info
instance.__is_cust_error__ = true
instance.__cust_error_name__ = c.name
if(_.isFunction(opts.ctor)){
opts.ctor(instance, error_class, c.name, msg, info)
}
return instance
}
error_class.is_inst = function cust_error_is_inst(instanace){
return ( (instanace instanceof Error) && instanace.__cust_error_name__ === error_class.__cust_error_name__ )
}
// Declare error in namespace(s)
_.each(_.isArray(opts.ns)?opts.ns:[opts.ns], function(ns){ ns[c.name] = error_class })
return error_class
}

A lot of the methods above won't work.
The last one is an actual error. If you use a string, it looks good, but it doesn't give a stack trace. If you throw with Error, you can't have "Uncaught BadError: bad", so you'll have to remove the custom error (sadly). If you throw an object, it looks kind of off, and the final one is just an average error.
This method creates an error with a custom name while preserving stack tracing:
var errProto = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
})
var isFirefox = !!window.InstallTrigger
// Hide stack for Firefox only, as stacks can cause problems with high "frame" counts.
function createError(name, message, hideStack) {
if (message == null) {
message = ""
}
var customError = Error(message)
customError.name = name
Object.setPrototypeOf(customError, errProto)
if (isFirefox && hideStack) {
customError.stack = ""
} else if (isFirefox) {
var stack = customError.stack
var newline = stack.indexOf("\n") + 1
stack = stack.slice(newline)
customError.stack = stack
var split = stack.split(":")
if (split.length > 4) {
var a = split[3]
var b = split[4]
var t = b.slice(0, b.indexOf("\n"))
customError.lineNumber = Number(a)
customError.columnNumber = Number(t)
}
} else {
var stack = customError.stack
var split = stack.split("\n")
var secondPart = split.slice(2).join("\n")
stack = split[0] + "\n" + secondPart
customError.stack = stack
var split = secondPart.split(":")
var a = split[2]
var b = split[3]
}
throw customError
}
var frame = 0
function aFunction() {
if (++frame === 100) {
createError("LazyError", "function getting lazy", false, true)
} else {
requestAnimationFrame(aFunction)
}
}
setTimeout(aFunction, Math.random() * 500)
* {
font-family: Verdana;
}
Check your inspector!

Try a new prototype object for each instance of the user defined error type. It allows instanceof checks to behave as usual plus type and message are correctly reported in Firefox and V8 (Chome, nodejs).
function NotImplementedError(message){
if(NotImplementedError.innercall===undefined){
NotImplementedError.innercall = true;
NotImplementedError.prototype = new Error(message);
NotImplementedError.prototype.name = "NotImplementedError";
NotImplementedError.prototype.constructor = NotImplementedError;
return new NotImplementedError(message);
}
delete NotImplementedError.innercall;
}
Note that an additional entry will preceed the otherwise correct stack.

This is fastest way to do it:
let thisVar = false
if (thisVar === false) {
throw new Error("thisVar is false. It should be true.")
}

easier way. You could make your object inherit from the Error object.
Example:
function NotImplementError(message)
{
this.message = message;
Error.call();
Error.call(message);
}
what we are doing is using the function call() which call the constructor of the Error class so is basicly the same thing as implementing a class inheritance in other object oriented languages.

MDN has an excellent example:
try {
throw new Error('Whoops!');
} catch (e) {
console.log(e.name + ': ' + e.message);
}

Related

Invalid-key request passed to a map to retrieve the value. Handle this exception

I have a map, and am trying to retrieve a value based on the key passed in the form of "id". However in some cases, the "id" that is passed is invalid i.e. it is not present in the Map, which in turn makes the application crash. Is throwing the Error causing this issue ? I tried experimenting with the try-catch but it still continued to malfunction i.e. the screen stops loading on detecting this error. Not sure if I wrote the try-catch correctly. how do I best handle this to stop the application from malfunctioning in such a case and continue loading the screen.
Failed approach:
this.hmItems = {}; //Map<Integer,Item>
Address.prototype.getItem = function (id) {
var item = this.hmItems[id];
if (!item)
throw new Error("'Illegal argument: id=" + id);
return item;
};
Another failed approach:
this.hmItems = {}; //Map<Integer,Item>
Address.prototype.getItem = function (id) {
try {
var item = this.hmItems[id];
if (!item) throw "illegal id!!!!!!!!!";
return item;
} catch(err) {
//log exception
}
}
Use hasOwnProperty to see if the property exists on the hmItems object:
this.hmItems = {}; //Map<Integer,Item>
Address.prototype.getItem = function(id) {
if (!this.hmItems.hasOwnProperty(id)) {
throw new Error("'Illegal argument: id=" + id);
}
return this.hmItems[id];
};
Just checking var item = this.hmItems[id]; if (!item) isn't a good test because it'll fail even if the property exists on the object but is falsey, like 0 or null.
Live demo:
class Address {
hmItems = {
1: 10,
2: 0
};
getItem(id) {
if (!this.hmItems.hasOwnProperty(id)) {
throw new Error("'Illegal argument: id=" + id);
}
return this.hmItems[id];
}
}
const a = new Address();
// Works:
console.log(a.getItem(1));
// Works:
console.log(a.getItem(2));
// Throws:
try {
console.log(a.getItem(3));
} catch(e) {
console.log(e.message);
}

TypeError: is not a function for the inheriting object function

I am just trying to understand prototypical inheritance using simple code.
function Place() {
}
Place.prototype.airportCode = function(code) {
console.log('Airport code is: ' + code);
}
function City(cityName) {
this.name = cityName;
}
City.prototype.railwayStateCode = function(code) {
console.log('Railway station code is: ' + code);
}
City.prototype = Object.create(Place.prototype);
const sydney = new City('Sydney');
const melbourne = new Place();
When I try to
sydney.railwayStateCode('SYD');
I get an error
TypeError: sydney.railwayStateCode is not a function
As per my understanding, it should not throw an error. Am I doing something wrong?
You overriding the prototype here:
City.prototype = Object.create(Place.prototype);
To make it work change the order like this:
City.prototype = Object.create(Place.prototype);
City.prototype.railwayStateCode = function(code) {
console.log('Railway station code is: ' + code);
}

Catching unhandled exceptions in Rhino

I'm using Rhino Script Engine and was wondering if it's possible (and how) to register a global handler that could be invoked whenever an unhandled exception is triggered.
I know that I cannot use browser objects like window to register a handler with something like:
window.addEventListener("error", function (e) {
alert("Error occurred: " + e.error.message);
return false;
})
Is there an alternative?
Depending exactly what you want -- and exactly what you have -- here's one approach:
var setUncaughtExceptionHandler = function(f) {
Packages.org.mozilla.javascript.Context.getCurrentContext().setErrorReporter(
new JavaAdapter(
Packages.org.mozilla.javascript.ErrorReporter,
new function() {
var handle = function(type) {
return function(message,sourceName,line,lineSource,lineOffset) {
f({
type: type,
message: String(message),
sourceName: String(sourceName),
line: line,
lineSource: String(lineSource),
lineOffset: lineOffset
});
};
};
["warning","error","runtimeError"].forEach(function(name) {
this[name] = handle(name);
},this);
}
)
);
};
setUncaughtExceptionHandler(function(error) {
Packages.java.lang.System.err.println("Caught exception: " + JSON.stringify(error,void(0)," "));
});
var x = true;
var y = null;
var z = y.foo;
The output for this is:
Caught exception: {
"type": "error",
"message": "uncaught JavaScript runtime exception: TypeError: Cannot read property \"foo\" from null",
"sourceName": "null",
"line": 0,
"lineSource": "null",
"lineOffset": 0
}

Javascript, why would "this" be undefined in a bound node callback?

I'm encountering this error for the first time and I'm at a loss of what could cause it.
AccountController.create = function () {
var password = this.param('password'),
confirmPassword = this.param('confirmPassword'),
account = new Account(),
errorMessage = "",
errors = "",
error,
errorCount;
if (password !== confirmPassword) {
this.req.flash('warning', 'passwords do not match');
return this.redirect(this.urlFor({action: 'index'}));
}
account.email = this.param('email');
account.set('password', password);
account.name.first = this.param('name.first');
account.name.last = this.param('name.last');
account.access = this.param('access');
account.save(function (err, account, numberAffected) {
if (err) {
console.log("THIS: " + this);
errorMessage = "";
errors = err.errors;
if (!errors) {
errors = [{message: "There is already an account with this email address."}];
}
errorCount = errors.length;
for (error = 0; error < errorCount; error = error + 1) {
if (typeof errors[error] === "string") {
errorMessage += errors[error].message + " ";
}
}
this.req.flash('warning', errorMessage);
return this.redirect(this.urlFor({action: 'index'}));
}
return this.redirect(this.urlFor({controller: "profile", action: 'login'}));
}).bind(this);
};
I'm hoping that there's no additional code required to make the example work.
Console output (running via nodemon) looks like this:
THIS: undefined
/Users/crispensmith/Documents/cinchedStore/Site/app/controllers/account_controller.js:68
this.req.flash('warning', errorMessage);
^
TypeError: Cannot read property 'req' of undefined
You're doing .bind(this) on the return value of account.save, not the function you're passing into it. Just move the bind inside the bracket;
account.save((function // ... possibly also use an extra parenthesis
// ...
}).bind(this)); // to make the bind target more obvious?

A proper wrapper for console.log with correct line number?

I'm now developing an application, and place a global isDebug switch. I would like to wrap console.log for more convenient usage.
//isDebug controls the entire site.
var isDebug = true;
//debug.js
function debug(msg, level){
var Global = this;
if(!(Global.isDebug && Global.console && Global.console.log)){
return;
}
level = level||'info';
Global.console.log(level + ': '+ msg);
}
//main.js
debug('Here is a msg.');
Then I get this result in Firefox console.
info: Here is a msg. debug.js (line 8)
What if I want to log with line number where debug() gets called, like info: Here is a msg. main.js (line 2)?
This is an old question and All the answers provided are overly hackey, have MAJOR cross browser issues, and don't provide anything super useful. This solution works in every browser and reports all console data exactly as it should. No hacks required and one line of code Check out the codepen.
var debug = console.log.bind(window.console)
Create the switch like this:
isDebug = true // toggle this to turn on / off for global controll
if (isDebug) var debug = console.log.bind(window.console)
else var debug = function(){}
Then simply call as follows:
debug('This is happening.')
You can even take over the console.log with a switch like this:
if (!isDebug) console.log = function(){}
If you want to do something useful with that.. You can add all the console methods and wrap it up in a reusable function that gives not only global control, but class level as well:
var Debugger = function(gState, klass) {
this.debug = {}
if (gState && klass.isDebug) {
for (var m in console)
if (typeof console[m] == 'function')
this.debug[m] = console[m].bind(window.console, klass.toString()+": ")
}else{
for (var m in console)
if (typeof console[m] == 'function')
this.debug[m] = function(){}
}
return this.debug
}
isDebug = true //global debug state
debug = Debugger(isDebug, this)
debug.log('Hello log!')
debug.trace('Hello trace!')
Now you can add it to your classes:
var MyClass = function() {
this.isDebug = true //local state
this.debug = Debugger(isDebug, this)
this.debug.warn('It works in classses')
}
You can maintain line numbers and output the log level with some clever use of Function.prototype.bind:
function setDebug(isDebug) {
if (window.isDebug) {
window.debug = window.console.log.bind(window.console, '%s: %s');
} else {
window.debug = function() {};
}
}
setDebug(true);
// ...
debug('level', 'This is my message.'); // --> level: This is my message. (line X)
Taking it a step further, you could make use of the console's error/warning/info distinctions and still have custom levels. Try it!
function setDebug(isDebug) {
if (isDebug) {
window.debug = {
log: window.console.log.bind(window.console, 'log: %s'),
error: window.console.error.bind(window.console, 'error: %s'),
info: window.console.info.bind(window.console, 'info: %s'),
warn: window.console.warn.bind(window.console, 'warn: %s')
};
} else {
var __no_op = function() {};
window.debug = {
log: __no_op,
error: __no_op,
warn: __no_op,
info: __no_op
}
}
}
setDebug(true);
// ...
debug.log('wat', 'Yay custom levels.'); // -> log: wat: Yay custom levels. (line X)
debug.info('This is info.'); // -> info: This is info. (line Y)
debug.error('Bad stuff happened.'); // -> error: Bad stuff happened. (line Z)
I liked #fredrik's answer, so I rolled it up with another answer which splits the Webkit stacktrace, and merged it with #PaulIrish's safe console.log wrapper. "Standardizes" the filename:line to a "special object" so it stands out and looks mostly the same in FF and Chrome.
Testing in fiddle: http://jsfiddle.net/drzaus/pWe6W/
_log = (function (undefined) {
var Log = Error; // does this do anything? proper inheritance...?
Log.prototype.write = function (args) {
/// <summary>
/// Paulirish-like console.log wrapper. Includes stack trace via #fredrik SO suggestion (see remarks for sources).
/// </summary>
/// <param name="args" type="Array">list of details to log, as provided by `arguments`</param>
/// <remarks>Includes line numbers by calling Error object -- see
/// * http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
/// * https://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number
/// * https://stackoverflow.com/a/3806596/1037948
/// </remarks>
// via #fredrik SO trace suggestion; wrapping in special construct so it stands out
var suffix = {
"#": (this.lineNumber
? this.fileName + ':' + this.lineNumber + ":1" // add arbitrary column value for chrome linking
: extractLineNumberFromStack(this.stack)
)
};
args = args.concat([suffix]);
// via #paulirish console wrapper
if (console && console.log) {
if (console.log.apply) { console.log.apply(console, args); } else { console.log(args); } // nicer display in some browsers
}
};
var extractLineNumberFromStack = function (stack) {
/// <summary>
/// Get the line/filename detail from a Webkit stack trace. See https://stackoverflow.com/a/3806596/1037948
/// </summary>
/// <param name="stack" type="String">the stack string</param>
if(!stack) return '?'; // fix undefined issue reported by #sigod
// correct line number according to how Log().write implemented
var line = stack.split('\n')[2];
// fix for various display text
line = (line.indexOf(' (') >= 0
? line.split(' (')[1].substring(0, line.length - 1)
: line.split('at ')[1]
);
return line;
};
return function (params) {
/// <summary>
/// Paulirish-like console.log wrapper
/// </summary>
/// <param name="params" type="[...]">list your logging parameters</param>
// only if explicitly true somewhere
if (typeof DEBUGMODE === typeof undefined || !DEBUGMODE) return;
// call handler extension which provides stack trace
Log().write(Array.prototype.slice.call(arguments, 0)); // turn into proper array
};//-- fn returned
})();//--- _log
This also works in node, and you can test it with:
// no debug mode
_log('this should not appear');
// turn it on
DEBUGMODE = true;
_log('you should', 'see this', {a:1, b:2, c:3});
console.log('--- regular log ---');
_log('you should', 'also see this', {a:4, b:8, c:16});
// turn it off
DEBUGMODE = false;
_log('disabled, should not appear');
console.log('--- regular log2 ---');
I found a simple solution to combine the accepted answer (binding to console.log/error/etc) with some outside logic to filter what is actually logged.
// or window.log = {...}
var log = {
ASSERT: 1, ERROR: 2, WARN: 3, INFO: 4, DEBUG: 5, VERBOSE: 6,
set level(level) {
if (level >= this.ASSERT) this.a = console.assert.bind(window.console);
else this.a = function() {};
if (level >= this.ERROR) this.e = console.error.bind(window.console);
else this.e = function() {};
if (level >= this.WARN) this.w = console.warn.bind(window.console);
else this.w = function() {};
if (level >= this.INFO) this.i = console.info.bind(window.console);
else this.i = function() {};
if (level >= this.DEBUG) this.d = console.debug.bind(window.console);
else this.d = function() {};
if (level >= this.VERBOSE) this.v = console.log.bind(window.console);
else this.v = function() {};
this.loggingLevel = level;
},
get level() { return this.loggingLevel; }
};
log.level = log.DEBUG;
Usage:
log.e('Error doing the thing!', e); // console.error
log.w('Bonus feature failed to load.'); // console.warn
log.i('Signed in.'); // console.info
log.d('Is this working as expected?'); // console.debug
log.v('Old debug messages, output dominating messages'); // console.log; ignored because `log.level` is set to `DEBUG`
log.a(someVar == 2) // console.assert
Note that console.assert uses conditional logging.
Make sure your browser's dev tools shows all message levels!
Listen McFly, this was the only thing that worked for me:
let debug = true;
Object.defineProperty(this, "log", {get: function () {
return debug ? console.log.bind(window.console, '['+Date.now()+']', '[DEBUG]')
: function(){};}
});
// usage:
log('Back to the future');
// outputs:
[1624398754679] [DEBUG] Back to the future
The beauty is to avoid another function call like log('xyz')()
or to create a wrapper object or even class. Its also ES5 safe.
If you don't want a prefix, just delete the param.
update included current timestamp to prefix every log output.
Chrome Devtools lets you achieve this with Blackboxing. You can create console.log wrapper that can have side effects, call other functions, etc, and still retain the line number that called the wrapper function.
Just put a small console.log wrapper into a separate file, e.g.
(function() {
var consolelog = console.log
console.log = function() {
// you may do something with side effects here.
// log to a remote server, whatever you want. here
// for example we append the log message to the DOM
var p = document.createElement('p')
var args = Array.prototype.slice.apply(arguments)
p.innerText = JSON.stringify(args)
document.body.appendChild(p)
// call the original console.log function
consolelog.apply(console,arguments)
}
})()
Name it something like log-blackbox.js
Then go to Chrome Devtools settings and find the section "Blackboxing", add a pattern for the filename you want to blackbox, in this case log-blackbox.js
From: How to get JavaScript caller function line number? How to get JavaScript caller source URL?
the Error object has a line number property(in FF). So something like this should work:
var err = new Error();
Global.console.log(level + ': '+ msg + 'file: ' + err.fileName + ' line:' + err.lineNumber);
In Webkit browser you have err.stack that is a string representing the current call stack. It will display the current line number and more information.
UPDATE
To get the correct linenumber you need to invoke the error on that line. Something like:
var Log = Error;
Log.prototype.write = function () {
var args = Array.prototype.slice.call(arguments, 0),
suffix = this.lineNumber ? 'line: ' + this.lineNumber : 'stack: ' + this.stack;
console.log.apply(console, args.concat([suffix]));
};
var a = Log().write('monkey' + 1, 'test: ' + 2);
var b = Log().write('hello' + 3, 'test: ' + 4);
A way to keep line number is here: https://gist.github.com/bgrins/5108712. It more or less boils down to this:
if (Function.prototype.bind) {
window.log = Function.prototype.bind.call(console.log, console);
}
else {
window.log = function() {
Function.prototype.apply.call(console.log, console, arguments);
};
}
You could wrap this with isDebug and set window.log to function() { } if you aren't debugging.
You can pass the line number to your debug method, like this :
//main.js
debug('Here is a msg.', (new Error).lineNumber);
Here, (new Error).lineNumber would give you the current line number in your javascript code.
If you simply want to control whether debug is used and have the correct line number, you can do this instead:
if(isDebug && window.console && console.log && console.warn && console.error){
window.debug = {
'log': window.console.log,
'warn': window.console.warn,
'error': window.console.error
};
}else{
window.debug = {
'log': function(){},
'warn': function(){},
'error': function(){}
};
}
When you need access to debug, you can do this:
debug.log("log");
debug.warn("warn");
debug.error("error");
If isDebug == true, The line numbers and filenames shown in the console will be correct, because debug.log etc is actually an alias of console.log etc.
If isDebug == false, no debug messages are shown, because debug.log etc simply does nothing (an empty function).
As you already know, a wrapper function will mess up the line numbers and filenames, so it's a good idea to prevent using wrapper functions.
Stack trace solutions display the line number but do not allow to click to go to source, which is a major problem. The only solution to keep this behaviour is to bind to the original function.
Binding prevents to include intermediate logic, because this logic would mess with line numbers. However, by redefining bound functions and playing with console string substitution, some additional behaviour is still possible.
This gist shows a minimalistic logging framework that offers modules, log levels, formatting, and proper clickable line numbers in 34 lines. Use it as a basis or inspiration for your own needs.
var log = Logger.get("module").level(Logger.WARN);
log.error("An error has occured", errorObject);
log("Always show this.");
EDIT: gist included below
/*
* Copyright 2016, Matthieu Dumas
* This work is licensed under the Creative Commons Attribution 4.0 International License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/
*/
/* Usage :
* var log = Logger.get("myModule") // .level(Logger.ALL) implicit
* log.info("always a string as first argument", then, other, stuff)
* log.level(Logger.WARN) // or ALL, DEBUG, INFO, WARN, ERROR, OFF
* log.debug("does not show")
* log("but this does because direct call on logger is not filtered by level")
*/
var Logger = (function() {
var levels = {
ALL:100,
DEBUG:100,
INFO:200,
WARN:300,
ERROR:400,
OFF:500
};
var loggerCache = {};
var cons = window.console;
var noop = function() {};
var level = function(level) {
this.error = level<=levels.ERROR ? cons.error.bind(cons, "["+this.id+"] - ERROR - %s") : noop;
this.warn = level<=levels.WARN ? cons.warn.bind(cons, "["+this.id+"] - WARN - %s") : noop;
this.info = level<=levels.INFO ? cons.info.bind(cons, "["+this.id+"] - INFO - %s") : noop;
this.debug = level<=levels.DEBUG ? cons.log.bind(cons, "["+this.id+"] - DEBUG - %s") : noop;
this.log = cons.log.bind(cons, "["+this.id+"] %s");
return this;
};
levels.get = function(id) {
var res = loggerCache[id];
if (!res) {
var ctx = {id:id,level:level}; // create a context
ctx.level(Logger.ALL); // apply level
res = ctx.log; // extract the log function, copy context to it and returns it
for (var prop in ctx)
res[prop] = ctx[prop];
loggerCache[id] = res;
}
return res;
};
return levels; // return levels augmented with "get"
})();
Here's a way to keep your existing console logging statements while adding a file name and line number or other stack trace info onto the output:
(function () {
'use strict';
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isChrome = !!window.chrome && !!window.chrome.webstore;
var isIE = /*#cc_on!#*/false || !!document.documentMode;
var isEdge = !isIE && !!window.StyleMedia;
var isPhantom = (/PhantomJS/).test(navigator.userAgent);
Object.defineProperties(console, ['log', 'info', 'warn', 'error'].reduce(function (props, method) {
var _consoleMethod = console[method].bind(console);
props[method] = {
value: function MyError () {
var stackPos = isOpera || isChrome ? 2 : 1;
var err = new Error();
if (isIE || isEdge || isPhantom) { // Untested in Edge
try { // Stack not yet defined until thrown per https://learn.microsoft.com/en-us/scripting/javascript/reference/stack-property-error-javascript
throw err;
} catch (e) {
err = e;
}
stackPos = isPhantom ? 1 : 2;
}
var a = arguments;
if (err.stack) {
var st = err.stack.split('\n')[stackPos]; // We could utilize the whole stack after the 0th index
var argEnd = a.length - 1;
[].slice.call(a).reverse().some(function(arg, i) {
var pos = argEnd - i;
if (typeof a[pos] !== 'string') {
return false;
}
if (typeof a[0] === 'string' && a[0].indexOf('%') > -1) { pos = 0 } // If formatting
a[pos] += ' \u00a0 (' + st.slice(0, st.lastIndexOf(':')) // Strip out character count
.slice(st.lastIndexOf('/') + 1) + ')'; // Leave only path and line (which also avoids ":" changing Safari console formatting)
return true;
});
}
return _consoleMethod.apply(null, a);
}
};
return props;
}, {}));
}());
Then use it like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="console-log.js"></script>
</head>
<body>
<script>
function a () {
console.log('xyz'); // xyz (console-log.html:10)
}
console.info('abc'); // abc (console-log.html:12)
console.log('%cdef', "color:red;"); // (IN RED:) // def (console-log.html:13)
a();
console.warn('uuu'); // uuu (console-log.html:15)
console.error('yyy'); // yyy (console-log.html:16)
</script>
</body>
</html>
This works in Firefox, Opera, Safari, Chrome, and IE 10 (not yet tested on IE11 or Edge).
The idea with bind Function.prototype.bind is brilliant. You can also use npm library lines-logger. It shows origin source files:
Create logger anyone once in your project:
var LoggerFactory = require('lines-logger').LoggerFactory;
var loggerFactory = new LoggerFactory();
var logger = loggerFactory.getLoggerColor('global', '#753e01');
Print logs:
logger.log('Hello world!')();
With modern javascript and the use of getters, you could write something like this:
window.Logger = {
debugMode: true,
get info() {
if ( window.Logger.debugMode ) {
return window.console.info.bind( window.console );
} else {
return () => {};
}
}
}
The nice part about it is that you can have both static and computed values printed out, together with correct line numbers. You could even define multiple logger with different settings:
class LoggerClz {
name = null;
debugMode = true;
constructor( name ) { this.name = name; }
get info() {
if ( this.debugMode ) {
return window.console.info.bind( window.console, 'INFO', new Date().getTime(), this.name );
} else {
return () => {};
}
}
}
const Logger1 = new LoggerClz( 'foo' );
const Logger2 = new LoggerClz( 'bar' );
function test() {
Logger1.info( '123' ); // INFO 1644750929128 foo 123 [script.js:18]
Logger2.info( '456' ); // INFO 1644750929128 bar 456 [script.js:19]
}
test();
A little variation is to to have debug() return a function, which is then executed where you need it - debug(message)(); and so properly shows the correct line number and calling script in the console window, while allowing for variations like redirecting as an alert, or saving to file.
var debugmode='console';
var debugloglevel=3;
function debug(msg, type, level) {
if(level && level>=debugloglevel) {
return(function() {});
}
switch(debugmode) {
case 'alert':
return(alert.bind(window, type+": "+msg));
break;
case 'console':
return(console.log.bind(window.console, type+": "+msg));
break;
default:
return (function() {});
}
}
Since it returns a function, that function needs to be executed at the debug line with ();. Secondly, the message is sent to the debug function, rather than into the returned function allowing pre-processing or checking that you might need, such as checking log-level state, making the message more readable, skipping different types, or only reporting items meeting the log level criteria;
debug(message, "serious", 1)();
debug(message, "minor", 4)();
You can use optional chaining to really simplify this. You get full access to the console object with no hacks and concise syntax.
const debug = (true) ? console : null;
debug?.log('test');
debug?.warn('test');
debug?.error('test')
If debug == null, everything after the ? is ignored with no error thrown about inaccessible properties.
const debug = (false) ? console : null;
debug?.error('not this time');
This also lets you use the debug object directly as a conditional for other debug related processes besides logging.
const debug = (true) ? console : null;
let test = false;
function doSomething() {
test = true;
debug?.log('did something');
}
debug && doSomething();
if (debug && test == false) {
debug?.warn('uh-oh');
} else {
debug?.info('perfect');
}
if (!debug) {
// set up production
}
If you want, you can override the various methods with a no-op based on your desired log level.
const debug = (true) ? console : null;
const quiet = true; const noop = ()=>{};
if (debug && quiet) {
debug.info = noop;
debug.warn = noop;
}
debug?.log('test');
debug?.info('ignored in quiet mode');
debug?.warn('me too');
//isDebug controls the entire site.
var isDebug = true;
//debug.js
function debug(msg, level){
var Global = this;
if(!(Global.isDebug && Global.console && Global.console.log)){
return;
}
level = level||'info';
return 'console.log(\'' + level + ': '+ JSON.stringify(msg) + '\')';
}
//main.js
eval(debug('Here is a msg.'));
This will give me info: "Here is a msg." main.js(line:2).
But the extra eval is needed, pity.
I have been looking at this issue myself lately. Needed something very straight forward to control logging, but also to retain line numbers. My solution is not looking as elegant in code, but provides what is needed for me. If one is careful enough with closures and retaining.
I've added a small wrapper to the beginning of the application:
window.log = {
log_level: 5,
d: function (level, cb) {
if (level < this.log_level) {
cb();
}
}
};
So that later I can simply do:
log.d(3, function(){console.log("file loaded: utils.js");});
I've tested it of firefox and crome, and both browsers seem to show console log as intended. If you fill like that, you can always extend the 'd' method and pass other parameters to it, so that it can do some extra logging.
Haven't found any serious drawbacks for my approach yet, except the ugly line in code for logging.
This implementation is based on the selected answer and helps reduce the amount of noise in the error console: https://stackoverflow.com/a/32928812/516126
var Logging = Logging || {};
const LOG_LEVEL_ERROR = 0,
LOG_LEVEL_WARNING = 1,
LOG_LEVEL_INFO = 2,
LOG_LEVEL_DEBUG = 3;
Logging.setLogLevel = function (level) {
const NOOP = function () { }
Logging.logLevel = level;
Logging.debug = (Logging.logLevel >= LOG_LEVEL_DEBUG) ? console.log.bind(window.console) : NOOP;
Logging.info = (Logging.logLevel >= LOG_LEVEL_INFO) ? console.log.bind(window.console) : NOOP;
Logging.warning = (Logging.logLevel >= LOG_LEVEL_WARNING) ? console.log.bind(window.console) : NOOP;
Logging.error = (Logging.logLevel >= LOG_LEVEL_ERROR) ? console.log.bind(window.console) : NOOP;
}
Logging.setLogLevel(LOG_LEVEL_INFO);
Here's my logger function (based on some of the answers). Hope someone can make use of it:
const DEBUG = true;
let log = function ( lvl, msg, fun ) {};
if ( DEBUG === true ) {
log = function ( lvl, msg, fun ) {
const d = new Date();
const timestamp = '[' + d.getHours() + ':' + d.getMinutes() + ':' +
d.getSeconds() + '.' + d.getMilliseconds() + ']';
let stackEntry = new Error().stack.split( '\n' )[2];
if ( stackEntry === 'undefined' || stackEntry === null ) {
stackEntry = new Error().stack.split( '\n' )[1];
}
if ( typeof fun === 'undefined' || fun === null ) {
fun = stackEntry.substring( stackEntry.indexOf( 'at' ) + 3,
stackEntry.lastIndexOf( ' ' ) );
if ( fun === 'undefined' || fun === null || fun.length <= 1 ) {
fun = 'anonymous';
}
}
const idx = stackEntry.lastIndexOf( '/' );
let file;
if ( idx !== -1 ) {
file = stackEntry.substring( idx + 1, stackEntry.length - 1 );
} else {
file = stackEntry.substring( stackEntry.lastIndexOf( '\\' ) + 1,
stackEntry.length - 1 );
}
if ( file === 'undefined' || file === null ) {
file = '<>';
}
const m = timestamp + ' ' + file + '::' + fun + '(): ' + msg;
switch ( lvl ) {
case 'log': console.log( m ); break;
case 'debug': console.log( m ); break;
case 'info': console.info( m ); break;
case 'warn': console.warn( m ); break;
case 'err': console.error( m ); break;
default: console.log( m ); break;
}
};
}
Examples:
log( 'warn', 'log message', 'my_function' );
log( 'info', 'log message' );
For Angular / Typescript console logger with correct line number you can do the following:
example file: console-logger.ts
export class Log {
static green(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #31A821`);
}
static red(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #DA5555`);
}
static blue(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #5560DA`);
}
static purple(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #A955DA`);
}
static yellow(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #EFEC47`);
}
}
Then call it from any other file:
example file: auth.service.ts
import { Log } from 'path to console-logger.ts';
const user = { user: '123' }; // mock data
Log.green('EXAMPLE')();
Log.red('EXAMPLE')(user);
Log.blue('EXAMPLE')(user);
Log.purple('EXAMPLE')(user);
Log.yellow('EXAMPLE')(user);
It will look like this in the console:
Stackblitz example
I found some of the answers to this problem a little too complex for my needs. Here is a simple solution, rendered in Coffeescript. It'a adapted from Brian Grinstead's version here
It assumes the global console object.
# exposes a global 'log' function that preserves line numbering and formatting.
(() ->
methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeStamp', 'trace', 'warn']
noop = () ->
# stub undefined methods.
for m in methods when !console[m]
console[m] = noop
if Function.prototype.bind?
window.log = Function.prototype.bind.call(console.log, console);
else
window.log = () ->
Function.prototype.apply.call(console.log, console, arguments)
)()
Code from http://www.briangrinstead.com/blog/console-log-helper-function:
// Full version of `log` that:
// * Prevents errors on console methods when no console present.
// * Exposes a global 'log' function that preserves line numbering and formatting.
(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;
}
}
if (Function.prototype.bind) {
window.log = Function.prototype.bind.call(console.log, console);
}
else {
window.log = function() {
Function.prototype.apply.call(console.log, console, arguments);
};
}
})();
var a = {b:1};
var d = "test";
log(a, d);
window.line = function () {
var error = new Error(''),
brower = {
ie: !-[1,], // !!window.ActiveXObject || "ActiveXObject" in window
opera: ~window.navigator.userAgent.indexOf("Opera"),
firefox: ~window.navigator.userAgent.indexOf("Firefox"),
chrome: ~window.navigator.userAgent.indexOf("Chrome"),
safari: ~window.navigator.userAgent.indexOf("Safari"), // /^((?!chrome).)*safari/i.test(navigator.userAgent)?
},
todo = function () {
// TODO:
console.error('a new island was found, please told the line()\'s author(roastwind)');
},
line = (function(error, origin){
// line, column, sourceURL
if(error.stack){
var line,
baseStr = '',
stacks = error.stack.split('\n');
stackLength = stacks.length,
isSupport = false;
// mac版本chrome(55.0.2883.95 (64-bit))
if(stackLength == 11 || brower.chrome){
line = stacks[3];
isSupport = true;
// mac版本safari(10.0.1 (12602.2.14.0.7))
}else if(brower.safari){
line = stacks[2];
isSupport = true;
}else{
todo();
}
if(isSupport){
line = ~line.indexOf(origin) ? line.replace(origin, '') : line;
line = ~line.indexOf('/') ? line.substring(line.indexOf('/')+1, line.lastIndexOf(':')) : line;
}
return line;
}else{
todo();
}
return '😭';
})(error, window.location.origin);
return line;
}
window.log = function () {
var _line = window.line.apply(arguments.callee.caller),
args = Array.prototype.slice.call(arguments, 0).concat(['\t\t\t#'+_line]);
window.console.log.apply(window.console, args);
}
log('hello');
here was my solution about this question. when you call the method: log, it will print the line number where you print your log
The way I solved it was to create an object, then create a new property on the object using Object.defineProperty() and return the console property, which was then used as the normal function, but now with the extended abilty.
var c = {};
var debugMode = true;
var createConsoleFunction = function(property) {
Object.defineProperty(c, property, {
get: function() {
if(debugMode)
return console[property];
else
return function() {};
}
});
};
Then, to define a property you just do...
createConsoleFunction("warn");
createConsoleFunction("log");
createConsoleFunction("trace");
createConsoleFunction("clear");
createConsoleFunction("error");
createConsoleFunction("info");
And now you can use your function just like
c.error("Error!");
Based on other answers (mainly #arctelix one) I created this for Node ES6, but a quick test showed good results in the browser as well. I'm just passing the other function as a reference.
let debug = () => {};
if (process.argv.includes('-v')) {
debug = console.log;
// debug = console; // For full object access
}
Nothing here really had what I needed so I add my own approach: Overriding the console and reading the original error line from a synthetic Error. The example stores the console warns and errors to console.appTrace, having errors very detailed and verbose; in such way that simple (console.appTrace.join("") tells me all I need from the user session.
Note that this works as of now only on Chrome
(function () {
window.console.appTrace = [];
const defaultError = console.error;
const timestamp = () => {
let ts = new Date(), pad = "000", ms = ts.getMilliseconds().toString();
return ts.toLocaleTimeString("cs-CZ") + "." + pad.substring(0, pad.length - ms.length) + ms + " ";
};
window.console.error = function () {
window.console.appTrace.push("ERROR ",
(new Error().stack.split("at ")[1]).trim(), " ",
timestamp(), ...arguments, "\n");
defaultError.apply(window.console, arguments);
};
const defaultWarn = console.warn;
window.console.warn = function () {
window.console.appTrace.push("WARN ", ...arguments, "\n");
defaultWarn.apply(window.console, arguments);
};
})();
inspired by Get name and line of calling function in node.js and date formatting approach in this thread.
This is what worked for me. It creates a new object with all the functionality of the original console object and retains the console object.
let debugOn=true; // we can set this from the console or from a query parameter, for example (&debugOn=true)
const noop = () => { }; // dummy function.
const debug = Object.create(console); // creates a deep copy of the console object. This retains the console object so we can use it when we need to and our new debug object has all the properties and methods of the console object.
let quiet = false;
debug.log = (debugOn) ? debug.log : noop;
if (debugOn&&quiet) {
debug.info = noop;
debug.warn = noop;
debug.assert = noop;
debug.error = noop;
debug.debug = noop;
}
console.log(`we should see this with debug off or on`);
debug.log(`we should not see this with debugOn=false`);
All the solutions here dance around the real problem -- the debugger should be able to ignore part of the stack to give meaningful lines. VSCode's js debugger can now do this. At the time of this edit, the feature is available via the nightly build of the js-debug extension. See the link in the following paragraph.
I proposed a feature for VSCode's debugger here that ignores the top of the stack that resides in any of the skipFiles file paths of the launch configuration. The property is in the launch.json config of your vscode workspace. So you can create a file/module responsible for wrapping console.log, add it to the skipFiles and the debugger will show the line that called into your skipped file rather than console.log itself.
It is in the nightly build of the js-debug extension. It looks like it could be in the next minor release of visual studio code. I've verified it works using the nightly build. No more hacky workarounds, yay!
let debug = console.log.bind(console);
let error = console.error.bind(console);
debug('debug msg');
error('more important message');
Reading for noobs:
bind (function): https://www.w3schools.com/js/js_function_bind.asp
chrome console (object): https://developer.mozilla.org/en-US/docs/Web/API/console
function: https://www.w3schools.com/js/js_functions.asp
object: https://www.w3schools.com/js/js_objects.asp

Categories

Resources