diff --git a/packages/tracing/src/integrations/index.ts b/packages/tracing/src/integrations/index.ts index f4255a0e3937..31c648263c61 100644 --- a/packages/tracing/src/integrations/index.ts +++ b/packages/tracing/src/integrations/index.ts @@ -1,2 +1,3 @@ export { Express } from './express'; +export { Mysql } from './mysql'; export { Mongo } from './mongo'; diff --git a/packages/tracing/src/integrations/mysql.ts b/packages/tracing/src/integrations/mysql.ts new file mode 100644 index 000000000000..543f70c1f438 --- /dev/null +++ b/packages/tracing/src/integrations/mysql.ts @@ -0,0 +1,68 @@ +import { Hub } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; +import { dynamicRequire, fill, logger } from '@sentry/utils'; + +interface MysqlConnection { + 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 { + // 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 (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 parentSpan = scope?.getSpan(); + const span = parentSpan?.startChild({ + description: typeof options === 'string' ? options : (options as { sql: string }).sql, + op: `db`, + }); + + 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); + }; + }); + } +}