Skip to content

Commit

Permalink
Merge pull request #7359 from ever-co/develop
Browse files Browse the repository at this point in the history
feat: better handing of Sentry / Unhandled exceptions / Redis connect…
  • Loading branch information
evereq authored Dec 23, 2023
2 parents e66a81c + de93498 commit d2ed794
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 63 deletions.
88 changes: 59 additions & 29 deletions packages/core/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { APP_GUARD, APP_INTERCEPTOR, HttpAdapterHost } from '@nestjs/core';
import { MulterModule } from '@nestjs/platform-express';
import { ThrottlerModule, ThrottlerModuleOptions } from '@nestjs/throttler';
import { ThrottlerBehindProxyGuard } from 'throttler/throttler-behind-proxy.guard';
import { SentryModule } from './core/sentry/ntegral';
import { SentryModule, SentryModuleOptions, SENTRY_MODULE_OPTIONS } from './core/sentry/ntegral';
import { GraphqlInterceptor } from './core/sentry/ntegral';
import { ServeStaticModule, ServeStaticModuleOptions } from '@nestjs/serve-static';
import { HeaderResolver, I18nModule } from 'nestjs-i18n';
Expand Down Expand Up @@ -237,6 +237,36 @@ if (environment.sentry && environment.sentry.dsn) {
console.log('Sentry Request Data Enabled');
}

function createSentryOptions(host: HttpAdapterHost): SentryModuleOptions {
return {
dsn: environment.sentry.dsn,
debug: process.env.SENTRY_DEBUG === 'true' || !environment.production,
environment: environment.production ? 'production' : 'development',
// TODO: we should use some internal function which returns version of Gauzy
release: 'gauzy@' + process.env.npm_package_version,
logLevels: ['error'],
integrations: [
...sentryIntegrations,
host?.httpAdapter
? new Integrations.Express({
app: host.httpAdapter.getInstance()
})
: null
].filter((i) => !!i),
tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE
? parseInt(process.env.SENTRY_TRACES_SAMPLE_RATE)
: 0.01,
profilesSampleRate: process.env.SENTRY_PROFILE_SAMPLE_RATE
? parseInt(process.env.SENTRY_PROFILE_SAMPLE_RATE)
: 1,
close: {
enabled: true,
// Time in milliseconds to forcefully quit the application
timeout: 3000
}
};
}

@Module({
imports: [
...(process.env.REDIS_ENABLED === 'true'
Expand Down Expand Up @@ -283,11 +313,32 @@ if (environment.sentry && environment.sentry.dsn) {
url: url,
username: username,
password: password,
ttl: 60 * 60 * 24 * 7 // 1 week
ttl: 60 * 60 * 24 * 7 // 1 week,
};

const store = await redisStore(storeOptions);

store.client
.on('error', (err) => {
console.log('Redis Client Error: ', err);
})
.on('connect', () => {
console.log('Redis Client Connected');
})
.on('ready', () => {
console.log('Redis Client Ready');
})
.on('reconnecting', () => {
console.log('Redis Client Reconnecting');
})
.on('end', () => {
console.log('Redis Client End');
});

// ping Redis
const res = await store.client.ping();
console.log('Redis Client Cache Ping: ', res);

return {
store: () => store
};
Expand Down Expand Up @@ -315,33 +366,7 @@ if (environment.sentry && environment.sentry.dsn) {
? [
SentryModule.forRootAsync({
inject: [ConfigService, HttpAdapterHost],
useFactory: async (config: ConfigService, host: HttpAdapterHost) => ({
dsn: environment.sentry.dsn,
debug: process.env.SENTRY_DEBUG === 'true' || !environment.production,
environment: environment.production ? 'production' : 'development',
// TODO: we should use some internal function which returns version of Gauzy
release: 'gauzy@' + process.env.npm_package_version,
logLevels: ['error'],
integrations: [
...sentryIntegrations,
host?.httpAdapter
? new Integrations.Express({
app: host.httpAdapter.getInstance()
})
: null
].filter((i) => !!i),
tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE
? parseInt(process.env.SENTRY_TRACES_SAMPLE_RATE)
: 0.01,
profilesSampleRate: process.env.SENTRY_PROFILE_SAMPLE_RATE
? parseInt(process.env.SENTRY_PROFILE_SAMPLE_RATE)
: 1,
close: {
enabled: true,
// Time in milliseconds to forcefully quit the application
timeout: 3000
}
})
useFactory: createSentryOptions
})
]
: []),
Expand Down Expand Up @@ -533,6 +558,11 @@ if (environment.sentry && environment.sentry.dsn) {
},
...(environment.sentry && environment.sentry.dsn
? [
{
provide: SENTRY_MODULE_OPTIONS,
useFactory: createSentryOptions,
inject: [HttpAdapterHost]
},
{
provide: APP_INTERCEPTOR,
useFactory: () => new SentryCustomInterceptor()
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/bootstrap/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export async function bootstrap(pluginConfig?: Partial<IPluginConfig>): Promise<

// ping Redis
const res = await redisClient.ping();
console.log('Redis Client ping: ', res);
console.log('Redis Client Sessions Ping: ', res);

const redisStore = new RedisStore({
client: redisClient,
Expand Down
59 changes: 30 additions & 29 deletions packages/core/src/core/sentry/ntegral/sentry.interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,53 @@
import { ModuleMetadata, Type } from "@nestjs/common/interfaces";
import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
import { Integration, Options } from '@sentry/types';
import { ConsoleLoggerOptions, HttpException } from "@nestjs/common";
import { SeverityLevel } from "@sentry/node";
import { ConsoleLoggerOptions } from '@nestjs/common';
import { SeverityLevel } from '@sentry/node';

export interface SentryCloseOptions {
enabled: boolean;
// timeout – Maximum time in ms the client should wait until closing forcefully
timeout?: number;
enabled: boolean;
// timeout – Maximum time in ms the client should wait until closing forcefully
timeout?: number;
}

export type SentryModuleOptions = Omit<Options, 'integrations'> & {
integrations?: Integration[];
close?: SentryCloseOptions
integrations?: Integration[];
close?: SentryCloseOptions;
profilesSampleRate?: number;
} & ConsoleLoggerOptions;

export interface SentryOptionsFactory {
createSentryModuleOptions(): Promise<SentryModuleOptions> | SentryModuleOptions;
createSentryModuleOptions(): Promise<SentryModuleOptions> | SentryModuleOptions;
}

export interface SentryModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
inject?: any[];
useClass?: Type<SentryOptionsFactory>;
useExisting?: Type<SentryOptionsFactory>;
useFactory?: (...args: any[]) => Promise<SentryModuleOptions> | SentryModuleOptions;
inject?: any[];
useClass?: Type<SentryOptionsFactory>;
useExisting?: Type<SentryOptionsFactory>;
useFactory?: (...args: any[]) => Promise<SentryModuleOptions> | SentryModuleOptions;
}

export type SentryTransaction = boolean | 'path' | 'methodPath' | 'handler';

export interface SentryFilterFunction {
(exception:any): boolean
(exception: any): boolean;
}

export interface SentryInterceptorOptionsFilter {
type: any;
filter?: SentryFilterFunction;
type: any;
filter?: SentryFilterFunction;
}

export interface SentryInterceptorOptions {
filters?: SentryInterceptorOptionsFilter[];
tags?: { [key: string]: string };
extra?: { [key: string]: any };
fingerprint?: string[];
level?: SeverityLevel;

// https://github.com/getsentry/sentry-javascript/blob/master/packages/node/src/handlers.ts#L163
request?: boolean;
serverName?: boolean;
transaction?: boolean | 'path' | 'methodPath' | 'handler'; // https://github.com/getsentry/sentry-javascript/blob/master/packages/node/src/handlers.ts#L16
user?: boolean | string[];
version?: boolean;
}
filters?: SentryInterceptorOptionsFilter[];
tags?: { [key: string]: string };
extra?: { [key: string]: any };
fingerprint?: string[];
level?: SeverityLevel;

// https://github.com/getsentry/sentry-javascript/blob/master/packages/node/src/handlers.ts#L163
request?: boolean;
serverName?: boolean;
transaction?: boolean | 'path' | 'methodPath' | 'handler'; // https://github.com/getsentry/sentry-javascript/blob/master/packages/node/src/handlers.ts#L16
user?: boolean | string[];
version?: boolean;
}
3 changes: 1 addition & 2 deletions packages/core/src/core/sentry/sentry-request.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { InjectSentry, SentryService } from './ntegral';
import { Handlers } from '@sentry/node';
import { NextFunction, Request, Response } from 'express';

Expand All @@ -14,7 +13,7 @@ export class SentryRequestMiddleware implements NestMiddleware {
}
});

constructor(@InjectSentry() private readonly sentry: SentryService) {}
constructor() {}
use(req: Request, res: Response, next: NextFunction): void {
this.handler(req, res, next);
}
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/core/sentry/sentry-trace.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { InjectSentry, SentryService } from './ntegral';
import { Handlers } from '@sentry/node';
import { NextFunction, Request, Response } from 'express';

@Injectable()
export class SentryTraceMiddleware implements NestMiddleware {
private readonly handler = Handlers.tracingHandler();

constructor(@InjectSentry() private readonly sentry: SentryService) {}
use(req: Request, res: Response, next: NextFunction): void {
this.handler(req, res, next);
}
Expand Down

0 comments on commit d2ed794

Please sign in to comment.