mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-23 14:03:35 +03:00
Add Sentry for Backend (#1403)
* - added sentry * - renamed env var * - logger driver * - add breadcrumb and category * - fix driver
This commit is contained in:
parent
110d5eaa9d
commit
35bcef5090
@ -22,3 +22,5 @@ SIGN_IN_PREFILLED=true
|
||||
# SUPPORT_DRIVER=front
|
||||
# SUPPORT_FRONT_HMAC_KEY=replace_me_with_front_chat_verification_secret
|
||||
# SUPPORT_FRONT_CHAT_ID=replace_me_with_front_chat_id
|
||||
# SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
|
||||
# LOGGER_DRIVER=console
|
@ -45,6 +45,8 @@
|
||||
"@nestjs/terminus": "^9.2.2",
|
||||
"@paljs/plugins": "^5.3.3",
|
||||
"@prisma/client": "4.13.0",
|
||||
"@sentry/node": "^7.66.0",
|
||||
"@sentry/tracing": "^7.66.0",
|
||||
"@types/lodash.camelcase": "^4.3.7",
|
||||
"@types/lodash.merge": "^4.6.7",
|
||||
"add": "^2.0.6",
|
||||
|
@ -5,6 +5,7 @@ import { ConfigService } from '@nestjs/config';
|
||||
import { AwsRegion } from './interfaces/aws-region.interface';
|
||||
import { StorageType } from './interfaces/storage.interface';
|
||||
import { SupportDriver } from './interfaces/support.interface';
|
||||
import { LoggerType } from './interfaces/logger.interface';
|
||||
|
||||
@Injectable()
|
||||
export class EnvironmentService {
|
||||
@ -120,4 +121,14 @@ export class EnvironmentService {
|
||||
getSupportFrontHMACKey(): string | undefined {
|
||||
return this.configService.get<string>('SUPPORT_FRONT_HMAC_KEY');
|
||||
}
|
||||
|
||||
getSentryDSN(): string | undefined {
|
||||
return this.configService.get<string>('SENTRY_DSN');
|
||||
}
|
||||
|
||||
getLoggerDriver(): string | undefined {
|
||||
return (
|
||||
this.configService.get<string>('LOGGER_DRIVER') ?? LoggerType.Console
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
export enum LoggerType {
|
||||
Console = 'console',
|
||||
Sentry = 'sentry',
|
||||
}
|
@ -7,6 +7,9 @@ import { EnvironmentService } from './environment/environment.service';
|
||||
import { FileStorageModule } from './file-storage/file-storage.module';
|
||||
import { FileStorageModuleOptions } from './file-storage/interfaces';
|
||||
import { StorageType } from './environment/interfaces/storage.interface';
|
||||
import { LoggerModule } from './logger/logger.module';
|
||||
import { LoggerType } from './environment/interfaces/logger.interface';
|
||||
import { LoggerModuleOptions } from './logger/interfaces';
|
||||
|
||||
/**
|
||||
* FileStorage Module factory
|
||||
@ -50,6 +53,35 @@ const fileStorageModuleFactory = async (
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Logger Module factory
|
||||
* @param environment
|
||||
* @returns LoggerModuleOptions
|
||||
*/
|
||||
const loggerModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
): Promise<LoggerModuleOptions> => {
|
||||
const type = environmentService.getLoggerDriver();
|
||||
switch (type) {
|
||||
case LoggerType.Console: {
|
||||
return {
|
||||
type: LoggerType.Console,
|
||||
options: null,
|
||||
};
|
||||
}
|
||||
case LoggerType.Sentry: {
|
||||
return {
|
||||
type: LoggerType.Sentry,
|
||||
options: {
|
||||
sentryDNS: environmentService.getSentryDSN() ?? '',
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid logger type (${type}), check your .env file`);
|
||||
}
|
||||
};
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
EnvironmentModule.forRoot({}),
|
||||
@ -57,6 +89,10 @@ const fileStorageModuleFactory = async (
|
||||
useFactory: fileStorageModuleFactory,
|
||||
inject: [EnvironmentService],
|
||||
}),
|
||||
LoggerModule.forRootAsync({
|
||||
useFactory: loggerModuleFactory,
|
||||
inject: [EnvironmentService],
|
||||
}),
|
||||
],
|
||||
exports: [],
|
||||
providers: [],
|
||||
|
67
server/src/integrations/logger/drivers/sentry.driver.ts
Normal file
67
server/src/integrations/logger/drivers/sentry.driver.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { LoggerService } from '@nestjs/common';
|
||||
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
||||
export interface SentryDriverOptions {
|
||||
sentryDNS: string;
|
||||
}
|
||||
|
||||
export class SentryDriver implements LoggerService {
|
||||
constructor(options: SentryDriverOptions) {
|
||||
Sentry.init({
|
||||
dsn: options.sentryDNS,
|
||||
tracesSampleRate: 1.0,
|
||||
profilesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
|
||||
log(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'log',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
error(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'error',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
warn(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'error',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
debug?(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'debug',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
verbose?(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'info',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
1
server/src/integrations/logger/interfaces/index.ts
Normal file
1
server/src/integrations/logger/interfaces/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './logger.interface';
|
@ -0,0 +1,17 @@
|
||||
import { LoggerType } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
|
||||
export interface SentryDriverFactoryOptions {
|
||||
type: LoggerType.Sentry;
|
||||
options: {
|
||||
sentryDNS: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ConsoleDriverFactoryOptions {
|
||||
type: LoggerType.Console;
|
||||
options: null;
|
||||
}
|
||||
|
||||
export type LoggerModuleOptions =
|
||||
| SentryDriverFactoryOptions
|
||||
| ConsoleDriverFactoryOptions;
|
1
server/src/integrations/logger/logger.constants.ts
Normal file
1
server/src/integrations/logger/logger.constants.ts
Normal file
@ -0,0 +1 @@
|
||||
export const LOGGER_DRIVER = Symbol('LOGGER_DRIVER');
|
25
server/src/integrations/logger/logger.module-definition.ts
Normal file
25
server/src/integrations/logger/logger.module-definition.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {
|
||||
ConfigurableModuleBuilder,
|
||||
FactoryProvider,
|
||||
ModuleMetadata,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { LoggerModuleOptions } from './interfaces';
|
||||
|
||||
export const {
|
||||
ConfigurableModuleClass,
|
||||
MODULE_OPTIONS_TOKEN,
|
||||
OPTIONS_TYPE,
|
||||
ASYNC_OPTIONS_TYPE,
|
||||
} = new ConfigurableModuleBuilder<LoggerModuleOptions>({
|
||||
moduleName: 'LoggerService',
|
||||
})
|
||||
.setClassMethodName('forRoot')
|
||||
.build();
|
||||
|
||||
export type LoggerModuleAsyncOptions = {
|
||||
useFactory: (
|
||||
...args: any[]
|
||||
) => LoggerModuleOptions | Promise<LoggerModuleOptions>;
|
||||
} & Pick<ModuleMetadata, 'imports'> &
|
||||
Pick<FactoryProvider, 'inject'>;
|
49
server/src/integrations/logger/logger.module.ts
Normal file
49
server/src/integrations/logger/logger.module.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { DynamicModule, Global, ConsoleLogger } from '@nestjs/common';
|
||||
|
||||
import { LoggerType } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
|
||||
import { LoggerService } from './logger.service';
|
||||
import { LoggerModuleOptions } from './interfaces';
|
||||
import { LOGGER_DRIVER } from './logger.constants';
|
||||
import { LoggerModuleAsyncOptions } from './logger.module-definition';
|
||||
|
||||
import { SentryDriver } from './drivers/sentry.driver';
|
||||
|
||||
@Global()
|
||||
export class LoggerModule {
|
||||
static forRoot(options: LoggerModuleOptions): DynamicModule {
|
||||
const provider = {
|
||||
provide: LOGGER_DRIVER,
|
||||
useValue:
|
||||
options.type === LoggerType.Console
|
||||
? new ConsoleLogger()
|
||||
: new SentryDriver(options.options),
|
||||
};
|
||||
|
||||
return {
|
||||
module: LoggerModule,
|
||||
providers: [LoggerService, provider],
|
||||
exports: [LoggerService],
|
||||
};
|
||||
}
|
||||
|
||||
static forRootAsync(options: LoggerModuleAsyncOptions): DynamicModule {
|
||||
const provider = {
|
||||
provide: LOGGER_DRIVER,
|
||||
useFactory: async (...args: any[]) => {
|
||||
const config = await options.useFactory(...args);
|
||||
return config?.type === LoggerType.Console
|
||||
? new ConsoleLogger()
|
||||
: new SentryDriver(config.options);
|
||||
},
|
||||
inject: options.inject || [],
|
||||
};
|
||||
|
||||
return {
|
||||
module: LoggerModule,
|
||||
imports: options.imports || [],
|
||||
providers: [LoggerService, provider],
|
||||
exports: [LoggerService],
|
||||
};
|
||||
}
|
||||
}
|
26
server/src/integrations/logger/logger.service.spec.ts
Normal file
26
server/src/integrations/logger/logger.service.spec.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { LoggerService } from './logger.service';
|
||||
import { LOGGER_DRIVER } from './logger.constants';
|
||||
|
||||
describe('LoggerService', () => {
|
||||
let service: LoggerService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
LoggerService,
|
||||
{
|
||||
provide: LOGGER_DRIVER,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<LoggerService>(LoggerService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
44
server/src/integrations/logger/logger.service.ts
Normal file
44
server/src/integrations/logger/logger.service.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {
|
||||
Inject,
|
||||
Injectable,
|
||||
LoggerService as ConsoleLoggerService,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { LOGGER_DRIVER } from './logger.constants';
|
||||
|
||||
@Injectable()
|
||||
export class LoggerService implements ConsoleLoggerService {
|
||||
constructor(@Inject(LOGGER_DRIVER) private driver: ConsoleLoggerService) {}
|
||||
|
||||
log(message: any, category: string, ...optionalParams: any[]) {
|
||||
this.driver.log.apply(this.driver, [message, category, ...optionalParams]);
|
||||
}
|
||||
|
||||
error(message: any, category: string, ...optionalParams: any[]) {
|
||||
this.driver.error.apply(this.driver, [
|
||||
message,
|
||||
category,
|
||||
...optionalParams,
|
||||
]);
|
||||
}
|
||||
|
||||
warn(message: any, category: string, ...optionalParams: any[]) {
|
||||
this.driver.warn.apply(this.driver, [message, category, ...optionalParams]);
|
||||
}
|
||||
|
||||
debug?(message: any, category: string, ...optionalParams: any[]) {
|
||||
this.driver.debug?.apply(this.driver, [
|
||||
message,
|
||||
category,
|
||||
...optionalParams,
|
||||
]);
|
||||
}
|
||||
|
||||
verbose?(message: any, category: string, ...optionalParams: any[]) {
|
||||
this.driver.verbose?.apply(this.driver, [
|
||||
message,
|
||||
category,
|
||||
...optionalParams,
|
||||
]);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import bytes from 'bytes';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
import { settings } from './constants/settings';
|
||||
import { LoggerService } from './integrations/logger/logger.service';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
@ -32,6 +33,8 @@ async function bootstrap() {
|
||||
maxFiles: 10,
|
||||
}),
|
||||
);
|
||||
const loggerService = app.get(LoggerService);
|
||||
app.useLogger(loggerService);
|
||||
|
||||
await app.listen(3000);
|
||||
}
|
||||
|
@ -2151,6 +2151,59 @@
|
||||
resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz"
|
||||
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
|
||||
|
||||
"@sentry-internal/tracing@7.66.0":
|
||||
version "7.66.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.66.0.tgz#45ea607917d55a5bcaa3229341387ff6ed9b3a2b"
|
||||
integrity sha512-3vCgC2hC3T45pn53yTDVcRpHoJTBxelDPPZVsipAbZnoOVPkj7n6dNfDhj3I3kwWCBPahPkXmE+R4xViR8VqJg==
|
||||
dependencies:
|
||||
"@sentry/core" "7.66.0"
|
||||
"@sentry/types" "7.66.0"
|
||||
"@sentry/utils" "7.66.0"
|
||||
tslib "^2.4.1 || ^1.9.3"
|
||||
|
||||
"@sentry/core@7.66.0":
|
||||
version "7.66.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.66.0.tgz#8968f2a9e641d33e3750a8e24d1d39953680c4f2"
|
||||
integrity sha512-WMAEPN86NeCJ1IT48Lqiz4MS5gdDjBwP4M63XP4msZn9aujSf2Qb6My5uT87AJr9zBtgk8MyJsuHr35F0P3q1w==
|
||||
dependencies:
|
||||
"@sentry/types" "7.66.0"
|
||||
"@sentry/utils" "7.66.0"
|
||||
tslib "^2.4.1 || ^1.9.3"
|
||||
|
||||
"@sentry/node@^7.66.0":
|
||||
version "7.66.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.66.0.tgz#d3e08471e1ecae28d3cd0ba3c18487ecb2449881"
|
||||
integrity sha512-PxqIqLr4Sh5xcDfECiBQ4PuZ7v8yTgLhaRkruWrZPYxQrcJFPkwbFkw/IskzVnhT2VwXUmeWEIlRMQKBJ0t83A==
|
||||
dependencies:
|
||||
"@sentry-internal/tracing" "7.66.0"
|
||||
"@sentry/core" "7.66.0"
|
||||
"@sentry/types" "7.66.0"
|
||||
"@sentry/utils" "7.66.0"
|
||||
cookie "^0.4.1"
|
||||
https-proxy-agent "^5.0.0"
|
||||
lru_map "^0.3.3"
|
||||
tslib "^2.4.1 || ^1.9.3"
|
||||
|
||||
"@sentry/tracing@^7.66.0":
|
||||
version "7.66.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.66.0.tgz#32e0e4c049abcbdbba793afdfe177f3d112242cf"
|
||||
integrity sha512-9bnz2EcOwjeMZAuYJnrwcRrImu9c10p7A0iDB8b2HLcp7gpuCkJbJyGoC1xeKD7reVD0BPq3VIbeHSwCcQufoQ==
|
||||
dependencies:
|
||||
"@sentry-internal/tracing" "7.66.0"
|
||||
|
||||
"@sentry/types@7.66.0":
|
||||
version "7.66.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.66.0.tgz#4ec290cc6a3dd2024a61a0bffb468cedb409f7fb"
|
||||
integrity sha512-uUMSoSiar6JhuD8p7ON/Ddp4JYvrVd2RpwXJRPH1A4H4Bd4DVt1mKJy1OLG6HdeQv39XyhB1lPZckKJg4tATPw==
|
||||
|
||||
"@sentry/utils@7.66.0":
|
||||
version "7.66.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.66.0.tgz#2e37c96610f26bc79ac064fca4222ea91fece68d"
|
||||
integrity sha512-9GYUVgXjK66uXXcLXVMXVzlptqMtq1eJENCuDeezQiEFrNA71KkLDg00wESp+LL+bl3wpVTBApArpbF6UEG5hQ==
|
||||
dependencies:
|
||||
"@sentry/types" "7.66.0"
|
||||
tslib "^2.4.1 || ^1.9.3"
|
||||
|
||||
"@sinclair/typebox@^0.24.1":
|
||||
version "0.24.51"
|
||||
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz"
|
||||
@ -4317,6 +4370,11 @@ cookie@0.5.0:
|
||||
resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
cookie@^0.4.1:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
||||
cookiejar@^2.1.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz"
|
||||
@ -6711,6 +6769,11 @@ lru-cache@^9.1.1:
|
||||
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz"
|
||||
integrity sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==
|
||||
|
||||
lru_map@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
|
||||
integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==
|
||||
|
||||
macos-release@^2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz"
|
||||
@ -8570,6 +8633,11 @@ tslib@^2.3.1, tslib@^2.5.0:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
|
||||
integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==
|
||||
|
||||
"tslib@^2.4.1 || ^1.9.3":
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
|
||||
tsutils@^3.21.0:
|
||||
version "3.21.0"
|
||||
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user