Skip to content

Commit

Permalink
fix(instrumentation-mysql2): patch Connection class when imported (op…
Browse files Browse the repository at this point in the history
  • Loading branch information
macno committed Jan 17, 2025
1 parent 3a1efc4 commit b9093ae
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as api from '@opentelemetry/api';
import {
InstrumentationBase,
InstrumentationNodeModuleDefinition,
InstrumentationNodeModuleFile,
isWrapped,
safeExecuteInTheMiddle,
} from '@opentelemetry/instrumentation';
Expand All @@ -41,6 +42,8 @@ import { PACKAGE_NAME, PACKAGE_VERSION } from './version';

type formatType = typeof mysqlTypes.format;

const supportedVersions = ['>=1.4.2 <4'];

export class MySQL2Instrumentation extends InstrumentationBase<MySQL2InstrumentationConfig> {
static readonly COMMON_ATTRIBUTES = {
[SEMATTRS_DB_SYSTEM]: DBSYSTEMVALUES_MYSQL,
Expand All @@ -51,45 +54,75 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
}

protected init() {
let format: formatType | undefined;
function setFormatFunction(moduleExports: any) {
if (!format && moduleExports.format) {
format = moduleExports.format;
}
}
const patch = (ConnectionPrototype: mysqlTypes.Connection) => {
if (isWrapped(ConnectionPrototype.query)) {
this._unwrap(ConnectionPrototype, 'query');
}
this._wrap(
ConnectionPrototype,
'query',
this._patchQuery(format, false) as any
);
if (isWrapped(ConnectionPrototype.execute)) {
this._unwrap(ConnectionPrototype, 'execute');
}
this._wrap(
ConnectionPrototype,
'execute',
this._patchQuery(format, true) as any
);
};
const unpatch = (ConnectionPrototype: mysqlTypes.Connection) => {
this._unwrap(ConnectionPrototype, 'query');
this._unwrap(ConnectionPrototype, 'execute');
};
return [
new InstrumentationNodeModuleDefinition(
'mysql2',
['>=1.4.2 <4'],
supportedVersions,
(moduleExports: any) => {
const ConnectionPrototype: mysqlTypes.Connection =
getConnectionPrototypeToInstrument(moduleExports.Connection);
if (isWrapped(ConnectionPrototype.query)) {
this._unwrap(ConnectionPrototype, 'query');
}
this._wrap(
ConnectionPrototype,
'query',
this._patchQuery(moduleExports.format, false) as any
);

if (isWrapped(ConnectionPrototype.execute)) {
this._unwrap(ConnectionPrototype, 'execute');
}
this._wrap(
ConnectionPrototype,
'execute',
this._patchQuery(moduleExports.format, true) as any
);

setFormatFunction(moduleExports);
return moduleExports;
},
(moduleExports: any) => {
if (moduleExports === undefined) return;
const ConnectionPrototype: mysqlTypes.Connection =
moduleExports.Connection.prototype;
this._unwrap(ConnectionPrototype, 'query');
this._unwrap(ConnectionPrototype, 'execute');
}
() => {},
[
new InstrumentationNodeModuleFile(
'mysql2/promise.js',
supportedVersions,
(moduleExports: any) => {
setFormatFunction(moduleExports);
return moduleExports;
},
() => {}
),
new InstrumentationNodeModuleFile(
'mysql2/lib/connection.js',
supportedVersions,
(moduleExports: any) => {
const ConnectionPrototype: mysqlTypes.Connection =
getConnectionPrototypeToInstrument(moduleExports);
patch(ConnectionPrototype);
return moduleExports;
},
(moduleExports: any) => {
if (moduleExports === undefined) return;
const ConnectionPrototype: mysqlTypes.Connection =
getConnectionPrototypeToInstrument(moduleExports);
unpatch(ConnectionPrototype);
}
),
]
),
];
}

private _patchQuery(format: formatType, isPrepared: boolean) {
private _patchQuery(format: formatType | undefined, isPrepared: boolean) {
return (originalQuery: Function): Function => {
const thisPlugin = this;
return function query(
Expand Down
13 changes: 7 additions & 6 deletions plugins/node/opentelemetry-instrumentation-mysql2/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import {
SEMATTRS_NET_PEER_NAME,
SEMATTRS_NET_PEER_PORT,
} from '@opentelemetry/semantic-conventions';
import type * as mysqlTypes from 'mysql2';

type formatType = typeof mysqlTypes.format;

/*
Following types declare an expectation on mysql2 types and define a subset we
Expand Down Expand Up @@ -103,14 +106,12 @@ function getJDBCString(
*/
export function getDbStatement(
query: string | Query | QueryOptions,
format: (
sql: string,
values: any[],
stringifyObjects?: boolean,
timeZone?: string
) => string,
format?: formatType,
values?: any[]
): string {
if (!format) {
return typeof query === 'string' ? query : query.sql;
}
if (typeof query === 'string') {
return values ? format(query, values) : query;
} else {
Expand Down

0 comments on commit b9093ae

Please sign in to comment.