diff --git a/src/app.module.ts b/src/app.module.ts index 4771a5d7d..a9f7f2a91 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -28,6 +28,7 @@ import { GroupsModule } from './groups/groups.module'; import { HistoryModule } from './history/history.module'; import { IdentityModule } from './identity/identity.module'; import { LoggerModule } from './logger/logger.module'; +import { TypeormLoggerService } from './logger/typeorm-logger.service'; import { MediaModule } from './media/media.module'; import { MonitoringModule } from './monitoring/monitoring.module'; import { NotesModule } from './notes/notes.module'; @@ -50,9 +51,12 @@ const routes: Routes = [ imports: [ RouterModule.forRoutes(routes), TypeOrmModule.forRootAsync({ - imports: [ConfigModule], - inject: [databaseConfig.KEY], - useFactory: (databaseConfig: DatabaseConfig) => { + imports: [ConfigModule, LoggerModule], + inject: [databaseConfig.KEY, TypeormLoggerService], + useFactory: ( + databaseConfig: DatabaseConfig, + logger: TypeormLoggerService, + ) => { return { type: databaseConfig.type, host: databaseConfig.host, @@ -62,6 +66,8 @@ const routes: Routes = [ database: databaseConfig.database, autoLoadEntities: true, synchronize: true, // ToDo: Remove this before release + logging: true, + logger: logger, }; }, }), diff --git a/src/logger/logger.module.ts b/src/logger/logger.module.ts index 1f14785f4..d4c5caab2 100644 --- a/src/logger/logger.module.ts +++ b/src/logger/logger.module.ts @@ -6,9 +6,10 @@ import { Module } from '@nestjs/common'; import { ConsoleLoggerService } from './console-logger.service'; +import { TypeormLoggerService } from './typeorm-logger.service'; @Module({ - providers: [ConsoleLoggerService], - exports: [ConsoleLoggerService], + providers: [ConsoleLoggerService, TypeormLoggerService], + exports: [ConsoleLoggerService, TypeormLoggerService], }) export class LoggerModule {} diff --git a/src/logger/typeorm-logger.service.ts b/src/logger/typeorm-logger.service.ts new file mode 100644 index 000000000..8f075f336 --- /dev/null +++ b/src/logger/typeorm-logger.service.ts @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + * + * The code in this class is based on: + * https://github.com/typeorm/typeorm/blob/master/src/logger/AdvancedConsoleLogger.ts + */ +import { Injectable } from '@nestjs/common'; +import { Logger, QueryRunner } from 'typeorm'; +import { PlatformTools } from 'typeorm/platform/PlatformTools'; + +import { ConsoleLoggerService } from './console-logger.service'; + +@Injectable() +export class TypeormLoggerService implements Logger { + constructor(private readonly logger: ConsoleLoggerService) { + this.logger.setContext('TypeORM'); + this.logger.setSkipColor(true); + } + + log(level: 'log' | 'info' | 'warn', message: unknown, _?: QueryRunner): void { + switch (level) { + case 'log': + case 'info': + this.logger.log(message); + break; + case 'warn': + this.logger.warn(message); + } + } + + logMigration(message: string, _?: QueryRunner): void { + // eslint-disable-next-line local-rules/correct-logger-context + this.logger.log(message, 'migration'); + } + + logQuery(query: string, parameters?: unknown[], _?: QueryRunner): void { + const sql = + query + + (parameters && parameters.length + ? ' -- PARAMETERS: ' + this.stringifyParams(parameters) + : ''); + // eslint-disable-next-line local-rules/correct-logger-context + this.logger.debug(PlatformTools.highlightSql(sql), 'query'); + } + + logQueryError( + error: string | Error, + query: string, + parameters?: unknown[], + _?: QueryRunner, + ): void { + const sql = + query + + (parameters && parameters.length + ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` + : ''); + this.logger.debug(PlatformTools.highlightSql(sql)); + // eslint-disable-next-line local-rules/correct-logger-context + this.logger.debug(error.toString(), 'queryError'); + } + + logQuerySlow( + time: number, + query: string, + parameters?: unknown[], + _?: QueryRunner, + ): void { + const sql = + query + + (parameters && parameters.length + ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` + : ''); + /* eslint-disable local-rules/correct-logger-context */ + this.logger.warn(PlatformTools.highlightSql(sql), 'querySlow'); + this.logger.warn(`execution time: ${time}`, 'querySlow'); + /* eslint-enable local-rules/correct-logger-context */ + } + + logSchemaBuild(message: string, _?: QueryRunner): void { + // eslint-disable-next-line local-rules/correct-logger-context + this.logger.debug(message, 'schemaBuild'); + } + + /** + * Converts parameters to a string. + * Sometimes parameters can have circular objects and therefore we are handle this case too. + */ + protected stringifyParams(parameters: unknown[]): string { + try { + return JSON.stringify(parameters); + } catch (error) { + // most probably circular objects in parameters + return parameters.toString(); + } + } +}