Skip to content

Commit

Permalink
Fix logged errors not being properly formatted (and tone down the err…
Browse files Browse the repository at this point in the history
…or verbosity a bit) (#874)

* Ensure we add the error handlers last

* Refactor error middleware to be less noisy

* changelog

* Add finalise
  • Loading branch information
Half-Shot authored Jan 2, 2024
1 parent c0bb71d commit 41ee467
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 13 deletions.
2 changes: 2 additions & 0 deletions changelog.d/874.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix hookshot failing to format API errors.
Only log a stacktrace of API errors on debug level logging, log limited error on info.
1 change: 1 addition & 0 deletions spec/util/e2e-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ export class E2ETestEnv {
}
};
const app = await start(config, registration);
app.listener.finaliseListeners();

return new E2ETestEnv(homeserver, app, opts, config, dir);
}
Expand Down
3 changes: 2 additions & 1 deletion src/App/BridgeApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ async function startFromFile() {
const registrationFile = process.argv[3] || "./registration.yml";
const config = await BridgeConfig.parseConfig(configFile, process.env);
const registration = await parseRegistrationFile(registrationFile);
const { bridgeApp } = await start(config, registration);
const { bridgeApp, listener } = await start(config, registration);
await bridgeApp.start();
listener.finaliseListeners();
}

if (require.main === module) {
Expand Down
1 change: 1 addition & 0 deletions src/App/GithubWebhookApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ async function start() {
}
const webhookHandler = new Webhooks(config);
listener.bindResource('webhooks', webhookHandler.expressRouter);
listener.finaliseListeners();
const userWatcher = new UserNotificationWatcher(config);
userWatcher.start();
process.once("SIGTERM", () => {
Expand Down
1 change: 1 addition & 0 deletions src/App/MatrixSenderApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ async function start() {
listener.bindResource('metrics', Metrics.expressRouter);
}
}
listener.finaliseListeners();
sender.listen();
process.once("SIGTERM", () => {
log.error("Got SIGTERM");
Expand Down
13 changes: 8 additions & 5 deletions src/ListenerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ export class ListenerService {
}
}

public finaliseListeners() {
for (const listener of this.listeners) {
// By default, Sentry only reports 500+ errors, which is what we want.
listener.app.use(Handlers.errorHandler());
listener.app.use((err: unknown, req: Request, res: Response, next: NextFunction) => errorMiddleware(log)(err, req, res, next));
}
}

public getApplicationsForResource(resourceName: ResourceName): Application[] {
const listeners = this.listeners.filter((l) => l.config.resources.includes(resourceName));
if (listeners.length === 0) {
Expand All @@ -74,11 +82,6 @@ export class ListenerService {
// Ensure each listener has a ready probe.
listener.app.get("/live", (_, res) => res.send({ok: true}));
listener.app.get("/ready", (_, res) => res.status(listener.resourcesBound ? 200 : 500).send({ready: listener.resourcesBound}));

// By default, Sentry only reports 500+ errors, which is what we want.
listener.app.use(Handlers.errorHandler());
// Always include the error handler
listener.app.use((err: unknown, req: Request, res: Response, next: NextFunction) => errorMiddleware(log)(err, req, res, next));
log.info(`Listening on http://${addr}:${listener.config.port} for ${listener.config.resources.join(', ')}`)
}
}
Expand Down
16 changes: 9 additions & 7 deletions src/api/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const ErrCodeToStatusCode: Record<ErrCode, StatusCodes> = {
}

export class ApiError extends Error implements IApiError {
static readonly GenericFailure = new ApiError("An internal error occurred");

constructor(
public readonly error: string,
public readonly errcode = ErrCode.Unknown,
Expand Down Expand Up @@ -109,19 +111,19 @@ export class ValidatorApiError extends ApiError {


export function errorMiddleware(log: Logger) {
return (err: unknown, _req: Request, res: Response, next: NextFunction) => {
return (err: unknown, req: Request, res: Response, next: NextFunction) => {
if (!err) {
next();
return;
}
log.warn(err);
const apiError = err instanceof ApiError ? err : ApiError.GenericFailure;
// Log a reduced error on info
log.info(`${req.method} ${req.path} ${apiError.statusCode} - ${apiError.errcode} - ${apiError.error}`);
// Only show full error on debug level.
log.debug(err);
if (res.headersSent) {
return;
}
if (err instanceof ApiError) {
err.apply(res);
} else {
new ApiError("An internal error occurred").apply(res);
}
apiError.apply(res);
}
}

0 comments on commit 41ee467

Please sign in to comment.