Skip to content

Commit c67a8c3

Browse files
authored
fix(instrumentation-mysql2): missing telemetry in mysql2 when importing only promise API (#2662)
1 parent 5f214eb commit c67a8c3

File tree

3 files changed

+1030
-874
lines changed

3 files changed

+1030
-874
lines changed

plugins/node/opentelemetry-instrumentation-mysql2/src/instrumentation.ts

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import * as api from '@opentelemetry/api';
1818
import {
1919
InstrumentationBase,
2020
InstrumentationNodeModuleDefinition,
21+
InstrumentationNodeModuleFile,
2122
isWrapped,
2223
safeExecuteInTheMiddle,
2324
} from '@opentelemetry/instrumentation';
@@ -41,6 +42,8 @@ import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
4142

4243
type formatType = typeof mysqlTypes.format;
4344

45+
const supportedVersions = ['>=1.4.2 <4'];
46+
4447
export class MySQL2Instrumentation extends InstrumentationBase<MySQL2InstrumentationConfig> {
4548
static readonly COMMON_ATTRIBUTES = {
4649
[SEMATTRS_DB_SYSTEM]: DBSYSTEMVALUES_MYSQL,
@@ -51,45 +54,75 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
5154
}
5255

5356
protected init() {
57+
let format: formatType | undefined;
58+
function setFormatFunction(moduleExports: any) {
59+
if (!format && moduleExports.format) {
60+
format = moduleExports.format;
61+
}
62+
}
63+
const patch = (ConnectionPrototype: mysqlTypes.Connection) => {
64+
if (isWrapped(ConnectionPrototype.query)) {
65+
this._unwrap(ConnectionPrototype, 'query');
66+
}
67+
this._wrap(
68+
ConnectionPrototype,
69+
'query',
70+
this._patchQuery(format, false) as any
71+
);
72+
if (isWrapped(ConnectionPrototype.execute)) {
73+
this._unwrap(ConnectionPrototype, 'execute');
74+
}
75+
this._wrap(
76+
ConnectionPrototype,
77+
'execute',
78+
this._patchQuery(format, true) as any
79+
);
80+
};
81+
const unpatch = (ConnectionPrototype: mysqlTypes.Connection) => {
82+
this._unwrap(ConnectionPrototype, 'query');
83+
this._unwrap(ConnectionPrototype, 'execute');
84+
};
5485
return [
5586
new InstrumentationNodeModuleDefinition(
5687
'mysql2',
57-
['>=1.4.2 <4'],
88+
supportedVersions,
5889
(moduleExports: any) => {
59-
const ConnectionPrototype: mysqlTypes.Connection =
60-
getConnectionPrototypeToInstrument(moduleExports.Connection);
61-
if (isWrapped(ConnectionPrototype.query)) {
62-
this._unwrap(ConnectionPrototype, 'query');
63-
}
64-
this._wrap(
65-
ConnectionPrototype,
66-
'query',
67-
this._patchQuery(moduleExports.format, false) as any
68-
);
69-
70-
if (isWrapped(ConnectionPrototype.execute)) {
71-
this._unwrap(ConnectionPrototype, 'execute');
72-
}
73-
this._wrap(
74-
ConnectionPrototype,
75-
'execute',
76-
this._patchQuery(moduleExports.format, true) as any
77-
);
78-
90+
setFormatFunction(moduleExports);
7991
return moduleExports;
8092
},
81-
(moduleExports: any) => {
82-
if (moduleExports === undefined) return;
83-
const ConnectionPrototype: mysqlTypes.Connection =
84-
moduleExports.Connection.prototype;
85-
this._unwrap(ConnectionPrototype, 'query');
86-
this._unwrap(ConnectionPrototype, 'execute');
87-
}
93+
() => {},
94+
[
95+
new InstrumentationNodeModuleFile(
96+
'mysql2/promise.js',
97+
supportedVersions,
98+
(moduleExports: any) => {
99+
setFormatFunction(moduleExports);
100+
return moduleExports;
101+
},
102+
() => {}
103+
),
104+
new InstrumentationNodeModuleFile(
105+
'mysql2/lib/connection.js',
106+
supportedVersions,
107+
(moduleExports: any) => {
108+
const ConnectionPrototype: mysqlTypes.Connection =
109+
getConnectionPrototypeToInstrument(moduleExports);
110+
patch(ConnectionPrototype);
111+
return moduleExports;
112+
},
113+
(moduleExports: any) => {
114+
if (moduleExports === undefined) return;
115+
const ConnectionPrototype: mysqlTypes.Connection =
116+
getConnectionPrototypeToInstrument(moduleExports);
117+
unpatch(ConnectionPrototype);
118+
}
119+
),
120+
]
88121
),
89122
];
90123
}
91124

92-
private _patchQuery(format: formatType, isPrepared: boolean) {
125+
private _patchQuery(format: formatType | undefined, isPrepared: boolean) {
93126
return (originalQuery: Function): Function => {
94127
const thisPlugin = this;
95128
return function query(

plugins/node/opentelemetry-instrumentation-mysql2/src/utils.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import {
2222
SEMATTRS_NET_PEER_NAME,
2323
SEMATTRS_NET_PEER_PORT,
2424
} from '@opentelemetry/semantic-conventions';
25+
import type * as mysqlTypes from 'mysql2';
26+
27+
type formatType = typeof mysqlTypes.format;
2528

2629
/*
2730
Following types declare an expectation on mysql2 types and define a subset we
@@ -103,14 +106,12 @@ function getJDBCString(
103106
*/
104107
export function getDbStatement(
105108
query: string | Query | QueryOptions,
106-
format: (
107-
sql: string,
108-
values: any[],
109-
stringifyObjects?: boolean,
110-
timeZone?: string
111-
) => string,
109+
format?: formatType,
112110
values?: any[]
113111
): string {
112+
if (!format) {
113+
return typeof query === 'string' ? query : query.sql;
114+
}
114115
if (typeof query === 'string') {
115116
return values ? format(query, values) : query;
116117
} else {

0 commit comments

Comments
 (0)