Skip to content

Commit

Permalink
fix: refactor pino log (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
lamngockhuong authored Nov 12, 2024
1 parent 18c64df commit fb657c6
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 44 deletions.
8 changes: 7 additions & 1 deletion apps/admin-api/src/api/article/article.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ArticleEntity } from '@repo/database-typeorm';
import { ArticleController } from './article.controller';
import { ArticleService } from './article.service';
import { CommentModule } from './comment/comment.module';
Expand All @@ -7,6 +9,10 @@ import { FavoriteModule } from './favorite/favorite.module';
@Module({
controllers: [ArticleController],
providers: [ArticleService],
imports: [CommentModule, FavoriteModule],
imports: [
CommentModule,
FavoriteModule,
TypeOrmModule.forFeature([ArticleEntity]),
],
})
export class ArticleModule {}
10 changes: 9 additions & 1 deletion apps/admin-api/src/database/typeorm-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AllConfigType } from '@/config/config.type';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { TypeOrmCustomLogger } from '@repo/database-typeorm';
import { join } from 'path';

@Injectable()
Expand All @@ -24,7 +25,14 @@ export class TypeOrmConfigService implements TypeOrmOptionsFactory {
}),
dropSchema: false,
keepConnectionAlive: true,
logging: this.configService.get('database.logging', { infer: true }),
// Only use logging or logger
// logging: this.configService.get('database.logging', { infer: true }),
logger: TypeOrmCustomLogger.getInstance(
'default',
this.configService.get('database.logging', { infer: true })
? ['error', 'warn', 'query', 'schema']
: ['error', 'warn'],
),
entities: [join(nodeModulesDir, 'dist', '**', '*.entity.{ts,js}')],
poolSize: this.configService.get('database.maxConnections', {
infer: true,
Expand Down
1 change: 1 addition & 0 deletions packages/database-typeorm/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import databaseConfig from './database.config';
export * from './database-config.type';
export * from './typeorm-custom-logger';

export { databaseConfig };
208 changes: 208 additions & 0 deletions packages/database-typeorm/src/config/typeorm-custom-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Logger } from '@nestjs/common';
import {
QueryRunner,
Logger as TypeOrmLogger,
type LogLevel,
type LogMessageType,
type LoggerOptions,
} from 'typeorm';

export class TypeOrmCustomLogger implements TypeOrmLogger {
static getInstance(connectionName: string, options: LoggerOptions) {
const logger = new Logger(`TypeORM[${connectionName}]`);
return new TypeOrmCustomLogger(logger, options);
}

constructor(
private readonly logger: Logger,
private readonly options: LoggerOptions,
) {}

/**
* Logs query and parameters used in it.
*/
logQuery(query: string, parameters?: any[], _queryRunner?: QueryRunner) {
if (!this.isLogEnabledFor('query')) {
return;
}

const sql =
query +
(parameters && parameters.length
? ' -- PARAMETERS: ' + this.stringifyParams(parameters)
: '');
this.logger.log(`query: ${sql}`);
}

/**
* Logs query that is failed.
*/
logQueryError(
error: string,
query: string,
parameters?: any[],
_queryRunner?: QueryRunner,
) {
if (!this.isLogEnabledFor('query-error')) {
return;
}

const sql =
query +
(parameters && parameters.length
? ' -- PARAMETERS: ' + this.stringifyParams(parameters)
: '');
this.logger.error(`query failed: ${sql}`);
this.logger.error(`error:`, error);
}

/**
* Logs query that is slow.
*/
logQuerySlow(
time: number,
query: string,
parameters?: any[],
_queryRunner?: QueryRunner,
) {
if (!this.isLogEnabledFor('query-slow')) {
return;
}

const sql =
query +
(parameters && parameters.length
? ' -- PARAMETERS: ' + this.stringifyParams(parameters)
: '');
this.logger.warn(`query is slow: ${sql}`);
this.logger.warn(`execution time: ${time}`);
}

/**
* Logs events from the schema build process.
*/
logSchemaBuild(message: string, _queryRunner?: QueryRunner) {
if (!this.isLogEnabledFor('schema-build')) {
return;
}

this.logger.log(message);
}

/**
* Logs events from the migrations run process.
*/
logMigration(message: string, _queryRunner?: QueryRunner) {
if (!this.isLogEnabledFor('migration')) {
return;
}

this.logger.log(message);
}

/**
* Perform logging using given logger, or by default to the this.logger.
* Log has its own level and message.
*/
log(
level: 'log' | 'info' | 'warn',
message: any,
_queryRunner?: QueryRunner,
) {
switch (level) {
case 'log':
if (!this.isLogEnabledFor('log')) {
return;
}

this.logger.log(message);
break;
case 'info':
if (!this.isLogEnabledFor('info')) {
return;
}

this.logger.log(message);
break;
case 'warn':
if (!this.isLogEnabledFor('warn')) {
return;
}

this.logger.warn(message);
break;
}
}

/**
* Check is logging for level or message type is enabled.
*/
protected isLogEnabledFor(type?: LogLevel | LogMessageType) {
switch (type) {
case 'query':
return (
this.options === 'all' ||
this.options === true ||
(Array.isArray(this.options) && this.options.indexOf('query') !== -1)
);

case 'error':
case 'query-error':
return (
this.options === 'all' ||
this.options === true ||
(Array.isArray(this.options) && this.options.indexOf('error') !== -1)
);

case 'query-slow':
return true;

case 'schema':
case 'schema-build':
return (
this.options === 'all' ||
(Array.isArray(this.options) && this.options.indexOf('schema') !== -1)
);

case 'migration':
return true;

case 'log':
return (
this.options === 'all' ||
(Array.isArray(this.options) && this.options.indexOf('log') !== -1)
);

case 'info':
return (
this.options === 'all' ||
(Array.isArray(this.options) && this.options.indexOf('info') !== -1)
);

case 'warn':
return (
this.options === 'all' ||
(Array.isArray(this.options) && this.options.indexOf('warn') !== -1)
);

default:
return false;
}
}

/**
* Converts parameters to a string.
* Sometimes parameters can have circular objects and therefor we are handle this case too.
*/
protected stringifyParams(parameters: any[]) {
try {
return JSON.stringify(parameters);
} catch (error) {
// most probably circular objects in parameters
return parameters;
}
}
}

export default TypeOrmCustomLogger;
Loading

0 comments on commit fb657c6

Please sign in to comment.