How is it possible to add a global exception / error handler in Meteor.js. I looked through so much code, but noone explains a global method... Every one just do a try catch around every Meteor.call or add a async callback to it. But I'm lazy I just want one piece of code that handles all my Meteor.Errors. Mostly it's already a client readable error. So I just have to show it to him.
I tryed to use:
$(window).error(function(error) {
const errorText = T9n.get("Exception." + error.originalEvent.error.error);
View.toast(errorText);
});
but its just working for normal javascript errors not for Metero.Error.. seems to be that Meteor catches it, before I can catch it there.
I don't know if this is exactly what you need but, here is a solution.
In every Meteor.call(), have it like this:
Meteor.call('contactForm', arg1, arg2, function(err, res){
handleError(err, res);
});
Define a reusable error handler in your client side:
handleError = function (err, res){
if(err){
//do something with the error sent from server. not the ugly alert() like this code.
alert('error!')
} else{
//do something if no errors.
alert('done!')
}
}
Of course, you have to define your errors and results in your methods but this solves writing the same things over and over again in client side. You can also define helpers like handleError above in your server side. For example, I have checkUser() in my server like this:
checkUser = function (){
if(!Meteor.user()){
throw new Meteor.Error(400, 'You are not a unicorn yet! I mean, user.')
}
}
and in methods I just write checkUser(); to use it.
EDIT:
Those global functions are not inside any other code block. Just have them standalone
Related
I Have a service that uses wrapper class for Winston logger to log the HTTP calls, internal failers, and so on to be able to maintain the service on production
the behavior of logging is something like this
function getUser(req, res, next) {
try {
logger.info("user::api::getUser", "controller called");
// do some stuff here, call the service related and return response
} catch(err) {
logger.error("user::api::getUser", err);
}
}
and also this logger is called in all subsequent functions along with the app, such as services, db access layers, controllers, middlewares, and so on
Actually, I am disappointed about calling logger in each function I write which I feel like polluting the code and don't make single responsibility principle also makes the code violates closed to modification principle` since if I updated the logger API for example, then I have to go through all the functions that the logger used inside and update it to the new syntax which is not good
I thought about if I can use event-driven, I mean event emitters to emit events on each failure, call and the handlers of these events will write the appropriate log
for example something like this
function getUser(req, res, next) {
try {
eventEmitter("info", "controller called");
// do some stuff here, call the service related and return response
} catch(err) {
eventEmitter.emit("error", err);
}
}
and here is the listener
eventEmitter.on("error", (err) => {
logger.error("error", err);
});
I see this syntax hides the implementation of calling the logger into one function instead of calling it in each part of the app, but still, the functions are polluted because I still have to call the event emitter inside them to emit logging event
I don't know if there is an intelligent way to implement logging without calling the logger in each part of the app like this !!
Also, I am wondering how giant companies handle these cases in their applications
I hope someone respond to me and guide me to the right direction
here is the code
function xyz() {
try {
var a = someexecutions();
handlesuccess(a)
} catch (err) {
handleerror(err)
}
}
this kind of function written any many times in my codebase I want
function xyz() {
try {
var a = someexecutions();
handlesuccess(a)
} catch (err) {
console.log(`function xyz throw ${err} from file ${__fileName}`)
handleerror(err)
}
}
but writing this anywhere is very huge work since there are more than 100 functions so I think if I am able to overwrite the catch function then it will work for me somehow. So is there any way for doing this
No, there isn't. It's part of the language. What you can do:
Use your IDE to search & replace
Put your code inside handleerror function
Hundred instances are not that much, you can do it :)
This is not possible. If handleerror is a globally shared function you may be able to extract the error's context function name through the non-standard stack error property or arguments.callee.
Otherwise you could possibly use a regex-based find and replace or a parser to rewrite the code programmatically.
I'm getting frustrated with part of a Yeoman Generator I'm building. As it's my first, I have no doubt I'm missing something obvious, but here it goes.
Simply put, I'm trying to log a message, Do Things™ and then log another message only when those things have been done.
Here's the method:
repos: function () {
var self = this;
this.log(highlightColour('Pulling down the repositories'));
// Skeleton
this.remote('user', 'skeleton', 'master', function(err, remote) {
if (!err) {
remote.bulkDirectory('.', self.destinationRoot());
} else {
self.log('\n');
self.log(alertColour('Failed to pull down Skeleton'));
repoErr = true;
}
});
//
// Three more near identical remote() tasks
//
if (!repoErr) {
self.log(successColour('Success!'));
self.log('\n');
} else {
self.log(alertColour('One or more repositories failed to download!'));
}
},
Each of the individual remote() tasks are working fine, but I get both the first and last self.log() messages before the file copying happens. It seems trivial, but I simply want the success message to come after everything has been completed.
For example, in the terminal I see:
Pulling down the repositories
Success!
file copying results
It should be:
Pulling down the repositories
file copying results
Success!
I thought it could be something to do with using this.async() with done() at the end of each remote() task, and I tried that, but whenever I do, none of the code fires at all.
I've even tried breaking everything (including the messages) into separate methods, but still no luck.
Such a simple goal, but I'm out of ideas! I'd be grateful for your help!
EDIT: In case you're wondering, I know the messages are coming first because any alerts regarding file conflicts are coming after the messages :)
This is not an issue related to Yeoman. You have asynchronous code, but you're handling it as if it was synchronous.
In the example you posted here, just do the logging as part of this.remote callback:
repos: function () {
var self = this;
this.log(highlightColour('Pulling down the repositories'));
// Skeleton
this.remote('user', 'skeleton', 'master', function(err, remote) {
if (!err) {
remote.bulkDirectory('.', self.destinationRoot());
self.log(successColour('Success!'));
self.log('\n');
} else {
self.log('\n');
self.log(alertColour('Failed to pull down Skeleton'));
self.log(alertColour('One or more repositories failed to download!'));
}
});
},
Maybe your actual use case is more complex; in this case you can use a module like async (or any other alternative) to handle more complex async flow. Either way, Yeoman doesn't provide helpers to handle asynchronous code as this is the bread and butter of Node.js.
I'd like to mock the save() function of a Mongoose model. The function I want to test looks like this in a file called user.js:
var User = import('User.js')
post: function(req, res) {
var user = new User({
password : req.body.password,
email : req.body.email,
});
user.save( function(err) {
if (err) {
....
} else {
....
}
});
I tried to write a test that looks like this in another file called user_spec.js:
var Hander = require('user.js')
it('works properly', function() {
spyOn(User, 'save').andReturn(null)
Handler.post(req, res);
});
but that gives me the error:
save() method does not exist
I've done some more digging and it looks like the User model itself does not have the save() method, an instance does. This would mean I have to mock the constructor of User, but I'm having a lot of trouble with this. Other posts refer to a statement like:
spyOn(window, User)
to fix this, but in NodeJS, global (the window equivalent here), does not have User, since I import is as a variable. Is it possible to mock the constructor to give me something with a mocked save()? I've also taken a look at an npm module called rewire, but I was hoping I could do this without mocking and replacing the entire user module in my handler.
This does not solve the issue of mocking a local variable, but it will solve the issue of unit testing the creation of new documents.
When creating a new document, it is better to use Model.create(). This can be mocked effectively, and it is simply less code. The right way to handle this and test it would be:
var User = import('User.js')
post: function(req, res) {
User.create({
password : req.body.password,
email : req.body.email,
}, function(err) {
if (err) {
....
} else {
....
}
});
});
Corresponding test:
var Hander = require('user.js')
it('works properly', function() {
spyOn(User, 'create').andReturn(null)
Handler.post(req, res);
});
Hopefully this workaround will help other people getting frustrated with jasmine and mongoose unit testing.
You can only swap a function with a spy after the object is created. Hence this will work:
var user = new User(…);
spyOn(user, save).…;
doSomething();
where this will not:
spyOn(User, save).…
doSomething()
Of course you could change the function inside mongoose that creates the save function on the object… but you probably don't want to go there.
In a sane world, you would be able to do this.
spyOn(Model.prototype, 'save')
However, Mongoose tries to overload all their Model functions to work as node.js callbacks and Promises simultaneously. To do this, they manipulate the prototype in a way that is a little hard to predict without reading the actual Model code (https://github.com/Automattic/mongoose/blob/master/lib/model.js).
Here's an example that actually worked for me.
spyOn(Model.prototype, '$__save').and.callFake(function (options, callback) {
callback();
});
For the record, I am using Mongoose with Promises in the application code.
I want to know your opinions on a function I made, that wraps every method of an object, adding "try catch" handlers to log JavaScript errors on server side.
I don't want to use window.onerror, because of this security restriction, and my scripts are going to be hosted on a different domain on a CDN.
/*
* object: Object to be wrapped
* errorHandler: Instance of ErrorHandler Object
*/
function addLog(object, errorHandler) {
var name, method;
for (name in object) {
method = object[name];
if (typeof method === "function") {
object[name] = function(method, name) {
return function() {
try {
return method.apply(this, arguments);
} catch (ex) {
ex.message += "; method: '" + name + "'";
errorHandler.addToStack(ex.message);
throw ex;
}
};
}(method, name);
}
}
return object;
}
errorHandler.addToStack is a method on a custom object that asynchronously sends error reports to a server using Ajax messages.
I want to wrap every object instantiated on my application with this function, but I'm not sure if:
Is this a bad practice?
Does it has performance issues?
Are there's a better ways for doing this?
Thanks in advance!
I think that the best way is to avoid try catch statements by preventing errors to occur adding validations and checks, but if you really need to do it this way, I don't think it will have a great performance issue.
Here I made a jspref test to measure it, and the difference shows only in IE, but it's not very significant.
I don't know if the errorHandler method has performance problems or slows down your code, but if it is async, I guess it won't be a problem.
Unlike stricter codes like Java, Javascript doesn't require try/catch for most objects instanciated. It tends to simply just not work if something goes wrong, and doesn't explode or end on you.
Some parts do however end the function, so a better method would be to surround the code as a whole with a try/catch, so that it fails silently, and use the catch to recall the original code while sending off an error report.
you can still send log to server using a global try/catch because exception objects contains the information.
however error object is not standard among browsers, i think