@firstfleet/fferrorhandler
TypeScript icon, indicating that this package has built-in type declarations

2.1.8 • Public • Published

ffErrorHandler

Test

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.

Options Parameter

Key Type Default Value Description
includeBody boolean false 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.

Options Parameter

Key Type Default Value Description
includeBody boolean false 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
    • This can be configured using the additional options payload, see the Options Parameters section below
  • 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, slack: {send: boolean, channel: string}} true Additional options for the error constructor.

Options Parameter

Key Type Default Value Description
includeBody boolean false Whether to include req.body on the error payload
slack.send boolean false Whether to send the error to slack, in addition to papertrail
slack.channel string alerts_web What channel to send the error to in slack
    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);
        }
    }
    const { slackChannels } = require("@firstfleet/fferrorhandler");

    const errorDetails = JSON.stringify({
        user: "jess",
        proc: "cool beans"
    });

    throw new errorTypes.HttpError(
    "My error message",
    500,
    errorDetails,
    {
        includeBody: true,
        slack: {
            send: true,
            channel: slackChannels.WARNING 
        }
    });

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.

Dependencies (3)

Dev Dependencies (1)

Package Sidebar

Install

npm i @firstfleet/fferrorhandler

Weekly Downloads

5

Version

2.1.8

License

ISC

Unpacked Size

76.6 kB

Total Files

20

Last publish

Collaborators

  • nathanbelete
  • dwhitak
  • spies36
  • brbeaird
  • grantdaddy
  • jxpatto