diff --git a/packages/tracing/src/integrations/index.ts b/packages/tracing/src/integrations/index.ts index 31c648263c61..5a9bd10b2d3c 100644 --- a/packages/tracing/src/integrations/index.ts +++ b/packages/tracing/src/integrations/index.ts @@ -1,3 +1,4 @@ export { Express } from './express'; +export { Postgres } from './postgres'; export { Mysql } from './mysql'; export { Mongo } from './mongo'; diff --git a/packages/tracing/src/integrations/postgres.ts b/packages/tracing/src/integrations/postgres.ts new file mode 100644 index 000000000000..f78f9d26121b --- /dev/null +++ b/packages/tracing/src/integrations/postgres.ts @@ -0,0 +1,73 @@ +import { Hub } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; +import { dynamicRequire, fill, logger } from '@sentry/utils'; + +interface PgClient { + prototype: { + query: () => void | Promise; + }; +} + +/** Tracing integration for node-postgres package */ +export class Postgres implements Integration { + /** + * @inheritDoc + */ + public static id: string = 'Postgres'; + + /** + * @inheritDoc + */ + public name: string = Postgres.id; + + /** + * @inheritDoc + */ + public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + let client: PgClient; + + try { + const pgModule = dynamicRequire(module, 'pg') as { Client: PgClient }; + client = pgModule.Client; + } catch (e) { + logger.error('Postgres Integration was unable to require `pg` package.'); + return; + } + + /** + * function (query, callback) => void + * function (query, params, callback) => void + * function (query) => Promise + * function (query, params) => Promise + */ + fill(client.prototype, 'query', function(orig: () => void | Promise) { + return function(this: unknown, config: unknown, values: unknown, callback: unknown) { + const scope = getCurrentHub().getScope(); + const parentSpan = scope?.getSpan(); + const span = parentSpan?.startChild({ + description: typeof config === 'string' ? config : (config as { text: string }).text, + op: `db`, + }); + + if (typeof callback === 'function') { + return orig.call(this, config, values, function(err: Error, result: unknown) { + span?.finish(); + callback(err, result); + }); + } + + if (typeof values === 'function') { + return orig.call(this, config, function(err: Error, result: unknown) { + span?.finish(); + values(err, result); + }); + } + + return (orig.call(this, config, values) as Promise).then((res: unknown) => { + span?.finish(); + return res; + }); + }; + }); + } +}