chore(server): better internal error stack (#11009)

This commit is contained in:
forehalo
2025-03-19 16:41:56 +00:00
parent b3c6333694
commit b3a245f47a
4 changed files with 24 additions and 35 deletions
+1
View File
@@ -150,6 +150,7 @@
], ],
"env": { "env": {
"NODE_ENV": "development", "NODE_ENV": "development",
"AFFINE_ENV": "dev",
"AFFINE_SERVER_EXTERNAL_URL": "http://localhost:8080", "AFFINE_SERVER_EXTERNAL_URL": "http://localhost:8080",
"DEBUG": "affine:*", "DEBUG": "affine:*",
"FORCE_COLOR": true, "FORCE_COLOR": true,
@@ -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() { toJSON() {
return { return {
status: this.status, status: this.status,
@@ -1,19 +1,16 @@
import './config'; import './config';
import { STATUS_CODES } from 'node:http';
import { join } from 'node:path'; import { join } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import type { ApolloDriverConfig } from '@nestjs/apollo'; import type { ApolloDriverConfig } from '@nestjs/apollo';
import { ApolloDriver } 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 { GraphQLModule } from '@nestjs/graphql';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { GraphQLError } from 'graphql';
import { Config } from '../config'; import { Config } from '../config';
import { UserFriendlyError } from '../error'; import { mapAnyError } from '../nestjs/exception';
import { isGraphQLBadRequest, mapAnyError } from '../nestjs/exception';
import { GQLLoggerPlugin } from './logger-plugin'; import { GQLLoggerPlugin } from './logger-plugin';
export type GraphqlContext = { export type GraphqlContext = {
@@ -52,39 +49,15 @@ export type GraphqlContext = {
res, res,
isAdminQuery: false, isAdminQuery: false,
}), }),
includeStacktraceInErrorResponses: !config.node.prod,
plugins: [new GQLLoggerPlugin()], plugins: [new GQLLoggerPlugin()],
formatError: (formattedError, error) => { formatError: (formattedError, error) => {
let ufe = mapAnyError(error);
// @ts-expect-error allow assign // @ts-expect-error allow assign
formattedError.extensions ??= {}; formattedError.extensions = ufe.toJSON();
if (config.affine.canary) {
if ( formattedError.extensions.stacktrace = ufe.stacktrace;
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];
} }
return formattedError; return formattedError;
}, },
}; };
@@ -1,6 +1,8 @@
import { ConsoleLogger, Injectable, type LogLevel } from '@nestjs/common'; import { ConsoleLogger, Injectable, type LogLevel } from '@nestjs/common';
import { ClsServiceManager } from 'nestjs-cls'; import { ClsServiceManager } from 'nestjs-cls';
import { UserFriendlyError } from '../error';
// DO NOT use this Logger directly // DO NOT use this Logger directly
// Use it via this way: `private readonly logger = new Logger(MyService.name)` // Use it via this way: `private readonly logger = new Logger(MyService.name)`
@Injectable() @Injectable()
@@ -20,7 +22,14 @@ export class AFFiNELogger extends ConsoleLogger {
static formatStack(stackOrError?: Error | string | unknown) { static formatStack(stackOrError?: Error | string | unknown) {
if (stackOrError instanceof Error) { 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 ?? ''; let stack = err.stack ?? '';
if (err.cause instanceof Error && err.cause.stack) { if (err.cause instanceof Error && err.cause.stack) {
stack += `\n\nCaused by:\n\n${err.cause.stack}`; stack += `\n\nCaused by:\n\n${err.cause.stack}`;