diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 7ebec3b..8540d22 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -104,7 +104,8 @@ import { winstonLoggerOptions } from '../logger/services/winston.logger'; ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), - logging: true, + logging: ['query', 'error', 'warn', 'schema', 'migration'], + logger: typeOrmLogger, extra: { charset: 'utf8mb4', diff --git a/libs/common/src/logger/services/typeorm.logger.ts b/libs/common/src/logger/services/typeorm.logger.ts index 098959b..bd5a14a 100644 --- a/libs/common/src/logger/services/typeorm.logger.ts +++ b/libs/common/src/logger/services/typeorm.logger.ts @@ -2,48 +2,56 @@ import { Logger as WinstonLogger } from 'winston'; import { Logger as TypeOrmLogger } from 'typeorm'; import { requestContext } from '@app/common/context/request-context'; +const ERROR_THRESHOLD = 2000; + export class TypeOrmWinstonLogger implements TypeOrmLogger { - constructor( - private readonly logger: WinstonLogger, - private readonly slowQueryThreshold = 500, // ms - ) {} + constructor(private readonly logger: WinstonLogger) {} + + private getContext() { + const context = requestContext.getStore(); + return { + requestId: context?.requestId ?? 'N/A', + userId: context?.userId ?? null, + }; + } + + private extractTable(query: string): string { + const match = + query.match(/from\s+["`]?(\w+)["`]?/i) || + query.match(/into\s+["`]?(\w+)["`]?/i); + return match?.[1] ?? 'unknown'; + } logQuery(query: string, parameters?: any[]) { - const context = requestContext.getStore(); - const requestId = context?.requestId ?? 'N/A'; - const start = Date.now(); - - const timeout = setTimeout(() => { - const duration = Date.now() - start; - - const isSlow = duration > this.slowQueryThreshold; - this.logger[isSlow ? 'warn' : 'debug'](`[DB][QUERY] ${query}`, { - requestId, - parameters, - duration: `${duration}ms`, - isSlow, - }); - }, 0); - - // Just ensures the setTimeout fires after this function returns - clearTimeout(timeout); + const context = this.getContext(); + this.logger.debug(`[DB][QUERY] ${query}`, { + ...context, + table: this.extractTable(query), + parameters, + }); } logQueryError(error: string | Error, query: string, parameters?: any[]) { - const requestId = requestContext.getStore()?.requestId ?? 'N/A'; + const context = this.getContext(); this.logger.error(`[DB][ERROR] ${query}`, { - requestId, + ...context, + table: this.extractTable(query), parameters, error, }); } logQuerySlow(time: number, query: string, parameters?: any[]) { - const requestId = requestContext.getStore()?.requestId ?? 'N/A'; - this.logger.warn(`🔥 [DB][SLOW > ${time}ms] ${query}`, { - requestId, + const context = this.getContext(); + const severity = time > ERROR_THRESHOLD ? 'error' : 'warn'; + const label = severity === 'error' ? 'VERY SLOW' : 'SLOW'; + + this.logger[severity](`[DB][${label} > ${time}ms] ${query}`, { + ...context, + table: this.extractTable(query), parameters, - time, + duration: `${time}ms`, + severity, }); } diff --git a/libs/common/src/logger/services/winston.logger.ts b/libs/common/src/logger/services/winston.logger.ts index 7f0e806..34b6a75 100644 --- a/libs/common/src/logger/services/winston.logger.ts +++ b/libs/common/src/logger/services/winston.logger.ts @@ -2,7 +2,8 @@ import { utilities as nestWinstonModuleUtilities } from 'nest-winston'; import * as winston from 'winston'; export const winstonLoggerOptions: winston.LoggerOptions = { - level: 'debug', + level: + process.env.AZURE_POSTGRESQL_DATABASE === 'development' ? 'debug' : 'error', transports: [ new winston.transports.Console({ format: winston.format.combine(