diff --git a/packages/node/src/integrations/tracing/mysql/index.ts b/packages/node/src/integrations/tracing/mysql/index.ts deleted file mode 100644 index 1691bfd10ef2..000000000000 --- a/packages/node/src/integrations/tracing/mysql/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { MySQLInstrumentation } from './vendored/instrumentation'; -import { generateInstrumentOnce } from '../../../otel/instrument'; - -const INTEGRATION_NAME = 'Mysql' as const; - -export const instrumentMysql = generateInstrumentOnce(INTEGRATION_NAME, () => new MySQLInstrumentation({})); diff --git a/packages/node/src/integrations/tracing/mysql/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/mysql/vendored/instrumentation.ts deleted file mode 100644 index 56ae9c3030de..000000000000 --- a/packages/node/src/integrations/tracing/mysql/vendored/instrumentation.ts +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-mysql - * - Upstream version: @opentelemetry/instrumentation-mysql@0.64.0 - * - Types from the `mysql` package inlined as simplified interfaces - * - Minor TypeScript strictness adjustments for this repository's compiler settings - * - Refactored to use Sentry's span APIs instead of OpenTelemetry tracing APIs, and the connection-pool - * metrics / `enhancedDatabaseReporting` option were removed (unused by the Sentry SDK) - */ - -import type { Span } from '@opentelemetry/api'; -import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; -import { InstrumentationBase, InstrumentationNodeModuleDefinition, isWrapped } from '@opentelemetry/instrumentation'; -import type { Scope, SpanAttributes } from '@sentry/core'; -import { - bindScopeToEmitter, - getCurrentScope, - SDK_VERSION, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - SPAN_STATUS_ERROR, - startInactiveSpan, - withActiveSpan, - withScope, -} from '@sentry/core'; -import { - DB_NAME, - DB_STATEMENT, - DB_SYSTEM, - DB_USER, - NET_PEER_NAME, - NET_PEER_PORT, - SENTRY_KIND, -} from '@sentry/conventions/attributes'; -import type * as mysqlTypes from './mysql-types'; -import { ATTR_DB_CONNECTION_STRING, DB_SYSTEM_VALUE_MYSQL } from './semconv'; -import { getConfig, getDbQueryText, getJDBCString, getSpanName } from './utils'; - -const PACKAGE_NAME = '@sentry/instrumentation-mysql'; -const ORIGIN = 'auto.db.otel.mysql'; - -type getConnectionCallbackType = (err: mysqlTypes.MysqlError, connection: mysqlTypes.PoolConnection) => void; - -export class MySQLInstrumentation extends InstrumentationBase { - public constructor(config: InstrumentationConfig = {}) { - super(PACKAGE_NAME, SDK_VERSION, config); - } - - protected init() { - return [ - new InstrumentationNodeModuleDefinition( - 'mysql', - ['>=2.0.0 <3'], - (moduleExports: typeof mysqlTypes) => { - if (isWrapped(moduleExports.createConnection)) { - this._unwrap(moduleExports, 'createConnection'); - } - // oxlint-disable-next-line typescript/no-explicit-any - this._wrap(moduleExports, 'createConnection', this._patchCreateConnection() as any); - - if (isWrapped(moduleExports.createPool)) { - this._unwrap(moduleExports, 'createPool'); - } - // oxlint-disable-next-line typescript/no-explicit-any - this._wrap(moduleExports, 'createPool', this._patchCreatePool() as any); - - if (isWrapped(moduleExports.createPoolCluster)) { - this._unwrap(moduleExports, 'createPoolCluster'); - } - // oxlint-disable-next-line typescript/no-explicit-any - this._wrap(moduleExports, 'createPoolCluster', this._patchCreatePoolCluster() as any); - - return moduleExports; - }, - (moduleExports: typeof mysqlTypes) => { - if (moduleExports === undefined) return; - this._unwrap(moduleExports, 'createConnection'); - this._unwrap(moduleExports, 'createPool'); - this._unwrap(moduleExports, 'createPoolCluster'); - }, - ), - ]; - } - - // global export function - private _patchCreateConnection() { - return (originalCreateConnection: Function) => { - // oxlint-disable-next-line typescript/no-this-alias - const thisPlugin = this; - - return function createConnection(_connectionUri: string | mysqlTypes.ConnectionConfig) { - const originalResult = originalCreateConnection(...arguments); - - // This is unwrapped on next call after unpatch - // oxlint-disable-next-line typescript/no-explicit-any - thisPlugin._wrap(originalResult, 'query', thisPlugin._patchQuery(originalResult) as any); - - return originalResult; - }; - }; - } - - // global export function - private _patchCreatePool() { - return (originalCreatePool: Function) => { - // oxlint-disable-next-line typescript/no-this-alias - const thisPlugin = this; - return function createPool(_config: string | mysqlTypes.PoolConfig) { - const pool = originalCreatePool(...arguments); - - thisPlugin._wrap(pool, 'query', thisPlugin._patchQuery(pool)); - thisPlugin._wrap(pool, 'getConnection', thisPlugin._patchGetConnection(pool)); - - return pool; - }; - }; - } - - // global export function - private _patchCreatePoolCluster() { - return (originalCreatePoolCluster: Function) => { - // oxlint-disable-next-line typescript/no-this-alias - const thisPlugin = this; - return function createPool(_config: string | mysqlTypes.PoolConfig) { - const cluster = originalCreatePoolCluster(...arguments); - - // This is unwrapped on next call after unpatch - thisPlugin._wrap(cluster, 'getConnection', thisPlugin._patchGetConnection(cluster)); - - return cluster; - }; - }; - } - - // method on cluster or pool - private _patchGetConnection(pool: mysqlTypes.Pool | mysqlTypes.PoolCluster) { - return (originalGetConnection: Function) => { - // oxlint-disable-next-line typescript/no-this-alias - const thisPlugin = this; - - return function getConnection(arg1?: unknown, arg2?: unknown, arg3?: unknown) { - // Unwrap if unpatch has been called - if (!thisPlugin['_enabled']) { - thisPlugin._unwrap(pool, 'getConnection'); - return originalGetConnection.apply(pool, arguments); - } - - if (arguments.length === 1 && typeof arg1 === 'function') { - const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg1 as getConnectionCallbackType); - return originalGetConnection.call(pool, patchFn); - } - if (arguments.length === 2 && typeof arg2 === 'function') { - const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg2 as getConnectionCallbackType); - return originalGetConnection.call(pool, arg1, patchFn); - } - if (arguments.length === 3 && typeof arg3 === 'function') { - const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg3 as getConnectionCallbackType); - return originalGetConnection.call(pool, arg1, arg2, patchFn); - } - - return originalGetConnection.apply(pool, arguments); - }; - }; - } - - private _getConnectionCallbackPatchFn(cb: getConnectionCallbackType) { - // oxlint-disable-next-line typescript/no-this-alias - const thisPlugin = this; - const scope = getCurrentScope(); - return function (this: unknown, err: mysqlTypes.MysqlError, connection: mysqlTypes.PoolConnection) { - if (connection) { - // this is the callback passed into a query - // no need to unwrap - if (!isWrapped(connection.query)) { - // oxlint-disable-next-line typescript/no-explicit-any - thisPlugin._wrap(connection, 'query', thisPlugin._patchQuery(connection) as any); - } - } - if (typeof cb === 'function') { - withScope(scope, () => cb.call(this, err, connection)); - } - }; - } - - private _patchQuery(connection: mysqlTypes.Connection | mysqlTypes.Pool) { - return (originalQuery: Function) => { - // oxlint-disable-next-line typescript/no-this-alias - const thisPlugin = this; - - return function query( - query: string | mysqlTypes.Query | mysqlTypes.QueryOptions, - _valuesOrCallback?: unknown[] | mysqlTypes.queryCallback, - _callback?: mysqlTypes.queryCallback, - ) { - if (!thisPlugin['_enabled']) { - thisPlugin._unwrap(connection, 'query'); - return originalQuery.apply(connection, arguments); - } - - const { host, port, database, user } = getConfig(connection.config); - const portNumber = parseInt(String(port), 10); - /* eslint-disable @sentry/no-deprecated-attributes */ - const attributes: SpanAttributes = { - [SENTRY_KIND]: 'client', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, - [DB_SYSTEM]: DB_SYSTEM_VALUE_MYSQL, - [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, port, database), - [DB_NAME]: database, - [DB_USER]: user, - [DB_STATEMENT]: getDbQueryText(query), - [NET_PEER_NAME]: host, - }; - - if (!isNaN(portNumber)) { - attributes[NET_PEER_PORT] = portNumber; - } - /* eslint-enable @sentry/no-deprecated-attributes */ - - const span = startInactiveSpan({ - name: getSpanName(query), - attributes, - }); - - const cbIndex = Array.from(arguments).findIndex(arg => typeof arg === 'function'); - - const scope = getCurrentScope(); - if (cbIndex === -1) { - const streamableQuery: mysqlTypes.Query = withActiveSpan(span, () => { - return originalQuery.apply(connection, arguments); - }); - bindScopeToEmitter(streamableQuery, scope); - - return streamableQuery - .on('error', (err: unknown) => { - span.setStatus({ - code: SPAN_STATUS_ERROR, - message: (err as mysqlTypes.MysqlError).message, - }); - }) - .on('end', () => { - span.end(); - }); - } else { - thisPlugin._wrap(arguments, cbIndex, thisPlugin._patchCallbackQuery(span, scope)); - - return withActiveSpan(span, () => { - return originalQuery.apply(connection, arguments); - }); - } - }; - }; - } - - private _patchCallbackQuery(span: Span, scope: Scope) { - return (originalCallback: Function) => { - return function (err: mysqlTypes.MysqlError | null, _results?: unknown, _fields?: mysqlTypes.FieldInfo[]) { - if (err) { - span.setStatus({ - code: SPAN_STATUS_ERROR, - message: err.message, - }); - } - span.end(); - return withScope(scope, () => originalCallback(...arguments)); - }; - }; - } -} diff --git a/packages/node/src/integrations/tracing/mysql/vendored/mysql-types.ts b/packages/node/src/integrations/tracing/mysql/vendored/mysql-types.ts deleted file mode 100644 index ee72492137e4..000000000000 --- a/packages/node/src/integrations/tracing/mysql/vendored/mysql-types.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-mysql - * - Upstream version: @opentelemetry/instrumentation-mysql@0.64.0 - * - Simplified type definitions inlined from the `mysql` package to avoid requiring it as a dependency - */ - -export interface MysqlError extends Error { - code: string; - errno: number; - fatal: boolean; - sql?: string; - sqlState?: string; - sqlMessage?: string; - [key: string]: unknown; -} - -export interface ConnectionConfig { - host?: string; - port?: number; - database?: string; - user?: string; - password?: string; - [key: string]: unknown; -} - -export interface PoolConfig extends ConnectionConfig { - connectionConfig?: ConnectionConfig; - [key: string]: unknown; -} - -export interface QueryOptions { - sql: string; - values?: unknown; - [key: string]: unknown; -} - -export interface FieldInfo { - catalog: string; - db: string; - table: string; - orgTable: string; - name: string; - orgName: string; - charsetNr: number; - length: number; - type: number; - [key: string]: unknown; -} - -export type queryCallback = (err: MysqlError | null, results?: unknown, fields?: FieldInfo[]) => void; - -export interface Query { - sql: string; - values?: unknown; - on(event: string, listener: (...args: unknown[]) => void): this; - [key: string]: unknown; -} - -export type QueryFunction = { - (query: string | QueryOptions, callback?: queryCallback): Query; - (query: string | QueryOptions, values?: unknown, callback?: queryCallback): Query; -}; - -export interface Connection { - config: ConnectionConfig; - query: QueryFunction; - [key: string]: unknown; -} - -export interface PoolConnection extends Connection { - release(): void; - [key: string]: unknown; -} - -export interface Pool { - config: PoolConfig; - query: QueryFunction; - getConnection(callback: (err: MysqlError, connection: PoolConnection) => void): void; - end(callback?: (err?: MysqlError) => void): void; - on(event: string, listener: (...args: unknown[]) => void): this; - [key: string]: unknown; -} - -export interface PoolCluster { - getConnection(callback: (err: MysqlError, connection: PoolConnection) => void): void; - getConnection(pattern: string, callback: (err: MysqlError, connection: PoolConnection) => void): void; - getConnection( - pattern: string, - selector: string, - callback: (err: MysqlError, connection: PoolConnection) => void, - ): void; - add(config: PoolConfig): void; - add(id: string, config: PoolConfig): void; - [key: string]: unknown; -} - -export declare function createConnection(connectionUri: string | ConnectionConfig): Connection; -export declare function createPool(config: string | PoolConfig): Pool; -export declare function createPoolCluster(config?: PoolConfig): PoolCluster; diff --git a/packages/node/src/integrations/tracing/mysql/vendored/semconv.ts b/packages/node/src/integrations/tracing/mysql/vendored/semconv.ts deleted file mode 100644 index 9bbb61510e4b..000000000000 --- a/packages/node/src/integrations/tracing/mysql/vendored/semconv.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-mysql - * - Upstream version: @opentelemetry/instrumentation-mysql@0.64.0 - */ - -/* - * This file contains a copy of unstable semantic convention definitions - * used by this package. - * @see https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions#unstable-semconv - */ - -/** - * Deprecated, use `server.address`, `server.port` attributes instead. - * - * @example "Server=(localdb)\\v11.0;Integrated Security=true;" - * - * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. - * - * @deprecated Replaced by `server.address` and `server.port`. - */ -export const ATTR_DB_CONNECTION_STRING = 'db.connection_string' as const; - -/** - * Enum value "mysql" for attribute `db.system`. - * - * MySQL - * - * @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. - */ -export const DB_SYSTEM_VALUE_MYSQL = 'mysql' as const; diff --git a/packages/node/src/integrations/tracing/mysql/vendored/utils.ts b/packages/node/src/integrations/tracing/mysql/vendored/utils.ts deleted file mode 100644 index 24e91b9ed44a..000000000000 --- a/packages/node/src/integrations/tracing/mysql/vendored/utils.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-mysql - * - Upstream version: @opentelemetry/instrumentation-mysql@0.64.0 - * - Types from the `mysql` package inlined as simplified interfaces - */ - -import type { ConnectionConfig, PoolConfig, Query, QueryOptions } from './mysql-types'; - -export function getConfig(config: ConnectionConfig | PoolConfig | undefined) { - const resolved = (config as PoolConfig | undefined)?.connectionConfig || config || {}; - const { host, port, database, user } = resolved; - return { host, port, database, user }; -} - -export function getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined) { - let jdbcString = `jdbc:mysql://${host || 'localhost'}`; - - if (typeof port === 'number') { - jdbcString += `:${port}`; - } - - if (typeof database === 'string') { - jdbcString += `/${database}`; - } - - return jdbcString; -} - -/** - * @returns the database query being executed. - */ -export function getDbQueryText(query: string | Query | QueryOptions): string { - if (typeof query === 'string') { - return query; - } else { - return query.sql; - } -} - -/** - * The span name SHOULD be set to a low cardinality value - * representing the statement executed on the database. - * - * TODO: revisit span name based on https://github.com/open-telemetry/semantic-conventions/blob/v1.33.0/docs/database/database-spans.md#name - * - * @returns SQL statement without variable arguments or SQL verb - */ -export function getSpanName(query: string | Query | QueryOptions): string { - const rawQuery = typeof query === 'object' ? query.sql : query; - // Extract the SQL verb - const firstSpace = rawQuery?.indexOf(' '); - if (typeof firstSpace === 'number' && firstSpace !== -1) { - return rawQuery?.substring(0, firstSpace); - } - return rawQuery; -}