Skip to content

Commit

Permalink
feat: Setup airbrake logging
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr committed Feb 28, 2024
1 parent c44e1a4 commit f897608
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 43 deletions.
15 changes: 0 additions & 15 deletions api.planx.uk/airbrake.ts

This file was deleted.

22 changes: 19 additions & 3 deletions api.planx.uk/errors/ServerError.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
export class ServerError extends Error {
/**
* Message passed to user who triggered error
*/
message: string;
/**
* HTTP status code to be returned to user
* @default 500
*/
status: number;
/**
* Original error, to be passed to Airbrake or logged in local dev
*/
cause: unknown | undefined;
/**
* Context obejct passed to Airbrake
* Can hold any key-value data which may prove useful for debugging
*/
context: object | undefined;

constructor({
message,
status,
cause,
context,
}: {
message: string;
status?: number;
cause?: unknown;
context?: object | undefined;
}) {
super(message);
this.message = message;
this.status = status || 500;
if (cause) {
this.cause = cause;
}
if (cause) this.cause = cause;
if (context) this.context = context;

// Set the prototype explicitly.
Object.setPrototypeOf(this, ServerError.prototype);
Expand Down
20 changes: 20 additions & 0 deletions api.planx.uk/errors/airbrake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Notifier } from "@airbrake/node";
import { isLiveEnv } from "../helpers";

export const airbrake =
isLiveEnv() &&
process.env.AIRBRAKE_PROJECT_ID &&
process.env.AIRBRAKE_PROJECT_KEY
? new Notifier({
projectId: Number(process.env.AIRBRAKE_PROJECT_ID),
projectKey: process.env.AIRBRAKE_PROJECT_KEY,
environment: process.env.NODE_ENV!,
})
: undefined;

/**
* Simple helper function to manually report an error to Airbrake
* To be used when you do not want to throw an error and halt execution
*/
export const reportError = (report: { error: any; context: object }) =>
airbrake ? airbrake.notify(report) : console.log(report);
78 changes: 78 additions & 0 deletions api.planx.uk/errors/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { ErrorRequestHandler, RequestHandler } from "express";
import { ServerError } from "./ServerError";
import { airbrake } from "./airbrake";
import airbrakeExpress from "@airbrake/node/dist/instrumentation/express";

/**
* Sets up Airbrake metrics recording
*/
export const airbrakeMiddleware: RequestHandler = (_req, _res, next) => {
if (!airbrake) return next();

return airbrakeExpress.makeMiddleware(airbrake);
};

/**
* Log errors to Airbrake
*/
export const errorLogger: ErrorRequestHandler = (
errorObject,
req,
_res,
next,
) => {
if (!airbrake) {
console.log(errorObject);
return next(errorObject);
}

// Default Airbrake notice for all errors
// See https://github.com/airbrake/airbrake-js/blob/master/packages/node/src/instrumentation/express.ts
const notice = {
error: errorObject,
context: {
userAddr: req.ip,
userAgent: req.headers["user-agent"],
url: req.protocol + "://" + req.headers.host + req.originalUrl,
httpMethod: req.method,
component: "express",
route: req.route?.path?.toString(),
action: req.route?.stack?.[0]?.name,
referer: req.headers?.referer,
message: "Something went wrong",
},
};

// Append additional information for explicitly caught errors
if (errorObject instanceof ServerError) {
notice.context = {
...notice.context,
// ...errorObject.context,
message: errorObject.message,
};

// Log original error if provided
if (errorObject.cause) notice.error = errorObject.cause;
}

// Send notice to Airbrake
airbrake.notify(notice);

return next({
...errorObject,
message: errorObject.message.concat(", this error has been logged"),
});
};

export const errorResponder: ErrorRequestHandler = (
errorObject,
_req,
res,
_next,
) => {
const { status = 500, message = "Something went wrong" } = errorObject;

res.status(status).send({
error: message,
});
};
31 changes: 8 additions & 23 deletions api.planx.uk/modules/send/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { gql } from "graphql-request";
import airbrake from "../../../airbrake";
import { $api } from "../../../client";
import { ServerError } from "../../../errors";

export async function logPaymentStatus({
sessionId,
Expand All @@ -20,9 +20,10 @@ export async function logPaymentStatus({
};
}): Promise<void> {
if (!flowId || !sessionId) {
reportError({
error: "Could not log the payment status due to missing context value(s)",
context: { sessionId, flowId, teamSlug },
throw new ServerError({
message:
"Could not log the payment status due to missing context value(s)",
// context: { sessionId, flowId, teamSlug },
});
} else {
// log payment status response
Expand All @@ -36,30 +37,14 @@ export async function logPaymentStatus({
amount: govUkResponse.amount,
});
} catch (e) {
reportError({
error: `Failed to insert a payment status: ${e}`,
context: { govUkResponse },
throw new ServerError({
message: `Failed to insert a payment status: ${e}`,
// context: { govUkResponse },
});
}
}
}

// tmp explicit error handling
export function reportError(report: { error: any; context: object }) {
if (airbrake) {
airbrake.notify(report);
return;
}
log(report);
}

// tmp logger
function log(event: object | string) {
if (!process.env.SUPPRESS_LOGS) {
console.log(event);
}
}

// TODO: this would ideally live in planx-client
async function insertPaymentStatus({
flowId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { isObject } from "lodash";
import { JSDOM } from "jsdom";
import createDOMPurify from "dompurify";
import { reportError } from "../../../send/utils/helpers";
import { decode } from "he";
import { reportError } from "../../../../errors/airbrake";

// Setup JSDOM and DOMPurify
const window = new JSDOM("").window;
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Server } from "http";
import passport from "passport";
import helmet from "helmet";
import { ServerError } from "./errors";
import airbrake from "./airbrake";
import airbrake from "./errors/airbrake";
import { apiLimiter } from "./rateLimit";
import { googleStrategy } from "./modules/auth/strategy/google";
import authRoutes from "./modules/auth/routes";
Expand Down

0 comments on commit f897608

Please sign in to comment.