Skip to content

Commit ee6f9b6

Browse files
committed
fix(instrumentation-mysql2)!: do not include parameterized values to db.statement span attribute (#1758)
1 parent 6158af0 commit ee6f9b6

File tree

3 files changed

+184
-105
lines changed

3 files changed

+184
-105
lines changed

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

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ import {
3939
/** @knipignore */
4040
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
4141

42-
type formatType = typeof mysqlTypes.format;
43-
4442
export class MySQL2Instrumentation extends InstrumentationBase<MySQL2InstrumentationConfig> {
4543
static readonly COMMON_ATTRIBUTES = {
4644
[SEMATTRS_DB_SYSTEM]: DBSYSTEMVALUES_MYSQL,
@@ -64,7 +62,7 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
6462
this._wrap(
6563
ConnectionPrototype,
6664
'query',
67-
this._patchQuery(moduleExports.format, false) as any
65+
this._patchQuery(false) as any
6866
);
6967

7068
if (isWrapped(ConnectionPrototype.execute)) {
@@ -73,7 +71,7 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
7371
this._wrap(
7472
ConnectionPrototype,
7573
'execute',
76-
this._patchQuery(moduleExports.format, true) as any
74+
this._patchQuery(true) as any
7775
);
7876

7977
return moduleExports;
@@ -82,14 +80,18 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
8280
if (moduleExports === undefined) return;
8381
const ConnectionPrototype: mysqlTypes.Connection =
8482
moduleExports.Connection.prototype;
85-
this._unwrap(ConnectionPrototype, 'query');
86-
this._unwrap(ConnectionPrototype, 'execute');
83+
if (isWrapped(ConnectionPrototype.query)) {
84+
this._unwrap(ConnectionPrototype, 'query');
85+
}
86+
if (isWrapped(ConnectionPrototype.execute)) {
87+
this._unwrap(ConnectionPrototype, 'execute');
88+
}
8789
}
8890
),
8991
];
9092
}
9193

92-
private _patchQuery(format: formatType, isPrepared: boolean) {
94+
private _patchQuery(isPrepared: boolean) {
9395
return (originalQuery: Function): Function => {
9496
const thisPlugin = this;
9597
return function query(
@@ -110,7 +112,7 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
110112
attributes: {
111113
...MySQL2Instrumentation.COMMON_ATTRIBUTES,
112114
...getConnectionAttributes(this.config),
113-
[SEMATTRS_DB_STATEMENT]: getDbStatement(query, format, values),
115+
[SEMATTRS_DB_STATEMENT]: getDbStatement(query, values),
114116
},
115117
});
116118

@@ -150,11 +152,10 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
150152
);
151153
}
152154
}
153-
154155
span.end();
155156
});
156157

157-
if (arguments.length === 1) {
158+
if (typeof arguments[arguments.length - 1] !== 'function') {
158159
if (typeof (query as any).onResult === 'function') {
159160
thisPlugin._wrap(
160161
query as any,
@@ -178,22 +179,13 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
178179
});
179180

180181
return streamableQuery;
181-
}
182-
183-
if (typeof arguments[1] === 'function') {
182+
} else {
184183
thisPlugin._wrap(
185184
arguments,
186-
1,
187-
thisPlugin._patchCallbackQuery(endSpan)
188-
);
189-
} else if (typeof arguments[2] === 'function') {
190-
thisPlugin._wrap(
191-
arguments,
192-
2,
185+
arguments.length - 1,
193186
thisPlugin._patchCallbackQuery(endSpan)
194187
);
195188
}
196-
197189
return originalQuery.apply(this, arguments);
198190
};
199191
};

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

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,14 @@ interface QueryOptions {
3636
values?: any | any[] | { [param: string]: any };
3737
}
3838

39-
interface Query {
40-
sql: string;
41-
}
42-
4339
interface Config {
4440
host?: string;
4541
port?: number;
4642
database?: string;
4743
user?: string;
4844
connectionConfig?: Config;
4945
}
46+
5047
/**
5148
* Get an Attributes map from a mysql connection config object
5249
*
@@ -97,29 +94,28 @@ function getJDBCString(
9794
}
9895

9996
/**
100-
* Conjures up the value for the db.statement attribute by formatting a SQL query.
101-
*
102-
* @returns the database statement being executed.
97+
* Returns a string representing the SQL query that is appropriate to return
98+
* in telemetry. If there are no `values` then, to be cautious, it is assumed
99+
* the query SQL may include sensitive data and `undefined` is returned, so
100+
* that no DB statement is included in telemetry.
103101
*/
104102
export function getDbStatement(
105-
query: string | Query | QueryOptions,
106-
format: (
107-
sql: string,
108-
values: any[],
109-
stringifyObjects?: boolean,
110-
timeZone?: string
111-
) => string,
103+
query: string | QueryOptions,
112104
values?: any[]
113-
): string {
105+
): string | undefined {
106+
let statement = '';
114107
if (typeof query === 'string') {
115-
return values ? format(query, values) : query;
108+
if (!values) {
109+
return;
110+
}
111+
statement = query;
116112
} else {
117-
// According to https://github.com/mysqljs/mysql#performing-queries
118-
// The values argument will override the values in the option object.
119-
return values || (query as QueryOptions).values
120-
? format(query.sql, values || (query as QueryOptions).values)
121-
: query.sql;
113+
if (!query.values && !values) {
114+
return;
115+
}
116+
statement = query.sql;
122117
}
118+
return statement;
123119
}
124120

125121
/**
@@ -128,7 +124,7 @@ export function getDbStatement(
128124
*
129125
* @returns SQL statement without variable arguments or SQL verb
130126
*/
131-
export function getSpanName(query: string | Query | QueryOptions): string {
127+
export function getSpanName(query: string | QueryOptions): string {
132128
const rawQuery = typeof query === 'object' ? query.sql : query;
133129
// Extract the SQL verb
134130
const firstSpace = rawQuery?.indexOf(' ');

0 commit comments

Comments
 (0)