diff --git a/src/client.ts b/src/client.ts index a6f40c8..a663db6 100644 --- a/src/client.ts +++ b/src/client.ts @@ -67,7 +67,7 @@ export class RDSClient extends Operator { constructor(options: RDSClientOptions) { super(); options.connectTimeout = options.connectTimeout ?? 500; - const { connectionStorage, connectionStorageKey, poolWaitTimeout, ...mysqlOptions } = options; + const { connectionStorage, connectionStorageKey, poolWaitTimeout, logging, ...mysqlOptions } = options; // get connection options from getConnectionConfig method every time if (mysqlOptions.getConnectionConfig) { this.#pool = new Pool({ config: new RDSPoolConfig(mysqlOptions, mysqlOptions.getConnectionConfig) } as any) as unknown as PoolPromisify; @@ -108,6 +108,7 @@ export class RDSClient extends Operator { connection, } as ConnectionMessage); }); + this.logging = logging; } async query(sql: string, values?: object | any[], options?: QueryOptions): Promise { @@ -177,6 +178,7 @@ export class RDSClient extends Operator { try { const _conn = await this.getConnectionWithTimeout(); const conn = new RDSConnection(_conn); + conn.setLogging(this.logging); if (this.beforeQueryHandlers.length > 0) { for (const handler of this.beforeQueryHandlers) { conn.beforeQuery(handler); diff --git a/src/operator.ts b/src/operator.ts index 95d1067..4562cff 100644 --- a/src/operator.ts +++ b/src/operator.ts @@ -9,6 +9,7 @@ import { SelectOption, UpdateOption, UpdateResult, UpdateRow, PoolConnectionPromisify, + Logging, } from './types'; import channels from './channels'; import type { QueryStartMessage, QueryEndMessage } from './channels'; @@ -20,6 +21,8 @@ const debug = debuglog('ali-rds:operator'); */ export abstract class Operator { #connection: PoolConnectionPromisify; + logging?: Logging; + constructor(connection?: PoolConnectionPromisify) { if (connection) { this.#connection = connection; @@ -43,6 +46,10 @@ export abstract class Operator { this.afterQueryHandlers.push(afterQueryHandler); } + setLogging(logging?: Logging) { + this.logging = logging; + } + escape(value: any, stringifyObjects?: boolean, timeZone?: string): string { return SqlString.escape(value, stringifyObjects, timeZone); } @@ -80,6 +87,9 @@ export abstract class Operator { } } debug('[connection#%s] query %o', this.threadId, sql); + if (typeof this.logging === 'function') { + this.logging(sql, { threadId: this.threadId }); + } const queryStart = performance.now(); let rows: any; let lastError: Error | undefined; diff --git a/src/types.ts b/src/types.ts index f273a35..695ee20 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,6 +9,7 @@ export interface RDSClientOptions extends PoolOptions { connectionStorage?: AsyncLocalStorage>; getConnectionConfig?: GetConnectionConfig; poolWaitTimeout?: number; + logging?: Logging; } export interface PoolConnectionPromisify extends Omit { @@ -67,3 +68,5 @@ export type AfterQueryHandler = (sql: string, result: any, execDuration: number, export type TransactionContext = Record; export type TransactionScope = (transaction: RDSTransaction) => Promise; + +export type Logging = (message: string, ...args: any[]) => any; diff --git a/test/client.test.ts b/test/client.test.ts index c614e6c..b1da0a5 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -338,6 +338,30 @@ describe('test/client.test.ts', () => { }); }); + describe('logging', () => { + const mockLogs: string[] = []; + + it('should logging sql', async () => { + const db = new RDSClient({ + ...config, + logging: (sql, { threadId }) => { + assert.equal(typeof threadId, 'number'); + mockLogs.push(sql); + }, + }); + + await db.query('show tables'); + assert.deepEqual(mockLogs, [ 'show tables' ]); + // logging SQL string with variable replaced + await db.query('select * from ?? where email = ? limit 1', + [ table, prefix + 'm@fengmk2.com' ]); + assert.deepEqual(mockLogs, [ + 'show tables', + `select * from \`${table}\` where email = '${prefix + 'm@fengmk2.com'}' limit 1`, + ]); + }); + }); + describe('beginTransactionScope(scope)', () => { it('should beginTransactionScope() error', async () => { const failDB = new RDSClient({