diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index 2560bbcfaf..1a69deb9cc 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -150,6 +150,7 @@ ], "env": { "NODE_ENV": "development", + "AFFINE_ENV": "dev", "AFFINE_SERVER_EXTERNAL_URL": "http://localhost:8080", "DEBUG": "affine:*", "FORCE_COLOR": true, diff --git a/packages/backend/server/src/base/error/def.ts b/packages/backend/server/src/base/error/def.ts index b3f63a06b1..826694c939 100644 --- a/packages/backend/server/src/base/error/def.ts +++ b/packages/backend/server/src/base/error/def.ts @@ -114,6 +114,12 @@ export class UserFriendlyError extends Error { ); } + get stacktrace() { + return this.name === 'internal_server_error' + ? ((this.cause as Error)?.stack ?? super.stack) + : super.stack; + } + toJSON() { return { status: this.status, diff --git a/packages/backend/server/src/base/graphql/index.ts b/packages/backend/server/src/base/graphql/index.ts index cba86da4fd..ae89b41646 100644 --- a/packages/backend/server/src/base/graphql/index.ts +++ b/packages/backend/server/src/base/graphql/index.ts @@ -1,19 +1,16 @@ import './config'; -import { STATUS_CODES } from 'node:http'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import type { ApolloDriverConfig } from '@nestjs/apollo'; import { ApolloDriver } from '@nestjs/apollo'; -import { Global, HttpStatus, Module } from '@nestjs/common'; +import { Global, Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { Request, Response } from 'express'; -import { GraphQLError } from 'graphql'; import { Config } from '../config'; -import { UserFriendlyError } from '../error'; -import { isGraphQLBadRequest, mapAnyError } from '../nestjs/exception'; +import { mapAnyError } from '../nestjs/exception'; import { GQLLoggerPlugin } from './logger-plugin'; export type GraphqlContext = { @@ -52,39 +49,15 @@ export type GraphqlContext = { res, isAdminQuery: false, }), - includeStacktraceInErrorResponses: !config.node.prod, plugins: [new GQLLoggerPlugin()], formatError: (formattedError, error) => { + let ufe = mapAnyError(error); + // @ts-expect-error allow assign - formattedError.extensions ??= {}; - - if ( - error instanceof GraphQLError && - error.originalError instanceof UserFriendlyError - ) { - // @ts-expect-error allow assign - formattedError.extensions = error.originalError.toJSON(); - formattedError.extensions.stacktrace = error.originalError.stack; - return formattedError; - } else if ( - error instanceof GraphQLError && - isGraphQLBadRequest(error) - ) { - const err = mapAnyError(error); - // @ts-expect-error allow assign - formattedError.extensions = err.toJSON(); - formattedError.extensions.stacktrace = err.stack; - return formattedError; - } else { - // @ts-expect-error allow assign - formattedError.message = 'Internal Server Error'; - - formattedError.extensions['status'] = - HttpStatus.INTERNAL_SERVER_ERROR; - formattedError.extensions['code'] = - STATUS_CODES[HttpStatus.INTERNAL_SERVER_ERROR]; + formattedError.extensions = ufe.toJSON(); + if (config.affine.canary) { + formattedError.extensions.stacktrace = ufe.stacktrace; } - return formattedError; }, }; diff --git a/packages/backend/server/src/base/logger/service.ts b/packages/backend/server/src/base/logger/service.ts index ac93b59d31..b0b6962cc0 100644 --- a/packages/backend/server/src/base/logger/service.ts +++ b/packages/backend/server/src/base/logger/service.ts @@ -1,6 +1,8 @@ import { ConsoleLogger, Injectable, type LogLevel } from '@nestjs/common'; import { ClsServiceManager } from 'nestjs-cls'; +import { UserFriendlyError } from '../error'; + // DO NOT use this Logger directly // Use it via this way: `private readonly logger = new Logger(MyService.name)` @Injectable() @@ -20,7 +22,14 @@ export class AFFiNELogger extends ConsoleLogger { static formatStack(stackOrError?: Error | string | unknown) { if (stackOrError instanceof Error) { - const err = stackOrError; + let err = stackOrError; + + // most of the internal error are caught and created by `GlobalExceptionFilter`, + // and their error stack is helpless + if (err instanceof UserFriendlyError) { + return err.stacktrace; + } + let stack = err.stack ?? ''; if (err.cause instanceof Error && err.cause.stack) { stack += `\n\nCaused by:\n\n${err.cause.stack}`;