From a90a46097e984bad3987a1b8fd665cf086db3cbd Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 12 Mar 2024 13:03:43 +0000 Subject: [PATCH] ref(node-experimental): Refactor usage of `startChild()` This refactors the old node integrations to the new syntax, so we can get rid of the public APIs. --- .../src/node/integrations/apollo.ts | 58 +++++++------------ .../src/node/integrations/express.ts | 42 ++++++++------ .../src/node/integrations/graphql.ts | 51 +++++----------- .../src/node/integrations/mongo.ts | 48 +++++++-------- .../src/node/integrations/mysql.ts | 19 +++--- .../src/node/integrations/postgres.ts | 29 ++++------ 6 files changed, 102 insertions(+), 145 deletions(-) diff --git a/packages/tracing-internal/src/node/integrations/apollo.ts b/packages/tracing-internal/src/node/integrations/apollo.ts index 2b4268239d63..17ba0ab15482 100644 --- a/packages/tracing-internal/src/node/integrations/apollo.ts +++ b/packages/tracing-internal/src/node/integrations/apollo.ts @@ -1,6 +1,5 @@ -import type { Hub, SentrySpan } from '@sentry/core'; -import type { EventProcessor } from '@sentry/types'; -import { arrayify, fill, isThenable, loadModule, logger } from '@sentry/utils'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core'; +import { arrayify, fill, loadModule, logger } from '@sentry/utils'; import { DEBUG_BUILD } from '../../common/debug-build'; import type { LazyLoadedIntegration } from './lazy'; @@ -75,7 +74,7 @@ export class Apollo implements LazyLoadedIntegration void, getCurrentHub: () => Hub): void { + public setupOnce(): void { if (this._useNest) { const pkg = this.loadDependency(); @@ -99,7 +98,7 @@ export class Apollo implements LazyLoadedIntegration Hub): ApolloModelResolvers[] { +function instrumentResolvers(resolvers: ApolloModelResolvers[]): ApolloModelResolvers[] { return resolvers.map(model => { Object.keys(model).forEach(resolverGroupName => { Object.keys(model[resolverGroupName]).forEach(resolverName => { @@ -163,7 +162,7 @@ function instrumentResolvers(resolvers: ApolloModelResolvers[], getCurrentHub: ( return; } - wrapResolver(model, resolverGroupName, resolverName, getCurrentHub); + wrapResolver(model, resolverGroupName, resolverName); }); }); @@ -174,37 +173,22 @@ function instrumentResolvers(resolvers: ApolloModelResolvers[], getCurrentHub: ( /** * Wrap a single resolver which can be a parent of other resolvers and/or db operations. */ -function wrapResolver( - model: ApolloModelResolvers, - resolverGroupName: string, - resolverName: string, - getCurrentHub: () => Hub, -): void { +function wrapResolver(model: ApolloModelResolvers, resolverGroupName: string, resolverName: string): void { fill(model[resolverGroupName], resolverName, function (orig: () => unknown | Promise) { return function (this: unknown, ...args: unknown[]) { - // eslint-disable-next-line deprecation/deprecation - const scope = getCurrentHub().getScope(); - // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan() as SentrySpan | undefined; - // eslint-disable-next-line deprecation/deprecation - const span = parentSpan?.startChild({ - name: `${resolverGroupName}.${resolverName}`, - op: 'graphql.resolve', - origin: 'auto.graphql.apollo', - }); - - const rv = orig.call(this, ...args); - - if (isThenable(rv)) { - return rv.then((res: unknown) => { - span?.end(); - return res; - }); - } - - span?.end(); - - return rv; + return startSpan( + { + onlyIfParent: true, + name: `${resolverGroupName}.${resolverName}`, + op: 'graphql.resolve', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.graphql.apollo', + }, + }, + () => { + return orig.call(this, ...args); + }, + ); }; }); } diff --git a/packages/tracing-internal/src/node/integrations/express.ts b/packages/tracing-internal/src/node/integrations/express.ts index b6012bad9a72..047983e91145 100644 --- a/packages/tracing-internal/src/node/integrations/express.ts +++ b/packages/tracing-internal/src/node/integrations/express.ts @@ -1,5 +1,6 @@ /* eslint-disable max-lines */ import type { Transaction } from '@sentry/core'; +import { startInactiveSpan, withActiveSpan } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, spanToJSON } from '@sentry/core'; import type { Integration, PolymorphicRequest } from '@sentry/types'; import { @@ -153,11 +154,12 @@ function wrap(fn: Function, method: Method): (...args: any[]) => void { return function (this: NodeJS.Global, req: unknown, res: ExpressResponse & SentryTracingResponse): void { const transaction = res.__sentry_transaction; if (transaction) { - // eslint-disable-next-line deprecation/deprecation - const span = transaction.startChild({ - name: fn.name, - op: `middleware.express.${method}`, - origin: 'auto.middleware.express', + const span = withActiveSpan(transaction, () => { + return startInactiveSpan({ + name: fn.name, + op: `middleware.express.${method}`, + origin: 'auto.middleware.express', + }); }); res.once('finish', () => { span.end(); @@ -174,12 +176,15 @@ function wrap(fn: Function, method: Method): (...args: any[]) => void { next: () => void, ): void { const transaction = res.__sentry_transaction; - // eslint-disable-next-line deprecation/deprecation - const span = transaction?.startChild({ - name: fn.name, - op: `middleware.express.${method}`, - origin: 'auto.middleware.express', - }); + const span = transaction + ? withActiveSpan(transaction, () => { + return startInactiveSpan({ + name: fn.name, + op: `middleware.express.${method}`, + origin: 'auto.middleware.express', + }); + }) + : undefined; fn.call(this, req, res, function (this: NodeJS.Global, ...args: unknown[]): void { span?.end(); next.call(this, ...args); @@ -195,12 +200,15 @@ function wrap(fn: Function, method: Method): (...args: any[]) => void { next: () => void, ): void { const transaction = res.__sentry_transaction; - // eslint-disable-next-line deprecation/deprecation - const span = transaction?.startChild({ - name: fn.name, - op: `middleware.express.${method}`, - origin: 'auto.middleware.express', - }); + const span = transaction + ? withActiveSpan(transaction, () => { + return startInactiveSpan({ + name: fn.name, + op: `middleware.express.${method}`, + origin: 'auto.middleware.express', + }); + }) + : undefined; fn.call(this, err, req, res, function (this: NodeJS.Global, ...args: unknown[]): void { span?.end(); next.call(this, ...args); diff --git a/packages/tracing-internal/src/node/integrations/graphql.ts b/packages/tracing-internal/src/node/integrations/graphql.ts index b2ddee7530f3..e61010e5953c 100644 --- a/packages/tracing-internal/src/node/integrations/graphql.ts +++ b/packages/tracing-internal/src/node/integrations/graphql.ts @@ -1,6 +1,5 @@ -import type { Hub, SentrySpan } from '@sentry/core'; -import type { EventProcessor } from '@sentry/types'; -import { fill, isThenable, loadModule, logger } from '@sentry/utils'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core'; +import { fill, loadModule, logger } from '@sentry/utils'; import { DEBUG_BUILD } from '../../common/debug-build'; import type { LazyLoadedIntegration } from './lazy'; @@ -35,7 +34,7 @@ export class GraphQL implements LazyLoadedIntegration { /** * @inheritDoc */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + public setupOnce(): void { const pkg = this.loadDependency(); if (!pkg) { @@ -45,37 +44,19 @@ export class GraphQL implements LazyLoadedIntegration { fill(pkg, 'execute', function (orig: () => void | Promise) { return function (this: unknown, ...args: unknown[]) { - // eslint-disable-next-line deprecation/deprecation - const scope = getCurrentHub().getScope(); - // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan() as SentrySpan | undefined; - - // eslint-disable-next-line deprecation/deprecation - const span = parentSpan?.startChild({ - name: 'execute', - op: 'graphql.execute', - origin: 'auto.graphql.graphql', - }); - - // eslint-disable-next-line deprecation/deprecation - scope?.setSpan(span); - - const rv = orig.call(this, ...args); - - if (isThenable(rv)) { - return rv.then((res: unknown) => { - span?.end(); - // eslint-disable-next-line deprecation/deprecation - scope?.setSpan(parentSpan); - - return res; - }); - } - - span?.end(); - // eslint-disable-next-line deprecation/deprecation - scope?.setSpan(parentSpan); - return rv; + return startSpan( + { + onlyIfParent: true, + name: 'execute', + op: 'graphql.execute', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.graphql.graphql', + }, + }, + () => { + return orig.call(this, ...args); + }, + ); }; }); } diff --git a/packages/tracing-internal/src/node/integrations/mongo.ts b/packages/tracing-internal/src/node/integrations/mongo.ts index e052eafa6378..7c6f1bf97159 100644 --- a/packages/tracing-internal/src/node/integrations/mongo.ts +++ b/packages/tracing-internal/src/node/integrations/mongo.ts @@ -1,5 +1,6 @@ -import type { Hub, SentrySpan } from '@sentry/core'; -import type { EventProcessor, SpanContext } from '@sentry/types'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core'; +import { getClient } from '@sentry/core'; +import type { EventProcessor, SpanAttributes, StartSpanOptions } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; import { DEBUG_BUILD } from '../../common/debug-build'; @@ -138,7 +139,7 @@ export class Mongo implements LazyLoadedIntegration { /** * @inheritDoc */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + public setupOnce(_: (callback: EventProcessor) => void): void { const pkg = this.loadDependency(); if (!pkg) { @@ -147,20 +148,20 @@ export class Mongo implements LazyLoadedIntegration { return; } - this._instrumentOperations(pkg.Collection, this._operations, getCurrentHub); + this._instrumentOperations(pkg.Collection, this._operations); } /** * Patches original collection methods */ - private _instrumentOperations(collection: MongoCollection, operations: Operation[], getCurrentHub: () => Hub): void { - operations.forEach((operation: Operation) => this._patchOperation(collection, operation, getCurrentHub)); + private _instrumentOperations(collection: MongoCollection, operations: Operation[]): void { + operations.forEach((operation: Operation) => this._patchOperation(collection, operation)); } /** * Patches original collection to utilize our tracing functionality */ - private _patchOperation(collection: MongoCollection, operation: Operation, getCurrentHub: () => Hub): void { + private _patchOperation(collection: MongoCollection, operation: Operation): void { if (!(operation in collection.prototype)) return; const getSpanContext = this._getSpanContextFromOperationArguments.bind(this); @@ -168,21 +169,15 @@ export class Mongo implements LazyLoadedIntegration { fill(collection.prototype, operation, function (orig: () => void | Promise) { return function (this: unknown, ...args: unknown[]) { const lastArg = args[args.length - 1]; - const hub = getCurrentHub(); - // eslint-disable-next-line deprecation/deprecation - const scope = hub.getScope(); - // eslint-disable-next-line deprecation/deprecation - const client = hub.getClient(); - // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan() as SentrySpan | undefined; + + const client = getClient(); const sendDefaultPii = client?.getOptions().sendDefaultPii; // Check if the operation was passed a callback. (mapReduce requires a different check, as // its (non-callback) arguments can also be functions.) if (typeof lastArg !== 'function' || (operation === 'mapReduce' && args.length === 2)) { - // eslint-disable-next-line deprecation/deprecation - const span = parentSpan?.startChild(getSpanContext(this, operation, args, sendDefaultPii)); + const span = startInactiveSpan(getSpanContext(this, operation, args, sendDefaultPii)); const maybePromiseOrCursor = orig.call(this, ...args); if (isThenable(maybePromiseOrCursor)) { @@ -213,8 +208,7 @@ export class Mongo implements LazyLoadedIntegration { } } - // eslint-disable-next-line deprecation/deprecation - const span = parentSpan?.startChild(getSpanContext(this, operation, args.slice(0, -1))); + const span = startInactiveSpan(getSpanContext(this, operation, args.slice(0, -1))); return orig.call(this, ...args.slice(0, -1), function (err: Error, result: unknown) { span?.end(); @@ -232,19 +226,19 @@ export class Mongo implements LazyLoadedIntegration { operation: Operation, args: unknown[], sendDefaultPii: boolean | undefined = false, - ): SpanContext { - const data: { [key: string]: string } = { + ): StartSpanOptions { + const attributes: SpanAttributes = { 'db.system': 'mongodb', 'db.name': collection.dbName, 'db.operation': operation, 'db.mongodb.collection': collection.collectionName, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `${collection.collectionName}.${operation}`, }; - const spanContext: SpanContext = { + const spanContext: StartSpanOptions = { op: 'db', - // TODO v8: Use `${collection.collectionName}.${operation}` - origin: 'auto.db.mongo', name: operation, - data, + attributes, + onlyIfParent: true, }; // If the operation takes no arguments besides `options` and `callback`, or if argument @@ -262,11 +256,11 @@ export class Mongo implements LazyLoadedIntegration { // Special case for `mapReduce`, as the only one accepting functions as arguments. if (operation === 'mapReduce') { const [map, reduce] = args as { name?: string }[]; - data[signature[0]] = typeof map === 'string' ? map : map.name || ''; - data[signature[1]] = typeof reduce === 'string' ? reduce : reduce.name || ''; + attributes[signature[0]] = typeof map === 'string' ? map : map.name || ''; + attributes[signature[1]] = typeof reduce === 'string' ? reduce : reduce.name || ''; } else { for (let i = 0; i < signature.length; i++) { - data[`db.mongodb.${signature[i]}`] = JSON.stringify(args[i]); + attributes[`db.mongodb.${signature[i]}`] = JSON.stringify(args[i]); } } } catch (_oO) { diff --git a/packages/tracing-internal/src/node/integrations/mysql.ts b/packages/tracing-internal/src/node/integrations/mysql.ts index 3cca1c8d5ccd..c62a6db0028f 100644 --- a/packages/tracing-internal/src/node/integrations/mysql.ts +++ b/packages/tracing-internal/src/node/integrations/mysql.ts @@ -1,5 +1,5 @@ -import type { Hub, SentrySpan } from '@sentry/core'; -import type { EventProcessor, Span } from '@sentry/types'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core'; +import type { Span } from '@sentry/types'; import { fill, loadModule, logger } from '@sentry/utils'; import { DEBUG_BUILD } from '../../common/debug-build'; @@ -44,7 +44,7 @@ export class Mysql implements LazyLoadedIntegration { /** * @inheritDoc */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + public setupOnce(): void { const pkg = this.loadDependency(); if (!pkg) { @@ -97,18 +97,13 @@ export class Mysql implements LazyLoadedIntegration { // function (options, values, callback) => void fill(pkg, 'createQuery', function (orig: () => void) { return function (this: unknown, options: unknown, values: unknown, callback: unknown) { - // eslint-disable-next-line deprecation/deprecation - const scope = getCurrentHub().getScope(); - // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan() as SentrySpan | undefined; - - // eslint-disable-next-line deprecation/deprecation - const span = parentSpan?.startChild({ + const span = startInactiveSpan({ + onlyIfParent: true, name: typeof options === 'string' ? options : (options as { sql: string }).sql, op: 'db', - origin: 'auto.db.mysql', - data: { + attributes: { 'db.system': 'mysql', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.mysql', }, }); diff --git a/packages/tracing-internal/src/node/integrations/postgres.ts b/packages/tracing-internal/src/node/integrations/postgres.ts index 3c883bb64de1..30f94e41998c 100644 --- a/packages/tracing-internal/src/node/integrations/postgres.ts +++ b/packages/tracing-internal/src/node/integrations/postgres.ts @@ -1,5 +1,5 @@ -import type { Hub, SentrySpan } from '@sentry/core'; -import type { EventProcessor } from '@sentry/types'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core'; +import type { SpanAttributes } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; import { DEBUG_BUILD } from '../../common/debug-build'; @@ -74,7 +74,7 @@ export class Postgres implements LazyLoadedIntegration { /** * @inheritDoc */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + public setupOnce(): void { const pkg = this.loadDependency(); if (!pkg) { @@ -98,38 +98,33 @@ export class Postgres implements LazyLoadedIntegration { */ fill(Client.prototype, 'query', function (orig: PgClientQuery) { return function (this: PgClientThis, config: unknown, values: unknown, callback: unknown) { - // eslint-disable-next-line deprecation/deprecation - const scope = getCurrentHub().getScope(); - // eslint-disable-next-line deprecation/deprecation - const parentSpan = scope.getSpan() as SentrySpan | undefined; - - const data: Record = { + const attributes: SpanAttributes = { 'db.system': 'postgresql', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.postgres', }; try { if (this.database) { - data['db.name'] = this.database; + attributes['db.name'] = this.database; } if (this.host) { - data['server.address'] = this.host; + attributes['server.address'] = this.host; } if (this.port) { - data['server.port'] = this.port; + attributes['server.port'] = this.port; } if (this.user) { - data['db.user'] = this.user; + attributes['db.user'] = this.user; } } catch (e) { // ignore } - // eslint-disable-next-line deprecation/deprecation - const span = parentSpan?.startChild({ + const span = startInactiveSpan({ + onlyIfParent: true, name: typeof config === 'string' ? config : (config as { text: string }).text, op: 'db', - origin: 'auto.db.postgres', - data, + attributes, }); if (typeof callback === 'function') {