One of the biggest things I miss about mobile development is remote crash reporting. It was absolutely essential. It was funny at the time because it seemed like such a revolutionary concept but then I realized that I should have been doing this for both languages on the server and in the browser. Now that I’m not doing as much mobile development I started looking for a similar exception aggregation system. I was pleased to find a language agnostic system called Sentry.
Great, so I found my crash reporting system but the next step was how I was going to implement it on a large scale. I quickly turned to the patterns that the most logging frameworks use in Java and Python. Native JavaScript’s console.log() was great, but didn’t offer the flexibility I’d want for a large scale app. I wanted the ability to have different log levels and display logs in the browser console when I was debugging things, but at the same time I wanted to report a crash if I ever invoked logger.fatal().
I did some searching for JavaScript logging frameworks and I found a port of Java’s log4j, log4javascript. I found this to be pretty awesome but I was skeptical of using it right away as I didn’t want something bloated that had a full kitchen sink. It does offer a lot, but there were a lot of features I was looking for, specifically the ability to create different logging adapters that would allow me to log to Sentry. One thing you have to be careful is that if you log too many fatal errors you could overload your Sentry server so you’ll have to load test and scale accordingly. Sentry does have configuration options for Redis queues etc. to handle large volumes of logs.
In order for JavaScript to log to Sentry, I’d have to use one of their clients, Raven-JS. Once I got that working standalone, I wrote an adapter for log4javascript that would allow me to log all fatal() and error() logs to Sentry.
If you’d like to take it a step further, you can create a wrapper that instantiates the logger and configures it for you.
com.restlessThinker.Logger = { getLogger: function (name) { var loggerName = name || null; var logger = log4javascript.getLogger(loggerName); var consoleAppender = new log4javascript.BrowserConsoleAppender(); var globalLoggingLayout = new log4javascript.PatternLayout('[%-5p] [%d{HH:mm:ss}] [%c] ::: %m%n'); var subHostName = com.restlessThinker.Util.subHostName(); consoleAppender.setThreshold((subHostName === 'prod') ? log4javascript.Level.ERROR : log4javascript.Level.DEBUG); consoleAppender.setLayout(globalLoggingLayout); logger.addAppender(consoleAppender); // this can be modified to fit dev/staging/prod if (subHostName !== 'prod') { var ravenAppender = new log4javascript.RavenAppender('somedevkey', 'mysentryurl.com/2'); ravenAppender.setThreshold(log4javascript.Level.ERROR); logger.addAppender(ravenAppender); } return logger; } }; |
You can generate multiple loggers in different classes/files.
var logger = com.restlessThinker.Logger.getLogger('newLogger'); logger.fatal('log this to sentry'); |
I hope this helps!