diff --git a/src/client.ts b/src/client.ts index be67505..2a3188c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -107,6 +107,7 @@ export class RDSClient extends Operator { connection, } as ConnectionMessage); }); + this.logging = options.logging; } async query(sql: string, values?: object | any[]): Promise { @@ -162,6 +163,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 dce6269..4539b17 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 0a999f6..4a4ab5c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,6 +9,7 @@ export interface RDSClientOptions extends PoolConfig { 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 f52e257..17369af 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(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({