From bdcc8fc9dc77bb49f28865fb6d5a764d1bfbac41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Tue, 1 Dec 2020 14:44:14 +0100 Subject: [PATCH 1/4] feat: node-mysql tracing integration --- packages/tracing/src/integrations/index.ts | 1 + packages/tracing/src/integrations/mysql.ts | 75 ++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 packages/tracing/src/integrations/mysql.ts diff --git a/packages/tracing/src/integrations/index.ts b/packages/tracing/src/integrations/index.ts index abe1faf43c27..291887ee3b7d 100644 --- a/packages/tracing/src/integrations/index.ts +++ b/packages/tracing/src/integrations/index.ts @@ -1 +1,2 @@ export { Express } from './express'; +export { Mysql } from './mysql'; diff --git a/packages/tracing/src/integrations/mysql.ts b/packages/tracing/src/integrations/mysql.ts new file mode 100644 index 000000000000..e6b92ae06069 --- /dev/null +++ b/packages/tracing/src/integrations/mysql.ts @@ -0,0 +1,75 @@ +import { Hub } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; +import { dynamicRequire, fill, logger } from '@sentry/utils'; + +interface MysqlConnection { + end: () => void; + prototype: { + query: () => void; + }; +} + +/** Tracing integration for node-mysql package */ +export class Mysql implements Integration { + /** + * @inheritDoc + */ + public static id: string = 'Mysql'; + + /** + * @inheritDoc + */ + public name: string = Mysql.id; + + /** + * @inheritDoc + */ + public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + let connection: MysqlConnection; + + try { + const mysqlModule = dynamicRequire(module, 'mysql') as { + createConnection: (options: unknown) => MysqlConnection; + }; + const conn = mysqlModule.createConnection({}); + connection = (conn.constructor as unknown) as MysqlConnection; + conn.end(); + } catch (e) { + logger.error('Mysql Integration was unable to require `mysql` package.'); + return; + } + + /** + * function (query, callback) => void + * function (query, params, callback) => void + * function (query) => Promise + * function (query, params) => Promise + */ + fill(connection, 'query', function(orig: () => void | Promise) { + return function(this: unknown, options: unknown, values: unknown, callback: unknown) { + const scope = getCurrentHub().getScope(); + const transaction = scope?.getTransaction(); + const span = transaction?.startChild({ + description: typeof options === 'string' ? options : (options as { sql: string }).sql, + op: `query`, + }); + + if (typeof callback === 'function') { + return orig.call(this, options, values, function(err: Error, result: unknown, fields: unknown) { + span?.finish(); + callback(err, result, fields); + }); + } + + if (typeof values === 'function') { + return orig.call(this, options, function(err: Error, result: unknown, fields: unknown) { + span?.finish(); + values(err, result, fields); + }); + } + + return orig.call(this, options, values, callback); + }; + }); + } +} From 09566ed2bfba4a572451673e2a58d612fc16c2e9 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Thu, 3 Dec 2020 22:22:23 -0800 Subject: [PATCH 2/4] Use // instead of /** */ --- packages/tracing/src/integrations/mysql.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/tracing/src/integrations/mysql.ts b/packages/tracing/src/integrations/mysql.ts index e6b92ae06069..797e4a06043d 100644 --- a/packages/tracing/src/integrations/mysql.ts +++ b/packages/tracing/src/integrations/mysql.ts @@ -39,12 +39,11 @@ export class Mysql implements Integration { return; } - /** - * function (query, callback) => void - * function (query, params, callback) => void - * function (query) => Promise - * function (query, params) => Promise - */ + // The original function will have one of these signatures: + // function (query, callback) => void + // function (query, params, callback) => void + // function (query) => Promise + // function (query, params) => Promise fill(connection, 'query', function(orig: () => void | Promise) { return function(this: unknown, options: unknown, values: unknown, callback: unknown) { const scope = getCurrentHub().getScope(); From d5c2464fe3afca08f0842f7e50e44866785b6a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Fri, 4 Dec 2020 11:19:19 +0100 Subject: [PATCH 3/4] fix: Require mysql.Connection directly through mysql/lib/Connection.js --- packages/tracing/src/integrations/mysql.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/tracing/src/integrations/mysql.ts b/packages/tracing/src/integrations/mysql.ts index 797e4a06043d..a7edc6079dba 100644 --- a/packages/tracing/src/integrations/mysql.ts +++ b/packages/tracing/src/integrations/mysql.ts @@ -3,7 +3,6 @@ import { EventProcessor, Integration } from '@sentry/types'; import { dynamicRequire, fill, logger } from '@sentry/utils'; interface MysqlConnection { - end: () => void; prototype: { query: () => void; }; @@ -28,23 +27,18 @@ export class Mysql implements Integration { let connection: MysqlConnection; try { - const mysqlModule = dynamicRequire(module, 'mysql') as { - createConnection: (options: unknown) => MysqlConnection; - }; - const conn = mysqlModule.createConnection({}); - connection = (conn.constructor as unknown) as MysqlConnection; - conn.end(); + // Unfortunatelly mysql is using some custom loading system and `Connection` is not exported directly. + connection = dynamicRequire(module, 'mysql/lib/Connection.js'); } catch (e) { logger.error('Mysql Integration was unable to require `mysql` package.'); return; } // The original function will have one of these signatures: - // function (query, callback) => void - // function (query, params, callback) => void - // function (query) => Promise - // function (query, params) => Promise - fill(connection, 'query', function(orig: () => void | Promise) { + // function (callback) => void + // function (options, callback) => void + // function (options, values, callback) => void + fill(connection.prototype, 'query', function(orig: () => void) { return function(this: unknown, options: unknown, values: unknown, callback: unknown) { const scope = getCurrentHub().getScope(); const transaction = scope?.getTransaction(); From 4b502fe040778fe8216e2808974c4342d754404f Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Fri, 4 Dec 2020 12:40:22 +0100 Subject: [PATCH 4/4] ref: Align span description --- packages/tracing/src/integrations/mysql.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/tracing/src/integrations/mysql.ts b/packages/tracing/src/integrations/mysql.ts index a7edc6079dba..543f70c1f438 100644 --- a/packages/tracing/src/integrations/mysql.ts +++ b/packages/tracing/src/integrations/mysql.ts @@ -41,10 +41,10 @@ export class Mysql implements Integration { fill(connection.prototype, 'query', function(orig: () => void) { return function(this: unknown, options: unknown, values: unknown, callback: unknown) { const scope = getCurrentHub().getScope(); - const transaction = scope?.getTransaction(); - const span = transaction?.startChild({ + const parentSpan = scope?.getSpan(); + const span = parentSpan?.startChild({ description: typeof options === 'string' ? options : (options as { sql: string }).sql, - op: `query`, + op: `db`, }); if (typeof callback === 'function') {