Skip to content

Commit

Permalink
Complete Sentry integration (#1546)
Browse files Browse the repository at this point in the history
  • Loading branch information
charlesBochet authored Sep 11, 2023
1 parent 35bcef5 commit 7621854
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 65 deletions.
3 changes: 2 additions & 1 deletion server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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://[email protected]/xxx
# LOGGER_DRIVER=console
# LOG_LEVEL=error,warn
3 changes: 1 addition & 2 deletions server/src/core/user/user.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ export class UserResolver {
select,
});
assert(user, 'User not found');

return user;
throw new Error('test2');
}

@UseFilters(ExceptionFilter)
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
});
});
Original file line number Diff line number Diff line change
@@ -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;
};
22 changes: 16 additions & 6 deletions server/src/integrations/environment/environment.service.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -122,13 +122,23 @@ export class EnvironmentService {
return this.configService.get<string>('SUPPORT_FRONT_HMAC_KEY');
}

getSentryDSN(): string | undefined {
return this.configService.get<string>('SENTRY_DSN');
getLoggerDriver(): string {
return (
this.configService.get<string>('LOGGER_DRIVER') ?? LoggerDriver.Console
);
}

getLoggerDriver(): string | undefined {
getLogLevels(): LogLevel[] {
return (
this.configService.get<string>('LOGGER_DRIVER') ?? LoggerType.Console
this.configService.get<LogLevel[]>('LOG_LEVELS') ?? [
'log',
'error',
'warn',
]
);
}

getSentryDSN(): string | undefined {
return this.configService.get<string>('SENTRY_DSN');
}
}
16 changes: 16 additions & 0 deletions server/src/integrations/environment/environment.validation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LogLevel } from '@nestjs/common';

import { plainToClass } from 'class-transformer';
import {
IsEnum,
Expand All @@ -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
Expand Down Expand Up @@ -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>) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export enum LoggerType {
export enum LoggerDriver {
Console = 'console',
Sentry = 'sentry',
}
10 changes: 5 additions & 5 deletions server/src/integrations/integrations.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() ?? '',
},
Expand Down
66 changes: 26 additions & 40 deletions server/src/integrations/logger/drivers/sentry.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' });
}
}
}
6 changes: 3 additions & 3 deletions server/src/integrations/logger/interfaces/logger.interface.ts
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down
6 changes: 3 additions & 3 deletions server/src/integrations/logger/logger.module.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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),
};
Expand All @@ -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);
},
Expand Down
11 changes: 8 additions & 3 deletions server/src/integrations/logger/logger.service.ts
Original file line number Diff line number Diff line change
@@ -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]);
Expand Down Expand Up @@ -41,4 +42,8 @@ export class LoggerService implements ConsoleLoggerService {
...optionalParams,
]);
}

setLogLevels(levels: LogLevel[]) {
this.driver.setLogLevels?.apply(this.driver, [levels]);
}
}
4 changes: 3 additions & 1 deletion server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand All @@ -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();

0 comments on commit 7621854

Please sign in to comment.