DEV Community

Cover image for Error recording - How to record errors in your application to debug later
Spyros Argalias for Programming Duck

Posted on • Updated on • Originally published at programmingduck.com

Error recording - How to record errors in your application to debug later

Recording errors is an important part of error handling. In short, when certain errors occur in programs, you want to know about it. This is particularly important with bugs.

You need to:

  1. know that they occurred
  2. have useful information about them so you can debug them later

One way to do this is to use an error monitoring or logging service. Some examples are New Relic and Splunk. These will automatically record any program errors and such.

In particular, logging is very useful. It records a lot of information about what's happening in your program. This can help a lot with debugging.

Alternatively, you can manually record information about errors.

How to manually record errors

The goal is to be able to view errors later. You can achieve that in any number of ways.

One way is to manually record errors in a database.

To do this, you can:

  1. prepare your database for recording errors
  2. set up some code to record errors to the database
  3. set up a global error handler to catch errors. (This error handler would call the code from step 2)
  4. set up an endpoint in the back end so the front end can record errors too. (This endpoint would call the code from step 2)

For example, you might use a MongoDB database with a collection for errors. Every time an error occurs in your application, add information about it to the collection. You can organise the information in any way you like. For example, you could organise by the type of error or exception, by the error message, or by the last code in the call stack for the error.

After setting that up, you could set up a global error handler. This error handler would be called on errors that occur in your application. In the handler, record the error in your database.

Different frameworks and "environments" provide different ways to set up global error handlers. For example, in the Unity game engine, you can use Application.logMessageReceived += Handler;. On the front end of a website, you can use window.addEventListener('error', handler);.

Finally, you can set up an endpoint so that the front end can record errors too. Then, the front end can make a network request with information about the error it encountered.

Here's an example call you could make from the front end:

function handleError(errorEvent) {
  const {error} = errorEvent;
  const data = {stack: error.stack, message: error.message, type: error.name};
  fetch('https://example.com/errors', {method: 'POST', body: JSON.stringify(data)});
}
window.addEventListener('error', handleError);
Enter fullscreen mode Exit fullscreen mode

The function handleError above is executed any time an error occurs. It creates an object with useful information about the error. Then, it sends a network request to the back end. The back end will then record the information about the error so it can be viewed later.

What information to record

You want as much useful information about the error as possible. This will help you debug it later.

The article .NET best practices on exceptions has some guidelines for this. Adapted for both error values and exceptions, they are to:

  • use the predefined error types in your programming language if they're relevant. Only create custom types if the predefined ones don't apply.
  • if you create custom error types:
    • they should usually be subclasses of the main error types (if you use an OOP language)
    • they can optionally have custom properties (if they would be useful)
  • use grammatically correct error messages. For example 'The file "foo.txt" could not be found.'.
  • include a localised string message in every error (if your application is localised)

Final notes

So that's it for this article. I hope that you found it useful.

As always, if any points were missed, or if you disagree with anything, or have any comments or feedback then please leave a comment below.

For the next steps, I recommend looking at the other articles in the error handling series.

Alright, thanks and see you next time.

Credits

Recording photo - Photo by Krists Luhaers on Unsplash

Discussion (11)

Collapse
lukeshiru profile image
LUKESHIRU

Ideally you should try to use some kind of auth for those requests so you can avoid spamming from the client-side like:

(function spam() {
    fetch("https://example.com/errors", {
        method: "POST",
        body: JSON.stringify({
            stack: "spam",
            message: "spamming",
            type: "spam"
        })
    }).then(spam);
})();
Enter fullscreen mode Exit fullscreen mode

You can also add some kind of limit on the logging system so you only receive certain amount of request from a given IP in a certain amount of time.

Cheers!

Collapse
sargalias profile image
Spyros Argalias Author

Thanks for mentioning those points. Rate limiters are a really good practice!

Would you mind clarifying on the first point a bit about auth? What kind of auth were you thinking of? For the user, for the app, etc.?

Collapse
lukeshiru profile image
LUKESHIRU

Mainly something like generating an auth key from the server and asking for that key when requests are made (a bearer token).

Thread Thread
sargalias profile image
Spyros Argalias Author

Thanks for the explanation :)

Thread Thread
sargalias profile image
Spyros Argalias Author

So I had to think about it a bit more, and I was hoping you could still clarify a few things :). If it's dragging on or asking for too many details feel free to ignore it though.

Would this auth solve the spamming issue? E.g. couldn't an attacker do the authentication request once to obtain and save the bearer token? Then continue spamming using that bearer token?

Or, if the bearer token is one-time use, couldn't the attacker repeat the entire thing (get the token, then submit an error)?

PS. For some reason, I couldn't reply to your latest comment. I had to reply to my own.

Thread Thread
lukeshiru profile image
LUKESHIRU

It depends on how you configure that token. You can make every token a one time use for this kind of requests, or you can use it as an identifier for a given user/device and if you receive several requests in a roll from the same token you can just block them until the token is renewed.
One thing to consider is that this will not make it impossible to be spammed, it will just make it harder, which sometimes is good enough. You still need to have some kind of request limiter in the back-end, either if you or don't use a token.

Thread Thread
sargalias profile image
Spyros Argalias Author

I see.

Yeah I can see some uses for that. Is it things like:

  • more coding effort required by the attacker (security through obscurity in a way?)
  • it helps reduce the frequency of the spamming. Before an attacker can spam, they need to wait to receive a new token. Then, when the token is "used up" after some requests, they need to request a new one and wait again.
  • you can potentially save the bearer token. Then, if the user turns out to be a spammer, you can delete all the relevant log inputs from them.

Let me know if I'm totally missing the point lol.

Cool tips though, thanks.

Thread Thread
lukeshiru profile image
LUKESHIRU

You completely got it. You could even limit not the actual POST request, but the GET for the token: Why would a user need hundreds of tokens in seconds?

Thread Thread
sargalias profile image
Spyros Argalias Author

👍

Collapse
tolgadevsec profile image
Tolga Ünlü

Thanks for this article series Spyros!
regarding the errors and the information that you can record, this can also be very useful in a security context as it can support you in identifying security issues or catch suspicious activity against your application :)
Anyone being interested in that might want to check out the OWASP Logging Cheatsheet on further information.

Collapse
sargalias profile image
Spyros Argalias Author

My pleasure :).

I agree. That page in particular is one of my main references whenever I have to deal with logging in an application.

In a best case scenario, the error recording described here is partnered with logging like what's described in the OWASP link.