Skip to content

Commit d5cd88f

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

File tree

3 files changed

+191
-108
lines changed

3 files changed

+191
-108
lines changed

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

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

41-
type formatType = typeof mysqlTypes.format;
42-
4341
export class MySQL2Instrumentation extends InstrumentationBase<MySQL2InstrumentationConfig> {
4442
static readonly COMMON_ATTRIBUTES = {
4543
[SEMATTRS_DB_SYSTEM]: DBSYSTEMVALUES_MYSQL,
@@ -63,7 +61,7 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
6361
this._wrap(
6462
ConnectionPrototype,
6563
'query',
66-
this._patchQuery(moduleExports.format, false) as any
64+
this._patchQuery(false) as any
6765
);
6866

6967
if (isWrapped(ConnectionPrototype.execute)) {
@@ -72,7 +70,7 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
7270
this._wrap(
7371
ConnectionPrototype,
7472
'execute',
75-
this._patchQuery(moduleExports.format, true) as any
73+
this._patchQuery(true) as any
7674
);
7775

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

91-
private _patchQuery(format: formatType, isPrepared: boolean) {
93+
private _patchQuery(isPrepared: boolean) {
9294
return (originalQuery: Function): Function => {
9395
const thisPlugin = this;
9496
return function query(
@@ -109,7 +111,7 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
109111
attributes: {
110112
...MySQL2Instrumentation.COMMON_ATTRIBUTES,
111113
...getConnectionAttributes(this.config),
112-
[SEMATTRS_DB_STATEMENT]: getDbStatement(query, format, values),
114+
[SEMATTRS_DB_STATEMENT]: getDbStatement(query, values),
113115
},
114116
});
115117

@@ -149,11 +151,10 @@ export class MySQL2Instrumentation extends InstrumentationBase<MySQL2Instrumenta
149151
);
150152
}
151153
}
152-
153154
span.end();
154155
});
155156

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

179180
return streamableQuery;
180-
}
181-
182-
if (typeof arguments[1] === 'function') {
181+
} else {
183182
thisPlugin._wrap(
184183
arguments,
185-
1,
186-
thisPlugin._patchCallbackQuery(endSpan)
187-
);
188-
} else if (typeof arguments[2] === 'function') {
189-
thisPlugin._wrap(
190-
arguments,
191-
2,
184+
arguments.length - 1,
192185
thisPlugin._patchCallbackQuery(endSpan)
193186
);
194187
}
195-
196188
return originalQuery.apply(this, arguments);
197189
};
198190
};

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)