@opentelemetry/instrumentation-mysql
Advanced tools
Comparing version 0.32.0 to 0.33.0
import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; | ||
import type * as mysqlTypes from 'mysql'; | ||
import { MySQLInstrumentationConfig } from './types'; | ||
import { MeterProvider } from '@opentelemetry/api'; | ||
export declare class MySQLInstrumentation extends InstrumentationBase<typeof mysqlTypes> { | ||
@@ -8,7 +9,12 @@ static readonly COMMON_ATTRIBUTES: { | ||
}; | ||
private _connectionsUsage; | ||
constructor(config?: MySQLInstrumentationConfig); | ||
setMeterProvider(meterProvider: MeterProvider): void; | ||
private _setMetricInstruments; | ||
protected init(): InstrumentationNodeModuleDefinition<typeof mysqlTypes>[]; | ||
private _patchCreateConnection; | ||
private _patchCreatePool; | ||
private _patchPoolEnd; | ||
private _patchCreatePoolCluster; | ||
private _patchAdd; | ||
private _patchGetConnection; | ||
@@ -18,3 +24,4 @@ private _getConnectionCallbackPatchFn; | ||
private _patchCallbackQuery; | ||
private _setPoolcallbacks; | ||
} | ||
//# sourceMappingURL=instrumentation.d.ts.map |
@@ -22,2 +22,3 @@ "use strict"; | ||
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); | ||
const AttributeNames_1 = require("./AttributeNames"); | ||
const utils_1 = require("./utils"); | ||
@@ -28,3 +29,15 @@ const version_1 = require("./version"); | ||
super('@opentelemetry/instrumentation-mysql', version_1.VERSION, config); | ||
this._setMetricInstruments(); | ||
} | ||
setMeterProvider(meterProvider) { | ||
super.setMeterProvider(meterProvider); | ||
this._setMetricInstruments(); | ||
} | ||
_setMetricInstruments() { | ||
this._connectionsUsage = this.meter.createUpDownCounter('db.client.connections.usage', //TODO:: use semantic convention | ||
{ | ||
description: 'The number of connections that are currently in the state referenced by the attribute "state".', | ||
unit: '{connections}', | ||
}); | ||
} | ||
init() { | ||
@@ -35,16 +48,16 @@ return [ | ||
api_1.diag.debug('Patching mysql.createConnection'); | ||
if (instrumentation_1.isWrapped(moduleExports.createConnection)) { | ||
if ((0, instrumentation_1.isWrapped)(moduleExports.createConnection)) { | ||
this._unwrap(moduleExports, 'createConnection'); | ||
} | ||
this._wrap(moduleExports, 'createConnection', this._patchCreateConnection(moduleExports.format)); | ||
this._wrap(moduleExports, 'createConnection', this._patchCreateConnection()); | ||
api_1.diag.debug('Patching mysql.createPool'); | ||
if (instrumentation_1.isWrapped(moduleExports.createPool)) { | ||
if ((0, instrumentation_1.isWrapped)(moduleExports.createPool)) { | ||
this._unwrap(moduleExports, 'createPool'); | ||
} | ||
this._wrap(moduleExports, 'createPool', this._patchCreatePool(moduleExports.format)); | ||
this._wrap(moduleExports, 'createPool', this._patchCreatePool()); | ||
api_1.diag.debug('Patching mysql.createPoolCluster'); | ||
if (instrumentation_1.isWrapped(moduleExports.createPoolCluster)) { | ||
if ((0, instrumentation_1.isWrapped)(moduleExports.createPoolCluster)) { | ||
this._unwrap(moduleExports, 'createPoolCluster'); | ||
} | ||
this._wrap(moduleExports, 'createPoolCluster', this._patchCreatePoolCluster(moduleExports.format)); | ||
this._wrap(moduleExports, 'createPoolCluster', this._patchCreatePoolCluster()); | ||
return moduleExports; | ||
@@ -61,3 +74,3 @@ }, moduleExports => { | ||
// global export function | ||
_patchCreateConnection(format) { | ||
_patchCreateConnection() { | ||
return (originalCreateConnection) => { | ||
@@ -69,3 +82,3 @@ const thisPlugin = this; | ||
// This is unwrapped on next call after unpatch | ||
thisPlugin._wrap(originalResult, 'query', thisPlugin._patchQuery(originalResult, format)); | ||
thisPlugin._wrap(originalResult, 'query', thisPlugin._patchQuery(originalResult)); | ||
return originalResult; | ||
@@ -76,3 +89,3 @@ }; | ||
// global export function | ||
_patchCreatePool(format) { | ||
_patchCreatePool() { | ||
return (originalCreatePool) => { | ||
@@ -83,4 +96,6 @@ const thisPlugin = this; | ||
const pool = originalCreatePool(...arguments); | ||
thisPlugin._wrap(pool, 'query', thisPlugin._patchQuery(pool, format)); | ||
thisPlugin._wrap(pool, 'getConnection', thisPlugin._patchGetConnection(pool, format)); | ||
thisPlugin._wrap(pool, 'query', thisPlugin._patchQuery(pool)); | ||
thisPlugin._wrap(pool, 'getConnection', thisPlugin._patchGetConnection(pool)); | ||
thisPlugin._wrap(pool, 'end', thisPlugin._patchPoolEnd(pool)); | ||
thisPlugin._setPoolcallbacks(pool, thisPlugin, ''); | ||
return pool; | ||
@@ -90,4 +105,25 @@ }; | ||
} | ||
_patchPoolEnd(pool) { | ||
return (originalPoolEnd) => { | ||
const thisPlugin = this; | ||
api_1.diag.debug('MySQLInstrumentation#patch: patched mysql pool end'); | ||
return function end(callback) { | ||
const nAll = pool._allConnections.length; | ||
const nFree = pool._freeConnections.length; | ||
const nUsed = nAll - nFree; | ||
const poolName = (0, utils_1.getPoolName)(pool); | ||
thisPlugin._connectionsUsage.add(-nUsed, { | ||
state: 'used', | ||
name: poolName, | ||
}); | ||
thisPlugin._connectionsUsage.add(-nFree, { | ||
state: 'idle', | ||
name: poolName, | ||
}); | ||
originalPoolEnd.apply(pool, arguments); | ||
}; | ||
}; | ||
} | ||
// global export function | ||
_patchCreatePoolCluster(format) { | ||
_patchCreatePoolCluster() { | ||
return (originalCreatePoolCluster) => { | ||
@@ -99,3 +135,4 @@ const thisPlugin = this; | ||
// This is unwrapped on next call after unpatch | ||
thisPlugin._wrap(cluster, 'getConnection', thisPlugin._patchGetConnection(cluster, format)); | ||
thisPlugin._wrap(cluster, 'getConnection', thisPlugin._patchGetConnection(cluster)); | ||
thisPlugin._wrap(cluster, 'add', thisPlugin._patchAdd(cluster)); | ||
return cluster; | ||
@@ -105,4 +142,26 @@ }; | ||
} | ||
_patchAdd(cluster) { | ||
return (originalAdd) => { | ||
const thisPlugin = this; | ||
api_1.diag.debug('MySQLInstrumentation#patch: patched mysql pool cluster add'); | ||
return function add(id, config) { | ||
// Unwrap if unpatch has been called | ||
if (!thisPlugin['_enabled']) { | ||
thisPlugin._unwrap(cluster, 'add'); | ||
return originalAdd.apply(cluster, arguments); | ||
} | ||
originalAdd.apply(cluster, arguments); | ||
const nodes = cluster['_nodes']; | ||
if (nodes) { | ||
const nodeId = typeof id === 'object' | ||
? 'CLUSTER::' + cluster._lastId | ||
: String(id); | ||
const pool = nodes[nodeId].pool; | ||
thisPlugin._setPoolcallbacks(pool, thisPlugin, id); | ||
} | ||
}; | ||
}; | ||
} | ||
// method on cluster or pool | ||
_patchGetConnection(pool, format) { | ||
_patchGetConnection(pool) { | ||
return (originalGetConnection) => { | ||
@@ -118,11 +177,11 @@ const thisPlugin = this; | ||
if (arguments.length === 1 && typeof arg1 === 'function') { | ||
const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg1, format); | ||
const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg1); | ||
return originalGetConnection.call(pool, patchFn); | ||
} | ||
if (arguments.length === 2 && typeof arg2 === 'function') { | ||
const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg2, format); | ||
const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg2); | ||
return originalGetConnection.call(pool, arg1, patchFn); | ||
} | ||
if (arguments.length === 3 && typeof arg3 === 'function') { | ||
const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg3, format); | ||
const patchFn = thisPlugin._getConnectionCallbackPatchFn(arg3); | ||
return originalGetConnection.call(pool, arg1, arg2, patchFn); | ||
@@ -134,3 +193,3 @@ } | ||
} | ||
_getConnectionCallbackPatchFn(cb, format) { | ||
_getConnectionCallbackPatchFn(cb) { | ||
const thisPlugin = this; | ||
@@ -142,4 +201,4 @@ const activeContext = api_1.context.active(); | ||
// no need to unwrap | ||
if (!instrumentation_1.isWrapped(connection.query)) { | ||
thisPlugin._wrap(connection, 'query', thisPlugin._patchQuery(connection, format)); | ||
if (!(0, instrumentation_1.isWrapped)(connection.query)) { | ||
thisPlugin._wrap(connection, 'query', thisPlugin._patchQuery(connection)); | ||
} | ||
@@ -152,3 +211,3 @@ } | ||
} | ||
_patchQuery(connection, format) { | ||
_patchQuery(connection) { | ||
return (originalQuery) => { | ||
@@ -162,14 +221,18 @@ const thisPlugin = this; | ||
} | ||
const span = thisPlugin.tracer.startSpan(utils_1.getSpanName(query), { | ||
const span = thisPlugin.tracer.startSpan((0, utils_1.getSpanName)(query), { | ||
kind: api_1.SpanKind.CLIENT, | ||
attributes: Object.assign(Object.assign({}, MySQLInstrumentation.COMMON_ATTRIBUTES), utils_1.getConnectionAttributes(connection.config)), | ||
attributes: Object.assign(Object.assign({}, MySQLInstrumentation.COMMON_ATTRIBUTES), (0, utils_1.getConnectionAttributes)(connection.config)), | ||
}); | ||
let values; | ||
if (Array.isArray(_valuesOrCallback)) { | ||
values = _valuesOrCallback; | ||
span.setAttribute(semantic_conventions_1.SemanticAttributes.DB_STATEMENT, (0, utils_1.getDbStatement)(query)); | ||
const instrumentationConfig = thisPlugin.getConfig(); | ||
if (instrumentationConfig.enhancedDatabaseReporting) { | ||
let values; | ||
if (Array.isArray(_valuesOrCallback)) { | ||
values = _valuesOrCallback; | ||
} | ||
else if (arguments[2]) { | ||
values = [_valuesOrCallback]; | ||
} | ||
span.setAttribute(AttributeNames_1.AttributeNames.MYSQL_VALUES, (0, utils_1.getDbValues)(query, values)); | ||
} | ||
else if (arguments[2]) { | ||
values = [_valuesOrCallback]; | ||
} | ||
span.setAttribute(semantic_conventions_1.SemanticAttributes.DB_STATEMENT, utils_1.getDbStatement(query, format, values)); | ||
const cbIndex = Array.from(arguments).findIndex(arg => typeof arg === 'function'); | ||
@@ -214,2 +277,32 @@ const parentContext = api_1.context.active(); | ||
} | ||
_setPoolcallbacks(pool, thisPlugin, id) { | ||
//TODO:: use semantic convention | ||
const poolName = id || (0, utils_1.getPoolName)(pool); | ||
pool.on('connection', connection => { | ||
thisPlugin._connectionsUsage.add(1, { | ||
state: 'idle', | ||
name: poolName, | ||
}); | ||
}); | ||
pool.on('acquire', connection => { | ||
thisPlugin._connectionsUsage.add(-1, { | ||
state: 'idle', | ||
name: poolName, | ||
}); | ||
thisPlugin._connectionsUsage.add(1, { | ||
state: 'used', | ||
name: poolName, | ||
}); | ||
}); | ||
pool.on('release', connection => { | ||
thisPlugin._connectionsUsage.add(-1, { | ||
state: 'used', | ||
name: poolName, | ||
}); | ||
thisPlugin._connectionsUsage.add(1, { | ||
state: 'idle', | ||
name: poolName, | ||
}); | ||
}); | ||
} | ||
} | ||
@@ -216,0 +309,0 @@ exports.MySQLInstrumentation = MySQLInstrumentation; |
import { InstrumentationConfig } from '@opentelemetry/instrumentation'; | ||
export declare type MySQLInstrumentationConfig = InstrumentationConfig; | ||
export interface MySQLInstrumentationConfig extends InstrumentationConfig { | ||
/** | ||
* If true, an attribute containing the query's parameters will be attached | ||
* the spans generated to represent the query. | ||
*/ | ||
enhancedDatabaseReporting?: boolean; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
import { SpanAttributes } from '@opentelemetry/api'; | ||
import type { ConnectionConfig, PoolActualConfig, Query, QueryOptions } from 'mysql'; | ||
import * as mysqlTypes from 'mysql'; | ||
/** | ||
@@ -10,7 +11,6 @@ * Get an SpanAttributes map from a mysql connection config object | ||
/** | ||
* Conjures up the value for the db.statement attribute by formatting a SQL query. | ||
* | ||
* @returns the database statement being executed. | ||
*/ | ||
export declare function getDbStatement(query: string | Query | QueryOptions, format: (sql: string, values: any[], stringifyObjects?: boolean, timeZone?: string) => string, values?: any[]): string; | ||
export declare function getDbStatement(query: string | Query | QueryOptions): string; | ||
export declare function getDbValues(query: string | Query | QueryOptions, values?: any[]): string; | ||
/** | ||
@@ -23,2 +23,4 @@ * The span name SHOULD be set to a low cardinality value | ||
export declare function getSpanName(query: string | Query | QueryOptions): string; | ||
export declare function arrayStringifyHelper(arr: Array<unknown> | undefined): string; | ||
export declare function getPoolName(pool: mysqlTypes.Pool): string; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -18,3 +18,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getSpanName = exports.getDbStatement = exports.getConnectionAttributes = void 0; | ||
exports.getPoolName = exports.arrayStringifyHelper = exports.getSpanName = exports.getDbValues = exports.getDbStatement = exports.getConnectionAttributes = void 0; | ||
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); | ||
@@ -52,19 +52,24 @@ /** | ||
/** | ||
* Conjures up the value for the db.statement attribute by formatting a SQL query. | ||
* | ||
* @returns the database statement being executed. | ||
*/ | ||
function getDbStatement(query, format, values) { | ||
function getDbStatement(query) { | ||
if (typeof query === 'string') { | ||
return values ? format(query, values) : query; | ||
return query; | ||
} | ||
else { | ||
return query.sql; | ||
} | ||
} | ||
exports.getDbStatement = getDbStatement; | ||
function getDbValues(query, values) { | ||
if (typeof query === 'string') { | ||
return arrayStringifyHelper(values); | ||
} | ||
else { | ||
// According to https://github.com/mysqljs/mysql#performing-queries | ||
// The values argument will override the values in the option object. | ||
return values || query.values | ||
? format(query.sql, values || query.values) | ||
: query.sql; | ||
return arrayStringifyHelper(values || query.values); | ||
} | ||
} | ||
exports.getDbStatement = getDbStatement; | ||
exports.getDbValues = getDbValues; | ||
/** | ||
@@ -83,2 +88,21 @@ * The span name SHOULD be set to a low cardinality value | ||
exports.getSpanName = getSpanName; | ||
function arrayStringifyHelper(arr) { | ||
if (arr) | ||
return `[${arr.toString()}]`; | ||
return ''; | ||
} | ||
exports.arrayStringifyHelper = arrayStringifyHelper; | ||
function getPoolName(pool) { | ||
const c = pool.config.connectionConfig; | ||
let poolName = ''; | ||
poolName += c.host ? `host: '${c.host}', ` : ''; | ||
poolName += c.port ? `port: ${c.port}, ` : ''; | ||
poolName += c.database ? `database: '${c.database}', ` : ''; | ||
poolName += c.user ? `user: '${c.user}'` : ''; | ||
if (!c.user) { | ||
poolName = poolName.substring(0, poolName.length - 2); //omit last comma | ||
} | ||
return poolName.trim(); | ||
} | ||
exports.getPoolName = getPoolName; | ||
//# sourceMappingURL=utils.js.map |
@@ -1,2 +0,2 @@ | ||
export declare const VERSION = "0.32.0"; | ||
export declare const VERSION = "0.33.0"; | ||
//# sourceMappingURL=version.d.ts.map |
@@ -20,3 +20,3 @@ "use strict"; | ||
// this is autogenerated file, see scripts/version-update.js | ||
exports.VERSION = '0.32.0'; | ||
exports.VERSION = '0.33.0'; | ||
//# sourceMappingURL=version.js.map |
{ | ||
"name": "@opentelemetry/instrumentation-mysql", | ||
"version": "0.32.0", | ||
"version": "0.33.0", | ||
"description": "OpenTelemetry mysql automatic instrumentation package.", | ||
@@ -17,3 +17,3 @@ "main": "build/src/index.js", | ||
"tdd": "npm run test -- --watch-extensions ts --watch", | ||
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'", | ||
"test": "nyc ts-mocha tsconfig.json 'test/**/*.test.ts'", | ||
"version:update": "node ../../../scripts/version-update.js", | ||
@@ -52,3 +52,4 @@ "compile:examples": "cd examples && npm run compile" | ||
"@opentelemetry/context-async-hooks": "^1.8.0", | ||
"@opentelemetry/contrib-test-utils": "^0.33.0", | ||
"@opentelemetry/contrib-test-utils": "^0.33.1", | ||
"@opentelemetry/sdk-metrics": "^1.8.0", | ||
"@opentelemetry/sdk-trace-base": "^1.8.0", | ||
@@ -60,16 +61,16 @@ "@types/mocha": "7.0.2", | ||
"mocha": "7.2.0", | ||
"mysql": "2.18.1", | ||
"nyc": "15.1.0", | ||
"rimraf": "3.0.2", | ||
"sinon": "14.0.0", | ||
"sinon": "15.0.1", | ||
"ts-mocha": "10.0.0", | ||
"typescript": "4.3.5" | ||
"typescript": "4.4.4" | ||
}, | ||
"dependencies": { | ||
"@opentelemetry/instrumentation": "^0.34.0", | ||
"@opentelemetry/instrumentation": "^0.35.1", | ||
"@opentelemetry/semantic-conventions": "^1.0.0", | ||
"@types/mysql": "2.15.19" | ||
"@types/mysql": "2.15.19", | ||
"mysql": "2.18.1" | ||
}, | ||
"homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-mysql#readme", | ||
"gitHead": "59fa57cfd0dff4ae0e6f3833dff73c55dfd79ee5" | ||
"gitHead": "63e0fc9b6b862f74304abf0343c506a5bd415191" | ||
} |
@@ -45,2 +45,9 @@ # OpenTelemetry mysql Instrumentation for Node.js | ||
### MySQL instrumentation Options | ||
| Options | Type | Default | Description | | ||
| ------- | ---- | --------| ----------- | | ||
| [`enhancedDatabaseReporting`](./src/types.ts#L24) | `boolean` | `false` | If true, an attribute containing the query's parameters will be attached the spans generated to represent the query | | ||
| | ||
## Useful links | ||
@@ -47,0 +54,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
53696
21
550
67
5
+ Addedmysql@2.18.1
+ Added@opentelemetry/instrumentation@0.35.1(transitive)
+ Addedbignumber.js@9.0.0(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedmysql@2.18.1(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedreadable-stream@2.3.7(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedsqlstring@2.3.1(transitive)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
- Removed@opentelemetry/instrumentation@0.34.0(transitive)