mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-23 14:03:35 +03:00
Complete Sentry integration (#1546)
This commit is contained in:
parent
35bcef5090
commit
7621854d4b
@ -22,5 +22,6 @@ 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
|
||||
# LOGGER_DRIVER=console
|
||||
# SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
|
||||
# LOGGER_DRIVER=console
|
||||
# LOG_LEVEL=error,warn
|
@ -75,8 +75,7 @@ export class UserResolver {
|
||||
select,
|
||||
});
|
||||
assert(user, 'User not found');
|
||||
|
||||
return user;
|
||||
throw new Error('test2');
|
||||
}
|
||||
|
||||
@UseFilters(ExceptionFilter)
|
||||
|
@ -0,0 +1,58 @@
|
||||
import { plainToClass } from 'class-transformer';
|
||||
|
||||
import { CastToLogLevelArray } from 'src/integrations/environment/decorators/cast-to-log-level-array.decorator';
|
||||
|
||||
class TestClass {
|
||||
@CastToLogLevelArray()
|
||||
logLevels?: any;
|
||||
}
|
||||
|
||||
describe('CastToLogLevelArray Decorator', () => {
|
||||
it('should cast "log" to ["log"]', () => {
|
||||
const transformedClass = plainToClass(TestClass, { logLevels: 'log' });
|
||||
expect(transformedClass.logLevels).toStrictEqual(['log']);
|
||||
});
|
||||
|
||||
it('should cast "error" to ["error"]', () => {
|
||||
const transformedClass = plainToClass(TestClass, { logLevels: 'error' });
|
||||
expect(transformedClass.logLevels).toStrictEqual(['error']);
|
||||
});
|
||||
|
||||
it('should cast "warn" to ["warn"]', () => {
|
||||
const transformedClass = plainToClass(TestClass, { logLevels: 'warn' });
|
||||
expect(transformedClass.logLevels).toStrictEqual(['warn']);
|
||||
});
|
||||
|
||||
it('should cast "debug" to ["debug"]', () => {
|
||||
const transformedClass = plainToClass(TestClass, { logLevels: 'debug' });
|
||||
expect(transformedClass.logLevels).toStrictEqual(['debug']);
|
||||
});
|
||||
|
||||
it('should cast "verbose" to ["verbose"]', () => {
|
||||
const transformedClass = plainToClass(TestClass, { logLevels: 'verbose' });
|
||||
expect(transformedClass.logLevels).toStrictEqual(['verbose']);
|
||||
});
|
||||
|
||||
it('should cast "verbose,error,warn" to ["verbose", "error", "warn"]', () => {
|
||||
const transformedClass = plainToClass(TestClass, {
|
||||
logLevels: 'verbose,error,warn',
|
||||
});
|
||||
expect(transformedClass.logLevels).toStrictEqual([
|
||||
'verbose',
|
||||
'error',
|
||||
'warn',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should cast "toto" to undefined', () => {
|
||||
const transformedClass = plainToClass(TestClass, { logLevels: 'toto' });
|
||||
expect(transformedClass.logLevels).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should cast "verbose,error,toto" to undefined', () => {
|
||||
const transformedClass = plainToClass(TestClass, {
|
||||
logLevels: 'verbose,error,toto',
|
||||
});
|
||||
expect(transformedClass.logLevels).toBeUndefined();
|
||||
});
|
||||
});
|
@ -0,0 +1,20 @@
|
||||
import { Transform } from 'class-transformer';
|
||||
|
||||
export function CastToLogLevelArray() {
|
||||
return Transform(({ value }: { value: string }) => toLogLevelArray(value));
|
||||
}
|
||||
|
||||
const toLogLevelArray = (value: any) => {
|
||||
if (typeof value === 'string') {
|
||||
const rawLogLevels = value.split(',').map((level) => level.trim());
|
||||
const isInvalid = rawLogLevels.some(
|
||||
(level) => !['log', 'error', 'warn', 'debug', 'verbose'].includes(level),
|
||||
);
|
||||
|
||||
if (!isInvalid) {
|
||||
return rawLogLevels;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, LogLevel } from '@nestjs/common';
|
||||
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';
|
||||
import { LoggerDriver } from './interfaces/logger.interface';
|
||||
|
||||
@Injectable()
|
||||
export class EnvironmentService {
|
||||
@ -122,13 +122,23 @@ export class EnvironmentService {
|
||||
return this.configService.get<string>('SUPPORT_FRONT_HMAC_KEY');
|
||||
}
|
||||
|
||||
getLoggerDriver(): string {
|
||||
return (
|
||||
this.configService.get<string>('LOGGER_DRIVER') ?? LoggerDriver.Console
|
||||
);
|
||||
}
|
||||
|
||||
getLogLevels(): LogLevel[] {
|
||||
return (
|
||||
this.configService.get<LogLevel[]>('LOG_LEVELS') ?? [
|
||||
'log',
|
||||
'error',
|
||||
'warn',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
getSentryDSN(): string | undefined {
|
||||
return this.configService.get<string>('SENTRY_DSN');
|
||||
}
|
||||
|
||||
getLoggerDriver(): string | undefined {
|
||||
return (
|
||||
this.configService.get<string>('LOGGER_DRIVER') ?? LoggerType.Console
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { LogLevel } from '@nestjs/common';
|
||||
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import {
|
||||
IsEnum,
|
||||
@ -19,6 +21,8 @@ import { IsAWSRegion } from './decorators/is-aws-region.decorator';
|
||||
import { CastToBoolean } from './decorators/cast-to-boolean.decorator';
|
||||
import { SupportDriver } from './interfaces/support.interface';
|
||||
import { CastToPositiveNumber } from './decorators/cast-to-positive-number.decorator';
|
||||
import { LoggerDriver } from './interfaces/logger.interface';
|
||||
import { CastToLogLevelArray } from './decorators/cast-to-log-level-array.decorator';
|
||||
|
||||
export class EnvironmentVariables {
|
||||
// Misc
|
||||
@ -125,6 +129,18 @@ export class EnvironmentVariables {
|
||||
@ValidateIf((env) => env.SUPPORT_DRIVER === SupportDriver.Front)
|
||||
@IsString()
|
||||
SUPPORT_FRONT_HMAC_KEY?: string;
|
||||
|
||||
@IsEnum(LoggerDriver)
|
||||
@IsOptional()
|
||||
LOGGER_DRIVER?: LoggerDriver;
|
||||
|
||||
@CastToLogLevelArray()
|
||||
@IsOptional()
|
||||
LOG_LEVELS?: LogLevel[];
|
||||
|
||||
@ValidateIf((env) => env.LOGGER_DRIVER === LoggerDriver.Sentry)
|
||||
@IsString()
|
||||
SENTRY_DSN?: string;
|
||||
}
|
||||
|
||||
export function validate(config: Record<string, unknown>) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
export enum LoggerType {
|
||||
export enum LoggerDriver {
|
||||
Console = 'console',
|
||||
Sentry = 'sentry',
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ 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';
|
||||
import { LoggerDriver } from './environment/interfaces/logger.interface';
|
||||
|
||||
/**
|
||||
* FileStorage Module factory
|
||||
@ -63,15 +63,15 @@ const loggerModuleFactory = async (
|
||||
): Promise<LoggerModuleOptions> => {
|
||||
const type = environmentService.getLoggerDriver();
|
||||
switch (type) {
|
||||
case LoggerType.Console: {
|
||||
case LoggerDriver.Console: {
|
||||
return {
|
||||
type: LoggerType.Console,
|
||||
type: LoggerDriver.Console,
|
||||
options: null,
|
||||
};
|
||||
}
|
||||
case LoggerType.Sentry: {
|
||||
case LoggerDriver.Sentry: {
|
||||
return {
|
||||
type: LoggerType.Sentry,
|
||||
type: LoggerDriver.Sentry,
|
||||
options: {
|
||||
sentryDNS: environmentService.getSentryDSN() ?? '',
|
||||
},
|
||||
|
@ -15,53 +15,39 @@ export class SentryDriver implements LoggerService {
|
||||
});
|
||||
}
|
||||
|
||||
log(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'log',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
private logLevels = ['log', 'error', 'warning', 'debug', 'info'];
|
||||
|
||||
setLogLevels(levels: string[]) {
|
||||
this.logLevels = levels;
|
||||
}
|
||||
|
||||
error(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'error',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
log(message: any) {
|
||||
if (this.logLevels.includes('log')) {
|
||||
Sentry.captureMessage(message, { level: 'log' });
|
||||
}
|
||||
}
|
||||
|
||||
warn(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'error',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
error(message: any) {
|
||||
if (this.logLevels.includes('error')) {
|
||||
Sentry.captureMessage(message, { level: 'error' });
|
||||
}
|
||||
}
|
||||
|
||||
debug?(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'debug',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
warn(message: any) {
|
||||
if (this.logLevels.includes('warn')) {
|
||||
Sentry.captureMessage(message, { level: 'warning' });
|
||||
}
|
||||
}
|
||||
|
||||
verbose?(message: any, category: string) {
|
||||
Sentry.addBreadcrumb({
|
||||
message,
|
||||
level: 'info',
|
||||
data: {
|
||||
category,
|
||||
},
|
||||
});
|
||||
debug?(message: any) {
|
||||
if (this.logLevels.includes('debug')) {
|
||||
Sentry.captureMessage(message, { level: 'debug' });
|
||||
}
|
||||
}
|
||||
|
||||
verbose?(message: any) {
|
||||
if (this.logLevels.includes('verbose')) {
|
||||
Sentry.captureMessage(message, { level: 'info' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { LoggerType } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
import { LoggerDriver } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
|
||||
export interface SentryDriverFactoryOptions {
|
||||
type: LoggerType.Sentry;
|
||||
type: LoggerDriver.Sentry;
|
||||
options: {
|
||||
sentryDNS: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ConsoleDriverFactoryOptions {
|
||||
type: LoggerType.Console;
|
||||
type: LoggerDriver.Console;
|
||||
options: null;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DynamicModule, Global, ConsoleLogger } from '@nestjs/common';
|
||||
|
||||
import { LoggerType } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
import { LoggerDriver } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
|
||||
import { LoggerService } from './logger.service';
|
||||
import { LoggerModuleOptions } from './interfaces';
|
||||
@ -15,7 +15,7 @@ export class LoggerModule {
|
||||
const provider = {
|
||||
provide: LOGGER_DRIVER,
|
||||
useValue:
|
||||
options.type === LoggerType.Console
|
||||
options.type === LoggerDriver.Console
|
||||
? new ConsoleLogger()
|
||||
: new SentryDriver(options.options),
|
||||
};
|
||||
@ -32,7 +32,7 @@ export class LoggerModule {
|
||||
provide: LOGGER_DRIVER,
|
||||
useFactory: async (...args: any[]) => {
|
||||
const config = await options.useFactory(...args);
|
||||
return config?.type === LoggerType.Console
|
||||
return config?.type === LoggerDriver.Console
|
||||
? new ConsoleLogger()
|
||||
: new SentryDriver(config.options);
|
||||
},
|
||||
|
@ -1,14 +1,15 @@
|
||||
import {
|
||||
Inject,
|
||||
Injectable,
|
||||
LoggerService as ConsoleLoggerService,
|
||||
LogLevel,
|
||||
LoggerService as LoggerServiceInterface,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { LOGGER_DRIVER } from './logger.constants';
|
||||
|
||||
@Injectable()
|
||||
export class LoggerService implements ConsoleLoggerService {
|
||||
constructor(@Inject(LOGGER_DRIVER) private driver: ConsoleLoggerService) {}
|
||||
export class LoggerService implements LoggerServiceInterface {
|
||||
constructor(@Inject(LOGGER_DRIVER) private driver: LoggerServiceInterface) {}
|
||||
|
||||
log(message: any, category: string, ...optionalParams: any[]) {
|
||||
this.driver.log.apply(this.driver, [message, category, ...optionalParams]);
|
||||
@ -41,4 +42,8 @@ export class LoggerService implements ConsoleLoggerService {
|
||||
...optionalParams,
|
||||
]);
|
||||
}
|
||||
|
||||
setLogLevels(levels: LogLevel[]) {
|
||||
this.driver.setLogLevels?.apply(this.driver, [levels]);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import { AppModule } from './app.module';
|
||||
|
||||
import { settings } from './constants/settings';
|
||||
import { LoggerService } from './integrations/logger/logger.service';
|
||||
import { EnvironmentService } from './integrations/environment/environment.service';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
@ -35,8 +36,9 @@ async function bootstrap() {
|
||||
);
|
||||
const loggerService = app.get(LoggerService);
|
||||
app.useLogger(loggerService);
|
||||
app.useLogger(app.get(EnvironmentService).getLogLevels());
|
||||
|
||||
await app.listen(3000);
|
||||
await app.listen(app.get(EnvironmentService).getPort());
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
Loading…
Reference in New Issue
Block a user