trickle-observe 0.2.106 → 0.2.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db-observer.d.ts +28 -0
- package/dist/db-observer.js +340 -0
- package/dist/observe-register.js +45 -0
- package/package.json +1 -1
- package/src/db-observer.ts +361 -0
- package/src/observe-register.ts +45 -0
package/dist/db-observer.d.ts
CHANGED
|
@@ -26,6 +26,34 @@ export declare function patchBetterSqlite3(dbConstructor: any, debug: boolean):
|
|
|
26
26
|
* Called from observe-register when ioredis is required.
|
|
27
27
|
*/
|
|
28
28
|
export declare function patchIoredis(ioredisModule: any, debug: boolean): void;
|
|
29
|
+
/**
|
|
30
|
+
* Patch @prisma/client to capture queries.
|
|
31
|
+
* Prisma has its own query engine (Rust binary) so patching pg/mysql2 won't work.
|
|
32
|
+
* Instead, we hook into Prisma's $on('query') event and $use() middleware.
|
|
33
|
+
*/
|
|
34
|
+
export declare function patchPrisma(prismaModule: any, debug: boolean): void;
|
|
35
|
+
/**
|
|
36
|
+
* Patch Drizzle ORM to capture queries.
|
|
37
|
+
* Drizzle exposes a logger option in its constructor.
|
|
38
|
+
* We patch the db object's execute method to capture queries.
|
|
39
|
+
*/
|
|
40
|
+
export declare function patchDrizzle(drizzleModule: any, debug: boolean): void;
|
|
41
|
+
/**
|
|
42
|
+
* Patch Knex to capture queries.
|
|
43
|
+
* Knex emits 'query' and 'query-response' events on the knex instance.
|
|
44
|
+
*/
|
|
45
|
+
export declare function patchKnex(knexModule: any, debug: boolean): void;
|
|
46
|
+
/**
|
|
47
|
+
* Patch TypeORM to capture queries via its Logger interface.
|
|
48
|
+
* TypeORM DataSource accepts a `logger` option. We wrap createConnection/DataSource
|
|
49
|
+
* to inject a custom logger that captures all queries.
|
|
50
|
+
*/
|
|
51
|
+
export declare function patchTypeORM(typeormModule: any, debug: boolean): void;
|
|
52
|
+
/**
|
|
53
|
+
* Patch Sequelize to capture queries via its logging option.
|
|
54
|
+
* Sequelize accepts a `logging` function in its constructor options.
|
|
55
|
+
*/
|
|
56
|
+
export declare function patchSequelize(sequelizeModule: any, debug: boolean): void;
|
|
29
57
|
/**
|
|
30
58
|
* Patch mongoose to capture MongoDB operations.
|
|
31
59
|
* Called from observe-register when mongoose is required.
|
package/dist/db-observer.js
CHANGED
|
@@ -47,6 +47,11 @@ exports.patchPg = patchPg;
|
|
|
47
47
|
exports.patchMysql2 = patchMysql2;
|
|
48
48
|
exports.patchBetterSqlite3 = patchBetterSqlite3;
|
|
49
49
|
exports.patchIoredis = patchIoredis;
|
|
50
|
+
exports.patchPrisma = patchPrisma;
|
|
51
|
+
exports.patchDrizzle = patchDrizzle;
|
|
52
|
+
exports.patchKnex = patchKnex;
|
|
53
|
+
exports.patchTypeORM = patchTypeORM;
|
|
54
|
+
exports.patchSequelize = patchSequelize;
|
|
50
55
|
exports.patchMongoose = patchMongoose;
|
|
51
56
|
const fs = __importStar(require("fs"));
|
|
52
57
|
const path = __importStar(require("path"));
|
|
@@ -333,6 +338,341 @@ function patchIoredis(ioredisModule, debug) {
|
|
|
333
338
|
if (debug)
|
|
334
339
|
console.log('[trickle/db] Redis (ioredis) query tracing enabled');
|
|
335
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Patch @prisma/client to capture queries.
|
|
343
|
+
* Prisma has its own query engine (Rust binary) so patching pg/mysql2 won't work.
|
|
344
|
+
* Instead, we hook into Prisma's $on('query') event and $use() middleware.
|
|
345
|
+
*/
|
|
346
|
+
function patchPrisma(prismaModule, debug) {
|
|
347
|
+
debugMode = debug;
|
|
348
|
+
const PrismaClient = prismaModule.PrismaClient;
|
|
349
|
+
if (!PrismaClient || PrismaClient.__trickle_patched)
|
|
350
|
+
return;
|
|
351
|
+
const originalConstructor = PrismaClient;
|
|
352
|
+
const OriginalPrototype = PrismaClient.prototype;
|
|
353
|
+
// Wrap the constructor to inject query logging
|
|
354
|
+
const patchedConstructor = function (opts = {}) {
|
|
355
|
+
// Enable query logging in Prisma
|
|
356
|
+
if (!opts.log)
|
|
357
|
+
opts.log = [];
|
|
358
|
+
const logEntries = Array.isArray(opts.log) ? opts.log : [];
|
|
359
|
+
// Add query event if not already present
|
|
360
|
+
const hasQueryLog = logEntries.some((entry) => (typeof entry === 'string' && entry === 'query') ||
|
|
361
|
+
(typeof entry === 'object' && entry.emit === 'event' && entry.level === 'query'));
|
|
362
|
+
if (!hasQueryLog) {
|
|
363
|
+
logEntries.push({ emit: 'event', level: 'query' });
|
|
364
|
+
}
|
|
365
|
+
opts.log = logEntries;
|
|
366
|
+
// Call original constructor
|
|
367
|
+
const instance = new originalConstructor(opts);
|
|
368
|
+
// Subscribe to query events
|
|
369
|
+
try {
|
|
370
|
+
instance.$on('query', (e) => {
|
|
371
|
+
const queryText = (e.query || '').substring(0, MAX_QUERY_LENGTH);
|
|
372
|
+
const params = e.params ? (() => { try {
|
|
373
|
+
return JSON.parse(e.params).slice(0, 5);
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
return undefined;
|
|
377
|
+
} })() : undefined;
|
|
378
|
+
writeQuery({
|
|
379
|
+
kind: 'query',
|
|
380
|
+
query: queryText,
|
|
381
|
+
params,
|
|
382
|
+
durationMs: e.duration || 0,
|
|
383
|
+
rowCount: 0, // Prisma query events don't include row count
|
|
384
|
+
timestamp: Date.now(),
|
|
385
|
+
});
|
|
386
|
+
if (debugMode) {
|
|
387
|
+
console.log(`[trickle/db] Prisma: ${queryText.substring(0, 60)}... (${e.duration}ms)`);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
catch { /* $on may not be available on all Prisma versions */ }
|
|
392
|
+
return instance;
|
|
393
|
+
};
|
|
394
|
+
// Copy prototype chain
|
|
395
|
+
patchedConstructor.prototype = OriginalPrototype;
|
|
396
|
+
Object.setPrototypeOf(patchedConstructor, originalConstructor);
|
|
397
|
+
// Copy static properties
|
|
398
|
+
for (const key of Object.getOwnPropertyNames(originalConstructor)) {
|
|
399
|
+
if (key !== 'prototype' && key !== 'length' && key !== 'name') {
|
|
400
|
+
try {
|
|
401
|
+
Object.defineProperty(patchedConstructor, key, Object.getOwnPropertyDescriptor(originalConstructor, key));
|
|
402
|
+
}
|
|
403
|
+
catch { }
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
prismaModule.PrismaClient = patchedConstructor;
|
|
407
|
+
prismaModule.PrismaClient.__trickle_patched = true;
|
|
408
|
+
if (debug)
|
|
409
|
+
console.log('[trickle/db] Prisma query tracing enabled');
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Patch Drizzle ORM to capture queries.
|
|
413
|
+
* Drizzle exposes a logger option in its constructor.
|
|
414
|
+
* We patch the db object's execute method to capture queries.
|
|
415
|
+
*/
|
|
416
|
+
function patchDrizzle(drizzleModule, debug) {
|
|
417
|
+
debugMode = debug;
|
|
418
|
+
// drizzle-orm exports various dialect-specific functions
|
|
419
|
+
// The common pattern is drizzle(client, { logger: true })
|
|
420
|
+
// We patch by wrapping the module's default export or named exports
|
|
421
|
+
for (const key of Object.keys(drizzleModule)) {
|
|
422
|
+
const fn = drizzleModule[key];
|
|
423
|
+
if (typeof fn !== 'function')
|
|
424
|
+
continue;
|
|
425
|
+
if (fn.__trickle_patched)
|
|
426
|
+
continue;
|
|
427
|
+
// Only patch functions that look like drizzle constructors
|
|
428
|
+
// (they take a client + config object)
|
|
429
|
+
drizzleModule[key] = function patchedDrizzle(...args) {
|
|
430
|
+
// Inject a custom logger into the config
|
|
431
|
+
const config = args[1] && typeof args[1] === 'object' ? { ...args[1] } : (args.length > 1 ? args[1] : {});
|
|
432
|
+
if (typeof config === 'object' && config !== null) {
|
|
433
|
+
const origLogger = config.logger;
|
|
434
|
+
config.logger = {
|
|
435
|
+
logQuery(query, params) {
|
|
436
|
+
const startTime = performance.now();
|
|
437
|
+
writeQuery({
|
|
438
|
+
kind: 'query',
|
|
439
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
440
|
+
params: Array.isArray(params) ? params.slice(0, 5) : undefined,
|
|
441
|
+
durationMs: 0, // Drizzle logger doesn't provide timing
|
|
442
|
+
rowCount: 0,
|
|
443
|
+
timestamp: Date.now(),
|
|
444
|
+
});
|
|
445
|
+
// Call original logger if present
|
|
446
|
+
if (origLogger && typeof origLogger === 'object' && 'logQuery' in origLogger) {
|
|
447
|
+
origLogger.logQuery(query, params);
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
args[1] = config;
|
|
452
|
+
}
|
|
453
|
+
return fn.apply(this, args);
|
|
454
|
+
};
|
|
455
|
+
drizzleModule[key].__trickle_patched = true;
|
|
456
|
+
}
|
|
457
|
+
if (debug)
|
|
458
|
+
console.log('[trickle/db] Drizzle ORM query tracing enabled');
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Patch Knex to capture queries.
|
|
462
|
+
* Knex emits 'query' and 'query-response' events on the knex instance.
|
|
463
|
+
*/
|
|
464
|
+
function patchKnex(knexModule, debug) {
|
|
465
|
+
debugMode = debug;
|
|
466
|
+
const origKnex = knexModule.default || knexModule;
|
|
467
|
+
if (typeof origKnex !== 'function' || origKnex.__trickle_patched)
|
|
468
|
+
return;
|
|
469
|
+
const wrapped = function patchedKnex(...args) {
|
|
470
|
+
const instance = origKnex.apply(this, args);
|
|
471
|
+
// Track query start times
|
|
472
|
+
const queryTimers = new Map();
|
|
473
|
+
try {
|
|
474
|
+
instance.on('query', (data) => {
|
|
475
|
+
queryTimers.set(data.__knexQueryUid || '', performance.now());
|
|
476
|
+
});
|
|
477
|
+
instance.on('query-response', (_response, data) => {
|
|
478
|
+
const startTime = queryTimers.get(data.__knexQueryUid || '');
|
|
479
|
+
const durationMs = startTime ? Math.round((performance.now() - startTime) * 100) / 100 : 0;
|
|
480
|
+
queryTimers.delete(data.__knexQueryUid || '');
|
|
481
|
+
writeQuery({
|
|
482
|
+
kind: 'query',
|
|
483
|
+
query: (data.sql || '').substring(0, MAX_QUERY_LENGTH),
|
|
484
|
+
params: Array.isArray(data.bindings) ? data.bindings.slice(0, 5) : undefined,
|
|
485
|
+
durationMs,
|
|
486
|
+
rowCount: 0,
|
|
487
|
+
timestamp: Date.now(),
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
instance.on('query-error', (error, data) => {
|
|
491
|
+
const startTime = queryTimers.get(data.__knexQueryUid || '');
|
|
492
|
+
const durationMs = startTime ? Math.round((performance.now() - startTime) * 100) / 100 : 0;
|
|
493
|
+
queryTimers.delete(data.__knexQueryUid || '');
|
|
494
|
+
writeQuery({
|
|
495
|
+
kind: 'query',
|
|
496
|
+
query: (data.sql || '').substring(0, MAX_QUERY_LENGTH),
|
|
497
|
+
durationMs,
|
|
498
|
+
rowCount: 0,
|
|
499
|
+
error: error?.message?.substring(0, 200),
|
|
500
|
+
timestamp: Date.now(),
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
catch { /* query events not available */ }
|
|
505
|
+
return instance;
|
|
506
|
+
};
|
|
507
|
+
// Copy properties
|
|
508
|
+
Object.setPrototypeOf(wrapped, origKnex);
|
|
509
|
+
for (const key of Object.getOwnPropertyNames(origKnex)) {
|
|
510
|
+
if (key !== 'length' && key !== 'name') {
|
|
511
|
+
try {
|
|
512
|
+
Object.defineProperty(wrapped, key, Object.getOwnPropertyDescriptor(origKnex, key));
|
|
513
|
+
}
|
|
514
|
+
catch { }
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (knexModule.default) {
|
|
518
|
+
knexModule.default = wrapped;
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
// knex exports the function directly via module.exports
|
|
522
|
+
// We can't replace module.exports from here, but observe-register handles that
|
|
523
|
+
}
|
|
524
|
+
wrapped.__trickle_patched = true;
|
|
525
|
+
if (debug)
|
|
526
|
+
console.log('[trickle/db] Knex query tracing enabled');
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Patch TypeORM to capture queries via its Logger interface.
|
|
530
|
+
* TypeORM DataSource accepts a `logger` option. We wrap createConnection/DataSource
|
|
531
|
+
* to inject a custom logger that captures all queries.
|
|
532
|
+
*/
|
|
533
|
+
function patchTypeORM(typeormModule, debug) {
|
|
534
|
+
debugMode = debug;
|
|
535
|
+
// Patch DataSource constructor (TypeORM 0.3+)
|
|
536
|
+
const DataSource = typeormModule.DataSource;
|
|
537
|
+
if (DataSource && !DataSource.__trickle_patched) {
|
|
538
|
+
const OrigDataSource = DataSource;
|
|
539
|
+
typeormModule.DataSource = function PatchedDataSource(options) {
|
|
540
|
+
// Inject custom logger
|
|
541
|
+
if (!options._trickle_injected) {
|
|
542
|
+
options._trickle_injected = true;
|
|
543
|
+
const origLogger = options.logger;
|
|
544
|
+
options.logger = {
|
|
545
|
+
logQuery(query, parameters) {
|
|
546
|
+
writeQuery({
|
|
547
|
+
kind: 'query',
|
|
548
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
549
|
+
params: parameters?.slice(0, 5),
|
|
550
|
+
durationMs: 0,
|
|
551
|
+
rowCount: 0,
|
|
552
|
+
timestamp: Date.now(),
|
|
553
|
+
});
|
|
554
|
+
if (origLogger && typeof origLogger === 'object' && 'logQuery' in origLogger) {
|
|
555
|
+
origLogger.logQuery(query, parameters);
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
logQueryError(error, query, parameters) {
|
|
559
|
+
writeQuery({
|
|
560
|
+
kind: 'query',
|
|
561
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
562
|
+
params: parameters?.slice(0, 5),
|
|
563
|
+
durationMs: 0,
|
|
564
|
+
rowCount: 0,
|
|
565
|
+
error: error.substring(0, 200),
|
|
566
|
+
timestamp: Date.now(),
|
|
567
|
+
});
|
|
568
|
+
},
|
|
569
|
+
logQuerySlow(time, query, parameters) {
|
|
570
|
+
writeQuery({
|
|
571
|
+
kind: 'query',
|
|
572
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
573
|
+
params: parameters?.slice(0, 5),
|
|
574
|
+
durationMs: time,
|
|
575
|
+
rowCount: 0,
|
|
576
|
+
timestamp: Date.now(),
|
|
577
|
+
});
|
|
578
|
+
},
|
|
579
|
+
logSchemaBuild() { },
|
|
580
|
+
logMigration() { },
|
|
581
|
+
log() { },
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
return new OrigDataSource(options);
|
|
585
|
+
};
|
|
586
|
+
// Copy statics
|
|
587
|
+
Object.setPrototypeOf(typeormModule.DataSource, OrigDataSource);
|
|
588
|
+
typeormModule.DataSource.prototype = OrigDataSource.prototype;
|
|
589
|
+
typeormModule.DataSource.__trickle_patched = true;
|
|
590
|
+
}
|
|
591
|
+
// Also patch createConnection (TypeORM 0.2)
|
|
592
|
+
if (typeormModule.createConnection && !typeormModule.createConnection.__trickle_patched) {
|
|
593
|
+
const origCreate = typeormModule.createConnection;
|
|
594
|
+
typeormModule.createConnection = function patchedCreateConnection(options) {
|
|
595
|
+
if (options && typeof options === 'object' && !options.logging) {
|
|
596
|
+
options.logging = true;
|
|
597
|
+
}
|
|
598
|
+
return origCreate(options);
|
|
599
|
+
};
|
|
600
|
+
typeormModule.createConnection.__trickle_patched = true;
|
|
601
|
+
}
|
|
602
|
+
if (debug)
|
|
603
|
+
console.log('[trickle/db] TypeORM query tracing enabled');
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Patch Sequelize to capture queries via its logging option.
|
|
607
|
+
* Sequelize accepts a `logging` function in its constructor options.
|
|
608
|
+
*/
|
|
609
|
+
function patchSequelize(sequelizeModule, debug) {
|
|
610
|
+
debugMode = debug;
|
|
611
|
+
const Sequelize = sequelizeModule.Sequelize || sequelizeModule.default || sequelizeModule;
|
|
612
|
+
if (!Sequelize || typeof Sequelize !== 'function' || Sequelize.__trickle_patched)
|
|
613
|
+
return;
|
|
614
|
+
const origConstructor = Sequelize;
|
|
615
|
+
const patchedSequelize = function (...args) {
|
|
616
|
+
// Sequelize(uri, options) or Sequelize(database, user, pass, options)
|
|
617
|
+
let options;
|
|
618
|
+
if (args.length >= 4) {
|
|
619
|
+
options = args[3] = args[3] || {};
|
|
620
|
+
}
|
|
621
|
+
else if (args.length >= 2 && typeof args[1] === 'object') {
|
|
622
|
+
options = args[1];
|
|
623
|
+
}
|
|
624
|
+
else if (args.length === 1 && typeof args[0] === 'object') {
|
|
625
|
+
options = args[0];
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
options = {};
|
|
629
|
+
args.push(options);
|
|
630
|
+
}
|
|
631
|
+
// Wrap the logging function
|
|
632
|
+
const origLogging = options.logging;
|
|
633
|
+
options.logging = (sql, timing) => {
|
|
634
|
+
const queryText = typeof sql === 'string' ? sql : String(sql);
|
|
635
|
+
// Sequelize prepends "Executed (default): " or "Executing (default): " to queries
|
|
636
|
+
const cleanQuery = queryText.replace(/^Execut(?:ed|ing) \([^)]*\):\s*/, '');
|
|
637
|
+
const durationMs = typeof timing === 'number' ? timing : 0;
|
|
638
|
+
writeQuery({
|
|
639
|
+
kind: 'query',
|
|
640
|
+
query: cleanQuery.substring(0, MAX_QUERY_LENGTH),
|
|
641
|
+
durationMs,
|
|
642
|
+
rowCount: 0,
|
|
643
|
+
timestamp: Date.now(),
|
|
644
|
+
});
|
|
645
|
+
// Call original logger
|
|
646
|
+
if (typeof origLogging === 'function') {
|
|
647
|
+
origLogging(sql, timing);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
options.benchmark = true; // Enable timing in log
|
|
651
|
+
// Call original constructor
|
|
652
|
+
if (new.target) {
|
|
653
|
+
return new origConstructor(...args);
|
|
654
|
+
}
|
|
655
|
+
return origConstructor.apply(this, args);
|
|
656
|
+
};
|
|
657
|
+
// Copy prototype and statics
|
|
658
|
+
patchedSequelize.prototype = origConstructor.prototype;
|
|
659
|
+
Object.setPrototypeOf(patchedSequelize, origConstructor);
|
|
660
|
+
for (const key of Object.getOwnPropertyNames(origConstructor)) {
|
|
661
|
+
if (key !== 'prototype' && key !== 'length' && key !== 'name') {
|
|
662
|
+
try {
|
|
663
|
+
Object.defineProperty(patchedSequelize, key, Object.getOwnPropertyDescriptor(origConstructor, key));
|
|
664
|
+
}
|
|
665
|
+
catch { }
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// Replace in module exports
|
|
669
|
+
if (sequelizeModule.Sequelize) {
|
|
670
|
+
sequelizeModule.Sequelize = patchedSequelize;
|
|
671
|
+
}
|
|
672
|
+
patchedSequelize.__trickle_patched = true;
|
|
673
|
+
if (debug)
|
|
674
|
+
console.log('[trickle/db] Sequelize query tracing enabled');
|
|
675
|
+
}
|
|
336
676
|
/**
|
|
337
677
|
* Patch mongoose to capture MongoDB operations.
|
|
338
678
|
* Called from observe-register when mongoose is required.
|
package/dist/observe-register.js
CHANGED
|
@@ -1203,6 +1203,51 @@ if (enabled) {
|
|
|
1203
1203
|
}
|
|
1204
1204
|
catch { /* not critical */ }
|
|
1205
1205
|
}
|
|
1206
|
+
// Prisma ORM
|
|
1207
|
+
if (request === '@prisma/client' && !expressPatched.has('@prisma/client')) {
|
|
1208
|
+
expressPatched.add('@prisma/client');
|
|
1209
|
+
try {
|
|
1210
|
+
const { patchPrisma } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1211
|
+
patchPrisma(exports, debug);
|
|
1212
|
+
}
|
|
1213
|
+
catch { /* not critical */ }
|
|
1214
|
+
}
|
|
1215
|
+
// Drizzle ORM
|
|
1216
|
+
if (request.startsWith('drizzle-orm') && !expressPatched.has('drizzle-orm')) {
|
|
1217
|
+
expressPatched.add('drizzle-orm');
|
|
1218
|
+
try {
|
|
1219
|
+
const { patchDrizzle } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1220
|
+
patchDrizzle(exports, debug);
|
|
1221
|
+
}
|
|
1222
|
+
catch { /* not critical */ }
|
|
1223
|
+
}
|
|
1224
|
+
// Knex query builder
|
|
1225
|
+
if (request === 'knex' && !expressPatched.has('knex')) {
|
|
1226
|
+
expressPatched.add('knex');
|
|
1227
|
+
try {
|
|
1228
|
+
const { patchKnex } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1229
|
+
patchKnex(exports, debug);
|
|
1230
|
+
}
|
|
1231
|
+
catch { /* not critical */ }
|
|
1232
|
+
}
|
|
1233
|
+
// TypeORM
|
|
1234
|
+
if (request === 'typeorm' && !expressPatched.has('typeorm')) {
|
|
1235
|
+
expressPatched.add('typeorm');
|
|
1236
|
+
try {
|
|
1237
|
+
const { patchTypeORM } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1238
|
+
patchTypeORM(exports, debug);
|
|
1239
|
+
}
|
|
1240
|
+
catch { /* not critical */ }
|
|
1241
|
+
}
|
|
1242
|
+
// Sequelize
|
|
1243
|
+
if (request === 'sequelize' && !expressPatched.has('sequelize')) {
|
|
1244
|
+
expressPatched.add('sequelize');
|
|
1245
|
+
try {
|
|
1246
|
+
const { patchSequelize } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1247
|
+
patchSequelize(exports, debug);
|
|
1248
|
+
}
|
|
1249
|
+
catch { /* not critical */ }
|
|
1250
|
+
}
|
|
1206
1251
|
// Redis (ioredis)
|
|
1207
1252
|
if (request === 'ioredis' && !expressPatched.has('ioredis')) {
|
|
1208
1253
|
expressPatched.add('ioredis');
|
package/package.json
CHANGED
package/src/db-observer.ts
CHANGED
|
@@ -332,6 +332,367 @@ export function patchIoredis(ioredisModule: any, debug: boolean): void {
|
|
|
332
332
|
if (debug) console.log('[trickle/db] Redis (ioredis) query tracing enabled');
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
+
/**
|
|
336
|
+
* Patch @prisma/client to capture queries.
|
|
337
|
+
* Prisma has its own query engine (Rust binary) so patching pg/mysql2 won't work.
|
|
338
|
+
* Instead, we hook into Prisma's $on('query') event and $use() middleware.
|
|
339
|
+
*/
|
|
340
|
+
export function patchPrisma(prismaModule: any, debug: boolean): void {
|
|
341
|
+
debugMode = debug;
|
|
342
|
+
|
|
343
|
+
const PrismaClient = prismaModule.PrismaClient;
|
|
344
|
+
if (!PrismaClient || (PrismaClient as any).__trickle_patched) return;
|
|
345
|
+
|
|
346
|
+
const originalConstructor = PrismaClient;
|
|
347
|
+
const OriginalPrototype = PrismaClient.prototype;
|
|
348
|
+
|
|
349
|
+
// Wrap the constructor to inject query logging
|
|
350
|
+
const patchedConstructor = function (this: any, opts: any = {}) {
|
|
351
|
+
// Enable query logging in Prisma
|
|
352
|
+
if (!opts.log) opts.log = [];
|
|
353
|
+
const logEntries = Array.isArray(opts.log) ? opts.log : [];
|
|
354
|
+
|
|
355
|
+
// Add query event if not already present
|
|
356
|
+
const hasQueryLog = logEntries.some((entry: any) =>
|
|
357
|
+
(typeof entry === 'string' && entry === 'query') ||
|
|
358
|
+
(typeof entry === 'object' && entry.emit === 'event' && entry.level === 'query')
|
|
359
|
+
);
|
|
360
|
+
if (!hasQueryLog) {
|
|
361
|
+
logEntries.push({ emit: 'event', level: 'query' });
|
|
362
|
+
}
|
|
363
|
+
opts.log = logEntries;
|
|
364
|
+
|
|
365
|
+
// Call original constructor
|
|
366
|
+
const instance = new originalConstructor(opts);
|
|
367
|
+
|
|
368
|
+
// Subscribe to query events
|
|
369
|
+
try {
|
|
370
|
+
instance.$on('query', (e: any) => {
|
|
371
|
+
const queryText = (e.query || '').substring(0, MAX_QUERY_LENGTH);
|
|
372
|
+
const params = e.params ? (() => { try { return JSON.parse(e.params).slice(0, 5); } catch { return undefined; } })() : undefined;
|
|
373
|
+
writeQuery({
|
|
374
|
+
kind: 'query',
|
|
375
|
+
query: queryText,
|
|
376
|
+
params,
|
|
377
|
+
durationMs: e.duration || 0,
|
|
378
|
+
rowCount: 0, // Prisma query events don't include row count
|
|
379
|
+
timestamp: Date.now(),
|
|
380
|
+
});
|
|
381
|
+
if (debugMode) {
|
|
382
|
+
console.log(`[trickle/db] Prisma: ${queryText.substring(0, 60)}... (${e.duration}ms)`);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
} catch { /* $on may not be available on all Prisma versions */ }
|
|
386
|
+
|
|
387
|
+
return instance;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// Copy prototype chain
|
|
391
|
+
patchedConstructor.prototype = OriginalPrototype;
|
|
392
|
+
Object.setPrototypeOf(patchedConstructor, originalConstructor);
|
|
393
|
+
|
|
394
|
+
// Copy static properties
|
|
395
|
+
for (const key of Object.getOwnPropertyNames(originalConstructor)) {
|
|
396
|
+
if (key !== 'prototype' && key !== 'length' && key !== 'name') {
|
|
397
|
+
try {
|
|
398
|
+
Object.defineProperty(patchedConstructor, key, Object.getOwnPropertyDescriptor(originalConstructor, key)!);
|
|
399
|
+
} catch {}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
prismaModule.PrismaClient = patchedConstructor;
|
|
404
|
+
(prismaModule.PrismaClient as any).__trickle_patched = true;
|
|
405
|
+
|
|
406
|
+
if (debug) console.log('[trickle/db] Prisma query tracing enabled');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Patch Drizzle ORM to capture queries.
|
|
411
|
+
* Drizzle exposes a logger option in its constructor.
|
|
412
|
+
* We patch the db object's execute method to capture queries.
|
|
413
|
+
*/
|
|
414
|
+
export function patchDrizzle(drizzleModule: any, debug: boolean): void {
|
|
415
|
+
debugMode = debug;
|
|
416
|
+
|
|
417
|
+
// drizzle-orm exports various dialect-specific functions
|
|
418
|
+
// The common pattern is drizzle(client, { logger: true })
|
|
419
|
+
// We patch by wrapping the module's default export or named exports
|
|
420
|
+
|
|
421
|
+
for (const key of Object.keys(drizzleModule)) {
|
|
422
|
+
const fn = drizzleModule[key];
|
|
423
|
+
if (typeof fn !== 'function') continue;
|
|
424
|
+
if ((fn as any).__trickle_patched) continue;
|
|
425
|
+
|
|
426
|
+
// Only patch functions that look like drizzle constructors
|
|
427
|
+
// (they take a client + config object)
|
|
428
|
+
drizzleModule[key] = function patchedDrizzle(...args: any[]): any {
|
|
429
|
+
// Inject a custom logger into the config
|
|
430
|
+
const config = args[1] && typeof args[1] === 'object' ? { ...args[1] } : (args.length > 1 ? args[1] : {});
|
|
431
|
+
|
|
432
|
+
if (typeof config === 'object' && config !== null) {
|
|
433
|
+
const origLogger = config.logger;
|
|
434
|
+
config.logger = {
|
|
435
|
+
logQuery(query: string, params: unknown[]): void {
|
|
436
|
+
const startTime = performance.now();
|
|
437
|
+
writeQuery({
|
|
438
|
+
kind: 'query',
|
|
439
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
440
|
+
params: Array.isArray(params) ? params.slice(0, 5) : undefined,
|
|
441
|
+
durationMs: 0, // Drizzle logger doesn't provide timing
|
|
442
|
+
rowCount: 0,
|
|
443
|
+
timestamp: Date.now(),
|
|
444
|
+
});
|
|
445
|
+
// Call original logger if present
|
|
446
|
+
if (origLogger && typeof origLogger === 'object' && 'logQuery' in origLogger) {
|
|
447
|
+
(origLogger as any).logQuery(query, params);
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
args[1] = config;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return fn.apply(this, args);
|
|
455
|
+
};
|
|
456
|
+
(drizzleModule[key] as any).__trickle_patched = true;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (debug) console.log('[trickle/db] Drizzle ORM query tracing enabled');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Patch Knex to capture queries.
|
|
464
|
+
* Knex emits 'query' and 'query-response' events on the knex instance.
|
|
465
|
+
*/
|
|
466
|
+
export function patchKnex(knexModule: any, debug: boolean): void {
|
|
467
|
+
debugMode = debug;
|
|
468
|
+
|
|
469
|
+
const origKnex = knexModule.default || knexModule;
|
|
470
|
+
if (typeof origKnex !== 'function' || (origKnex as any).__trickle_patched) return;
|
|
471
|
+
|
|
472
|
+
const wrapped = function patchedKnex(this: any, ...args: any[]): any {
|
|
473
|
+
const instance = origKnex.apply(this, args);
|
|
474
|
+
|
|
475
|
+
// Track query start times
|
|
476
|
+
const queryTimers = new Map<string, number>();
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
instance.on('query', (data: any) => {
|
|
480
|
+
queryTimers.set(data.__knexQueryUid || '', performance.now());
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
instance.on('query-response', (_response: any, data: any) => {
|
|
484
|
+
const startTime = queryTimers.get(data.__knexQueryUid || '');
|
|
485
|
+
const durationMs = startTime ? Math.round((performance.now() - startTime) * 100) / 100 : 0;
|
|
486
|
+
queryTimers.delete(data.__knexQueryUid || '');
|
|
487
|
+
|
|
488
|
+
writeQuery({
|
|
489
|
+
kind: 'query',
|
|
490
|
+
query: (data.sql || '').substring(0, MAX_QUERY_LENGTH),
|
|
491
|
+
params: Array.isArray(data.bindings) ? data.bindings.slice(0, 5) : undefined,
|
|
492
|
+
durationMs,
|
|
493
|
+
rowCount: 0,
|
|
494
|
+
timestamp: Date.now(),
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
instance.on('query-error', (error: any, data: any) => {
|
|
499
|
+
const startTime = queryTimers.get(data.__knexQueryUid || '');
|
|
500
|
+
const durationMs = startTime ? Math.round((performance.now() - startTime) * 100) / 100 : 0;
|
|
501
|
+
queryTimers.delete(data.__knexQueryUid || '');
|
|
502
|
+
|
|
503
|
+
writeQuery({
|
|
504
|
+
kind: 'query',
|
|
505
|
+
query: (data.sql || '').substring(0, MAX_QUERY_LENGTH),
|
|
506
|
+
durationMs,
|
|
507
|
+
rowCount: 0,
|
|
508
|
+
error: error?.message?.substring(0, 200),
|
|
509
|
+
timestamp: Date.now(),
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
} catch { /* query events not available */ }
|
|
513
|
+
|
|
514
|
+
return instance;
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// Copy properties
|
|
518
|
+
Object.setPrototypeOf(wrapped, origKnex);
|
|
519
|
+
for (const key of Object.getOwnPropertyNames(origKnex)) {
|
|
520
|
+
if (key !== 'length' && key !== 'name') {
|
|
521
|
+
try { Object.defineProperty(wrapped, key, Object.getOwnPropertyDescriptor(origKnex, key)!); } catch {}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (knexModule.default) {
|
|
526
|
+
knexModule.default = wrapped;
|
|
527
|
+
} else {
|
|
528
|
+
// knex exports the function directly via module.exports
|
|
529
|
+
// We can't replace module.exports from here, but observe-register handles that
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
(wrapped as any).__trickle_patched = true;
|
|
533
|
+
if (debug) console.log('[trickle/db] Knex query tracing enabled');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Patch TypeORM to capture queries via its Logger interface.
|
|
538
|
+
* TypeORM DataSource accepts a `logger` option. We wrap createConnection/DataSource
|
|
539
|
+
* to inject a custom logger that captures all queries.
|
|
540
|
+
*/
|
|
541
|
+
export function patchTypeORM(typeormModule: any, debug: boolean): void {
|
|
542
|
+
debugMode = debug;
|
|
543
|
+
|
|
544
|
+
// Patch DataSource constructor (TypeORM 0.3+)
|
|
545
|
+
const DataSource = typeormModule.DataSource;
|
|
546
|
+
if (DataSource && !DataSource.__trickle_patched) {
|
|
547
|
+
const OrigDataSource = DataSource;
|
|
548
|
+
|
|
549
|
+
typeormModule.DataSource = function PatchedDataSource(options: any) {
|
|
550
|
+
// Inject custom logger
|
|
551
|
+
if (!options._trickle_injected) {
|
|
552
|
+
options._trickle_injected = true;
|
|
553
|
+
const origLogger = options.logger;
|
|
554
|
+
|
|
555
|
+
options.logger = {
|
|
556
|
+
logQuery(query: string, parameters?: any[]) {
|
|
557
|
+
writeQuery({
|
|
558
|
+
kind: 'query',
|
|
559
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
560
|
+
params: parameters?.slice(0, 5),
|
|
561
|
+
durationMs: 0,
|
|
562
|
+
rowCount: 0,
|
|
563
|
+
timestamp: Date.now(),
|
|
564
|
+
});
|
|
565
|
+
if (origLogger && typeof origLogger === 'object' && 'logQuery' in origLogger) {
|
|
566
|
+
origLogger.logQuery(query, parameters);
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
logQueryError(error: string, query: string, parameters?: any[]) {
|
|
570
|
+
writeQuery({
|
|
571
|
+
kind: 'query',
|
|
572
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
573
|
+
params: parameters?.slice(0, 5),
|
|
574
|
+
durationMs: 0,
|
|
575
|
+
rowCount: 0,
|
|
576
|
+
error: error.substring(0, 200),
|
|
577
|
+
timestamp: Date.now(),
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
logQuerySlow(time: number, query: string, parameters?: any[]) {
|
|
581
|
+
writeQuery({
|
|
582
|
+
kind: 'query',
|
|
583
|
+
query: query.substring(0, MAX_QUERY_LENGTH),
|
|
584
|
+
params: parameters?.slice(0, 5),
|
|
585
|
+
durationMs: time,
|
|
586
|
+
rowCount: 0,
|
|
587
|
+
timestamp: Date.now(),
|
|
588
|
+
});
|
|
589
|
+
},
|
|
590
|
+
logSchemaBuild() {},
|
|
591
|
+
logMigration() {},
|
|
592
|
+
log() {},
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return new OrigDataSource(options);
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// Copy statics
|
|
600
|
+
Object.setPrototypeOf(typeormModule.DataSource, OrigDataSource);
|
|
601
|
+
typeormModule.DataSource.prototype = OrigDataSource.prototype;
|
|
602
|
+
typeormModule.DataSource.__trickle_patched = true;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Also patch createConnection (TypeORM 0.2)
|
|
606
|
+
if (typeormModule.createConnection && !(typeormModule.createConnection as any).__trickle_patched) {
|
|
607
|
+
const origCreate = typeormModule.createConnection;
|
|
608
|
+
typeormModule.createConnection = function patchedCreateConnection(options: any) {
|
|
609
|
+
if (options && typeof options === 'object' && !options.logging) {
|
|
610
|
+
options.logging = true;
|
|
611
|
+
}
|
|
612
|
+
return origCreate(options);
|
|
613
|
+
};
|
|
614
|
+
(typeormModule.createConnection as any).__trickle_patched = true;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (debug) console.log('[trickle/db] TypeORM query tracing enabled');
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Patch Sequelize to capture queries via its logging option.
|
|
622
|
+
* Sequelize accepts a `logging` function in its constructor options.
|
|
623
|
+
*/
|
|
624
|
+
export function patchSequelize(sequelizeModule: any, debug: boolean): void {
|
|
625
|
+
debugMode = debug;
|
|
626
|
+
|
|
627
|
+
const Sequelize = sequelizeModule.Sequelize || sequelizeModule.default || sequelizeModule;
|
|
628
|
+
if (!Sequelize || typeof Sequelize !== 'function' || (Sequelize as any).__trickle_patched) return;
|
|
629
|
+
|
|
630
|
+
const origConstructor = Sequelize;
|
|
631
|
+
|
|
632
|
+
const patchedSequelize = function (this: any, ...args: any[]) {
|
|
633
|
+
// Sequelize(uri, options) or Sequelize(database, user, pass, options)
|
|
634
|
+
let options: any;
|
|
635
|
+
if (args.length >= 4) {
|
|
636
|
+
options = args[3] = args[3] || {};
|
|
637
|
+
} else if (args.length >= 2 && typeof args[1] === 'object') {
|
|
638
|
+
options = args[1];
|
|
639
|
+
} else if (args.length === 1 && typeof args[0] === 'object') {
|
|
640
|
+
options = args[0];
|
|
641
|
+
} else {
|
|
642
|
+
options = {};
|
|
643
|
+
args.push(options);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Wrap the logging function
|
|
647
|
+
const origLogging = options.logging;
|
|
648
|
+
options.logging = (sql: string, timing?: any) => {
|
|
649
|
+
const queryText = typeof sql === 'string' ? sql : String(sql);
|
|
650
|
+
// Sequelize prepends "Executed (default): " or "Executing (default): " to queries
|
|
651
|
+
const cleanQuery = queryText.replace(/^Execut(?:ed|ing) \([^)]*\):\s*/, '');
|
|
652
|
+
const durationMs = typeof timing === 'number' ? timing : 0;
|
|
653
|
+
|
|
654
|
+
writeQuery({
|
|
655
|
+
kind: 'query',
|
|
656
|
+
query: cleanQuery.substring(0, MAX_QUERY_LENGTH),
|
|
657
|
+
durationMs,
|
|
658
|
+
rowCount: 0,
|
|
659
|
+
timestamp: Date.now(),
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// Call original logger
|
|
663
|
+
if (typeof origLogging === 'function') {
|
|
664
|
+
origLogging(sql, timing);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
options.benchmark = true; // Enable timing in log
|
|
668
|
+
|
|
669
|
+
// Call original constructor
|
|
670
|
+
if (new.target) {
|
|
671
|
+
return new origConstructor(...args);
|
|
672
|
+
}
|
|
673
|
+
return origConstructor.apply(this, args);
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
// Copy prototype and statics
|
|
677
|
+
patchedSequelize.prototype = origConstructor.prototype;
|
|
678
|
+
Object.setPrototypeOf(patchedSequelize, origConstructor);
|
|
679
|
+
for (const key of Object.getOwnPropertyNames(origConstructor)) {
|
|
680
|
+
if (key !== 'prototype' && key !== 'length' && key !== 'name') {
|
|
681
|
+
try {
|
|
682
|
+
Object.defineProperty(patchedSequelize, key, Object.getOwnPropertyDescriptor(origConstructor, key)!);
|
|
683
|
+
} catch {}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Replace in module exports
|
|
688
|
+
if (sequelizeModule.Sequelize) {
|
|
689
|
+
sequelizeModule.Sequelize = patchedSequelize;
|
|
690
|
+
}
|
|
691
|
+
(patchedSequelize as any).__trickle_patched = true;
|
|
692
|
+
|
|
693
|
+
if (debug) console.log('[trickle/db] Sequelize query tracing enabled');
|
|
694
|
+
}
|
|
695
|
+
|
|
335
696
|
/**
|
|
336
697
|
* Patch mongoose to capture MongoDB operations.
|
|
337
698
|
* Called from observe-register when mongoose is required.
|
package/src/observe-register.ts
CHANGED
|
@@ -1188,6 +1188,51 @@ if (enabled) {
|
|
|
1188
1188
|
} catch { /* not critical */ }
|
|
1189
1189
|
}
|
|
1190
1190
|
|
|
1191
|
+
// Prisma ORM
|
|
1192
|
+
if (request === '@prisma/client' && !expressPatched.has('@prisma/client')) {
|
|
1193
|
+
expressPatched.add('@prisma/client');
|
|
1194
|
+
try {
|
|
1195
|
+
const { patchPrisma } = require(path.join(__dirname, 'db-observer.js'));
|
|
1196
|
+
patchPrisma(exports, debug);
|
|
1197
|
+
} catch { /* not critical */ }
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// Drizzle ORM
|
|
1201
|
+
if (request.startsWith('drizzle-orm') && !expressPatched.has('drizzle-orm')) {
|
|
1202
|
+
expressPatched.add('drizzle-orm');
|
|
1203
|
+
try {
|
|
1204
|
+
const { patchDrizzle } = require(path.join(__dirname, 'db-observer.js'));
|
|
1205
|
+
patchDrizzle(exports, debug);
|
|
1206
|
+
} catch { /* not critical */ }
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Knex query builder
|
|
1210
|
+
if (request === 'knex' && !expressPatched.has('knex')) {
|
|
1211
|
+
expressPatched.add('knex');
|
|
1212
|
+
try {
|
|
1213
|
+
const { patchKnex } = require(path.join(__dirname, 'db-observer.js'));
|
|
1214
|
+
patchKnex(exports, debug);
|
|
1215
|
+
} catch { /* not critical */ }
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// TypeORM
|
|
1219
|
+
if (request === 'typeorm' && !expressPatched.has('typeorm')) {
|
|
1220
|
+
expressPatched.add('typeorm');
|
|
1221
|
+
try {
|
|
1222
|
+
const { patchTypeORM } = require(path.join(__dirname, 'db-observer.js'));
|
|
1223
|
+
patchTypeORM(exports, debug);
|
|
1224
|
+
} catch { /* not critical */ }
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// Sequelize
|
|
1228
|
+
if (request === 'sequelize' && !expressPatched.has('sequelize')) {
|
|
1229
|
+
expressPatched.add('sequelize');
|
|
1230
|
+
try {
|
|
1231
|
+
const { patchSequelize } = require(path.join(__dirname, 'db-observer.js'));
|
|
1232
|
+
patchSequelize(exports, debug);
|
|
1233
|
+
} catch { /* not critical */ }
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1191
1236
|
// Redis (ioredis)
|
|
1192
1237
|
if (request === 'ioredis' && !expressPatched.has('ioredis')) {
|
|
1193
1238
|
expressPatched.add('ioredis');
|