National Park of Minnesota

    @firstfleet/fferrorhandler

    2.0.10 • Public • Published

    ffErrorHandler

    This is the firstfleetinc error handler library, responsible for logging exceptions to papertrail and slack for our nodejs applications.

    Also contains error handling middleware to be used with express in our api servers.

    Index

    Use

    1. Run

          npm install @firstfleet/fferrorhandler
      
    2. Import

      CommonJS

          const ffErrorHandler = require("@firstfleet/fferrorhandler");
      

      ES Module

          import ffErrorHandler from "@firstfleet/fferrorhandler";
      

    Required Environment Variables

    These environment variables are required in the ecosystem.config.js used by pm2 (our node process manager), or in the .launch.json/debugging setup when running locally for the error handler to work.

    1. SlackPosterURL - Our cloud hosted slack logger endpoint
    2. PAPERTRAIL_HOST - Papertrail host uri
    3. PAPERTRAIL_PORT - Papertrail host port
    4. PAPERTRAIL_PROGRAM - Application name used for logging
    5. PAPERTRAIL_HOSTNAME - Host machines name used for logging

    Optional

    1. NON_OPERATIONAL_SLACK_CHANNEL - Override default slack channel when using express middlewar

    Constants

    ffErrorHandler.slackChannels

    This property holds constants that map to slack channels. You can use this in your ffErrorHandler.logAndNotify call, to avoid mistyping the slack channel, or having to look them up.

    • DEV -> alerts_dev
    • CRITICAL -> alerts_critical
    • MOBILE -> alerts_mobile
    • NODEJOBS -> alerts_nodejobs
    • SQL -> alerts_sql
    • WARNING -> alerts_warning
    • WEB -> alerts_web
    • WORKFLOW -> alerts_workflow
    • MOBILECOM -> alerts_mobilecomm

    Methods

    ffErrorHandler.logAndNotify

    Logs the error to papertrail, and an optional slack channel. If you do not pass in a slack channel, it will not log to slack.

    Parameter Type Optional Description
    appName string false Application Name, please use process.env.PAPERTRAIL_PROGRAM
    methodName string false Name of the method the error is being logged from
    message string false The error message, similar errors need to use the same message, in order to be muted together
    messageDetail string false Additional error details, such as meta data, stack, etc
    slackChannel string true Optional slack channel, if you want your error to go to slack. Please use ffErrorHandler.slackChannels constants for this
    const {logAndNotify, slackChannels} = require("@firstfleet/fferrorhandler");
    const appName = process.env.PAPERTRAIL_PROGRAM;
    
    function doThing() {
        try {
            throw new Error("oops");
        } catch (error) {
            // This done does NOT got to slack
            logAndNotify(appName, "doThing", "My Error Message", "Extra error details, or maybe error.stack");
    
            // This one DOES go to slack
            logAndNotify(appName, "doThing", "My Error Message", "Extra error details, or maybe error.stack", slackChannels.WARNING);
        }
    }

    Express Middleware

    The express middleware error handler methods are designed to be used with node and express.They follow the express error handling setup, and can be added to your express app, routers, or routes and be triggered by calling next(error). They are designed to be used with custom error types in ffErrorHandler.errorTypes.

    Custom error types automatically combine any additional data provided with the error stack trace. So, when building additional data to be logged in the error type constructor, you do not need to provider the error stack trace.

    To learn more about how express handles errors please read this

    Error Types

    These custom error types work in sync with out custom express middleware error handlers.

    Import The Error Types

    const { errorTypes } = require("@firstfleet/fferrorhandler");

    Types

    Operational Error

    Sidebar - You most likely want to use HttpError rather than OperationalError. HttpError is a subset of OperationalError. You can use OperationalError directly, but it will send back a 500 to the client by default and no error message. HttpError will let you dictate the status code and the error message sent to the client.

    An operational, or expected error. These are errors that developers do not need to know about and are normally low priority. Use this error when you want to log an error to papertrail, but not to slack. This will also send back a 500 to the client.

    • Sends to slack: false
    • Sends to papertrail: true
    • Status code returned: 500
    • Message returned to client when NODE_ENV === production: Internal Server Error
    • Message returned to client when NODE_ENV === development: error.stack
    Parameter Type Optional Description
    message string false The error message, will not be sent to the client, but will be sent to papertrail
    additionalData string true Any additional data you want logged to papertrail, will be combined with the stacktrace of the error. Defaults to empty string.
    options {includeBody: boolean} true Additional options for the error constructor. includeBody is whether to include req.body on the error payload
        const { errorTypes } = require("@firstfleet/fferrorhandler");
    
        function handleRoute(req, res, next) {
            try {
                throw new errorTypes.OperationalError("My error message", "Additional error data");
            } catch (error) {
                // OperationalError will be passed onto express error handling.
                // See section on express middleware methods
                next(error);
            }
        }
        
    Non Operational Error

    Sidebar - You can also use a plain JS Error instead of a NonOperationalError, and it will mostly have the same result. However, regular Errors do not allow additional data to be passed in for logging.

    Non Optional Errors are unexpected errors, or errors the developers want to be notified of in a timely manner. These errors get sent to papertrail and the specified slack channel. This error will result in a status code of 500 sent back to client, as something has failed unexpectedly.

    Non Operational Errors by default get sent to alerts_warning. If you want to change the default channel at the application level, you can set process.env.NON_OPERATIONAL_SLACK_CHANNEL and it will use that as the default channel. If you want to change channel for a particular call (overriding either the alerts_warning default, or the process.env default), you can pass in a slack channel in the error constructor. These slack channels are also validated, and if an invalid channel id is passed in, it will fall back to alerts_warning. To make sure you don't pass in an invalid slack channel, please use the constants in slackChannels.

    • Sends to slack: true
    • Sends to papertrail: true
    • Status code returned: 500
    • Message returned to client when NODE_ENV === production: Internal Server Error
    • Message returned to client when NODE_ENV === development: error.stack
    Parameter Type Optional Description
    message string false The error message, will not be sent to the client, but will be sent to papertrail
    additionalData string true Any additional data you want logged to papertrail, will be combined with the stacktrace of the error
    slackChannel string true The slack channel to send the error to.
    options {includeBody: boolean} true Additional options for the error constructor. includeBody is whether to include req.body on the error payload
        const { errorTypes, slackChannels } = require("@firstfleet/fferrorhandler");
    
        function handleRoute(req, res, next) {
            try {
                throw new errorTypes.NonOperationalError("My error message", "Additional error data");
                // OR with slackChannel override
                throw new errorTypes.NonOperationalError("My error message", "Additional error data", slackChannels.WEB);
            } catch (error) {
                // NonOperationalError will be passed onto express error handling.
                // See section on express middleware methods
                next(error);
            }
        }
        

    Http Error

    Sidebar - This is most likely the error you want to use if you need a status code other than 500, and a message to be returned to the client. However, these errors do NOT get logged to slack, only to papertrail. They are good for expected http errors (400, 401, 404). Should most likely not be used for status code 500, as a 500 would indicate an unexpected error and NonOperationalError should be used instead. If you are using status code 500, but don't want the error to go to slack, you should probably use a different status code.

    An Http Error, it is a subset of Operational Error, and inherits some of its properties. This error will go to papertrail, and return the status code provided in the constructor, as well as the message and return them to the client.

    • Sends to slack: false
    • Sends to papertrail: true
    • Status code returned: Developer provided in error constructor
    • Message returned: Developer provided in error constructor
    Parameter Type Optional Description
    message string false The error message, will not be sent to the client, but will be sent to papertrail
    statusCode number false The status code you wish to return to the client
    additionalData string true Any additional data you want logged to papertrail, will be combined with the stacktrace of the error
    options {includeBody: boolean} true Additional options for the error constructor. includeBody is whether to include req.body on the error payload
        const { errorTypes } = require("@firstfleet/fferrorhandler");
    
        function handleRoute(req, res, next) {
            try {
                throw new errorTypes.HttpError("My error message", 400, "Additional error data");
            } catch (error) {
                // HttpError will be passed onto express error handling.
                // See section on express middleware methods
                next(error);
            }
        }

    Plain JS Error

    • Sends to slack: true
    • Sends to papertrail: true
    • Status code returned: 500
    • Message returned: Internal Server Error

    You can also send plain JS Errors to the error handling middleware, however, you are limited in your options as to what additional data can be included with the error.

    Currently, you can only set the includeBody option directly on the error, to include the req.body in the error payload.

    const myHandler = async function(req, res, next) {
       try {
            throw new Error("My error message");
       } catch (error) {
            error.includeBody = true;
            next(error);
       }
    }

    Express Middleware Methods

    These middleware methods are used to handle errors in nodejs express applications. There are two middleware methods, one is for logging errors, the other is responsible for responding with the error to the client. They are meant to always be used together, with the logger coming first in the stack, and the error handler coming last.

    Sidebar - You may be asking, if they have to be used together, why not just make them one middleware method. The reason is so that consumers of these methods can add their own error handling middleware if desired, and choose where in the middleware stack they want to place it, as well as for separation of concerns.

    Ideally, you would add the middleware methods to the express app, as app level middleware, but you can add them just to a router, or route if you need more fine grained control

    ffErrorHandler.expressLogErrors

    This is an express middleware that is responsible for logging errors to slack and papertrail (or potentially anywhere, but currently set up for slack and papertrail). All errors this method logs that go to slack will go to the alerts_web channel, will use the express route.path as the method name, and will combine any additional data provided in the constructor with the error stack trace.It uses the errorTypes to determine where and how the error should be logged (slack, papertrail or both).

    You can read more about this in the error types section but just as a reminder

    OperationalError gets logged to papertrail

    NonOperationalError gets logged to slack and papertrail. By default Non Operational errors are sent to alerts_warning. You can change the default slack channel at the application level by setting process.env.NON_OPERATIONAL_SLACK_CHANNEL. If you want to override the default channel (either the default alerts_warning, or the one set in process.env.NON_OPERATIONAL_SLACK_CHANNEL) in a particular call, you can pass in a slack channel into the error constructor.

    Slack channels are validated against the values in slackChannels if you pass in a channel that is not in the slackChannels constant, it will fall back to alerts_warning

    HttpError gets logged to papertrail

    expressLogErrors Is meant to be used in conjunction with expressHandleErrors. expressLogErrors, after logging the error based on the error type, will forward the error to the next express error handler by calling next(error). These two middlewares are separate to allow allow consumers to add their own error handling middleware between them if needed and to separate concerns.

    Sidebar - you can send plain JS Errors to the expressLogErrors middleware, and it will handle them, but it is really set up to work well with the custom error types, so when throw an error to be sent to next(error) just consider what you want the logging behavior of that error to be and there should be a corresponding custom error type. The built in JS Error won't be able to attach additional data to the payload that gets logged, and will always be sent to slack.

    See Middleware Examples for how to use this method with expressHandleErrors

    ffErrorHandler.expressHandleErrors

    This is an express middleware that is responsible for responding to the client when an error occurs. It will send back a status code, and sometimes a message depending on the error type. This will middleware will also hand over the error handling to express in edge cases, where the response has already been sent to the client. This should always be used as the final error handling middleware in the middleware stack.

    You can read more about this in the error types section but just as a reminder

    OperationalError responds with no message and a 500 to the client.

    NonOperationalError responds with no message with a 500 to the client.

    HttpError gets logged to papertrail, and sends back the message and status code provided in the constructor to the client.

    See Middleware Examples for how to use this method with expressHandleErrors

    Middleware Examples

    Import the logging middleware

        const { expressLogErrors, expressHandleErrors } = require("@firstfleet/fferrorhandler");

    Add it to an express app - Normally what you want

        const express = require('express');
        const app = express();
        const { expressLogErrors, expressHandleErrors, errorTypes } = require("@firstfleet/fferrorhandler");
        
        app.post("/route", (req, res, next) => {
            try {
                /** 
                * The "Invalid Request" message will be sent to the client, and papertrail (not to slack, as this is an HttpError)
                * The status code 400 will be sent to the client
                * The "Additional" data will be logged to papertrail
                */
                throw new errorTypes.HttpError("Invalid Request", 400, "Additional data");
            } catch (error) {
                next(error)
            }
        });
    
        // To send errors to the middleware, all you need to do is call next(error), either in your route handler, or in another middleware
        app.use(expressLogErrors);
    
        // Should always be used with the expressHandleErrors middleware, with expressHandleErrors being at the bottom of the middleware stack
        app.use(expressHandleErrors);

    Add it to an express router

        const express = require('express');
        const app = express();
        const router = express.Router();
        const { expressLogErrors, expressHandleErrors, errorTypes } = require("@firstfleet/fferrorhandler");
    
        router.post("/route", (req, res, next) => {
            try {
                /** 
                * The "Invalid Request" message will be sent to the client, and papertrail (not to slack, as this is an HttpError)
                * The status code 400 will be sent to the client
                * The "Additional" data will be logged to papertrail
                */
                throw new errorTypes.HttpError("Invalid Request", 400, "Additional data");
            } catch (error) {
                next(error)
            }
        })
    
        // To send errors to the middleware, all you need to do is call next(error), either in your route handler, or in another middleware
        router.use(expressLogErrors);
        // Should always be used with the expressHandleErrors middleware, with expressHandleErrors being at the bottom of the middleware stack
        router.use(expressHandleErrors);

    Add it to an express route

        const express = require('express');
        const app = express();
        const router = express.Router();
        const { expressLogErrors, expressHandleErrors, errorTypes } = require("@firstfleet/fferrorhandler");
    
        // To send errors to the middleware, all you need to do is call next(error), either in your route handler, or in another middleware
        router.post("/route", (req, res, next) => { 
            try {
                /** 
                * The "Invalid Request" message will be sent to the client, and papertrail (not to slack, as this is an HttpError)
                * The status code 400 will be sent to the client
                * The "Additional" data will be logged to papertrail
                */
                throw new errorTypes.HttpError("Invalid Request", 400, "Additional data");
            } catch (error) {
                next(error)
            }
        }, expressLogErrors, expressHandleErrors);

    Handling Uncaught Errors In Node Apps

    The error handler is set up to catch two types of uncaught error exceptions. Those are

    1. uncaughtException

    The error handler will first log the error to slack and papertrail, along with the stack trace, then it will exit the process, and let pm2 restart the server.

    1. unhandledRejection

    The error handler will log the error to slack and papertrail gracefully, rather than forcing the server to restart.

    1. PapertrailErrorEvents

    The error handler also listens for papertrail error events, and will log any errors to slack to let us know if the error handler is not able to log to papertrail, or is failing to send logs to papertrail.

    Keywords

    none

    Install

    npm i @firstfleet/fferrorhandler

    DownloadsWeekly Downloads

    141

    Version

    2.0.10

    License

    ISC

    Unpacked Size

    67.1 kB

    Total Files

    14

    Last publish

    Collaborators

    • dwhitak
    • spies36
    • brbeaird
    • grantdaddy
    • jxpatto