diff --git a/apps/server/src/middleware/exception-logger.ts b/apps/server/src/middleware/exception-logger.ts index cec7eaecc6..00e17e6fa0 100644 --- a/apps/server/src/middleware/exception-logger.ts +++ b/apps/server/src/middleware/exception-logger.ts @@ -4,10 +4,13 @@ import { ExceptionFilter, HttpException, Logger, + NotFoundException, } from '@nestjs/common'; +import { GqlContextType } from '@nestjs/graphql'; import { Request, Response } from 'express'; import { REQUEST_ID } from '../constants'; +const TrivialExceptions = [NotFoundException]; @Catch() export class ExceptionLogger implements ExceptionFilter { @@ -16,15 +19,26 @@ export class ExceptionLogger implements ExceptionFilter { catch(exception: Error, host: ArgumentsHost) { // with useGlobalFilters, the context is always HTTP const ctx = host.switchToHttp(); + const request = ctx.getRequest(); const requestId = request?.header(REQUEST_ID); + + const shouldVerboseLog = !TrivialExceptions.some( + e => exception instanceof e + ); this.logger.error( new Error( `${requestId ? `requestId-${requestId}:` : ''}${exception.message}`, { cause: exception } - ), - exception.stack + ) ); + if (shouldVerboseLog) { + this.logger.error(exception.stack); + } + + if (host.getType() === 'graphql') { + return; + } const response = ctx.getResponse(); if (exception instanceof HttpException) { diff --git a/apps/server/src/tests/exception-logger.e2e.ts b/apps/server/src/tests/exception-logger.e2e.ts new file mode 100644 index 0000000000..73e917f20a --- /dev/null +++ b/apps/server/src/tests/exception-logger.e2e.ts @@ -0,0 +1,87 @@ +import { Controller, Get, INestApplication } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import test from 'ava'; +// @ts-expect-error graphql-upload is not typed +import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs'; +import request from 'supertest'; + +import { AppModule } from '../app'; +import { ExceptionLogger } from '../middleware/exception-logger'; +import { PrismaService } from '../prisma'; + +const gql = '/graphql'; +const rest = '/rest'; + +let app: INestApplication; + +class FakePrisma { + get workspace() { + return { + async findUnique() { + throw Error('exception from graphql'); + }, + }; + } +} + +@Controller('rest') +export class MockController { + @Get() + test(): string { + throw new Error('exception from rest api'); + } +} + +test.beforeEach(async () => { + const module = await Test.createTestingModule({ + imports: [AppModule], + controllers: [MockController], + }) + .overrideProvider(PrismaService) + .useClass(FakePrisma) + .compile(); + app = module.createNestApplication({ + cors: true, + bodyParser: true, + }); + app.useGlobalFilters(new ExceptionLogger()); + app.use( + graphqlUploadExpress({ + maxFileSize: 10 * 1024 * 1024, + maxFiles: 5, + }) + ); + await app.init(); +}); + +test.afterEach(async () => { + await app.close(); +}); + +test('should get response from graphql', async t => { + const id = 'workspace'; + + const response = await request(app.getHttpServer()) + .post(gql) + .send({ + name: 'getPublicWorkspace', + query: ` + query getPublicWorkspace($id: String!) { + publicWorkspace(id: $id) { + id + } + } + `, + variables: { id }, + }); + + t.is(response.status, 200); + t.is(response.body.errors[0].message, 'exception from graphql'); +}); + +test('should get response from rest api', async t => { + const response = await request(app.getHttpServer()).get(rest); + + t.is(response.status, 500); + t.is(response.body.error, 'exception from rest api'); +});