Add Sentry for Backend (#1403)
* - added sentry * - renamed env var * - logger driver * - add breadcrumb and category * - fix driver
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user