Skip to content

Commit

Permalink
feat: implement mysql semantic conventions
Browse files Browse the repository at this point in the history
Signed-off-by: Naseem <[email protected]>
  • Loading branch information
Naseem committed Sep 8, 2020
1 parent fb86c6f commit b3e29eb
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 218 deletions.
1 change: 1 addition & 0 deletions plugins/node/opentelemetry-plugin-mysql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"devDependencies": {
"@opentelemetry/context-async-hooks": "0.10.2",
"@opentelemetry/node": "0.10.2",
"@opentelemetry/semantic-conventions": "^0.10.2",
"@opentelemetry/test-utils": "^0.9.0",
"@opentelemetry/tracing": "0.10.2",
"@types/mocha": "7.0.2",
Expand Down
36 changes: 0 additions & 36 deletions plugins/node/opentelemetry-plugin-mysql/src/enums.ts

This file was deleted.

41 changes: 17 additions & 24 deletions plugins/node/opentelemetry-plugin-mysql/src/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,19 @@

import { BasePlugin, isWrapped } from '@opentelemetry/core';
import { CanonicalCode, Span, SpanKind } from '@opentelemetry/api';
import * as mysqlTypes from 'mysql';
import type * as mysqlTypes from 'mysql';
import * as shimmer from 'shimmer';
import { AttributeNames } from './enums';
import { getConnectionAttributes, getSpanName } from './utils';
import { getConnectionAttributes, getDbStatement } from './utils';
import { VERSION } from './version';
import { DatabaseAttribute } from '@opentelemetry/semantic-conventions';

export class MysqlPlugin extends BasePlugin<typeof mysqlTypes> {
readonly supportedVersions = ['2.*'];

static readonly COMPONENT = 'mysql';
static readonly DB_TYPE = 'sql';

static readonly COMMON_ATTRIBUTES = {
[AttributeNames.COMPONENT]: MysqlPlugin.COMPONENT,
[AttributeNames.DB_TYPE]: MysqlPlugin.DB_TYPE,
[AttributeNames.PEER_SERVICE]: MysqlPlugin.COMPONENT,
[DatabaseAttribute.DB_SYSTEM]: MysqlPlugin.COMPONENT,
};

private _enabled = false;
Expand Down Expand Up @@ -198,6 +195,8 @@ export class MysqlPlugin extends BasePlugin<typeof mysqlTypes> {
const thisPlugin = this;
thisPlugin._logger.debug('MysqlPlugin: patched mysql query');

const format = this._moduleExports.format;

return function query(
query: string | mysqlTypes.Query | mysqlTypes.QueryOptions,
_valuesOrCallback?: unknown[] | mysqlTypes.queryCallback,
Expand All @@ -208,28 +207,27 @@ export class MysqlPlugin extends BasePlugin<typeof mysqlTypes> {
return originalQuery.apply(connection, arguments);
}

const spanName = getSpanName(query);

const span = thisPlugin._tracer.startSpan(spanName, {
const span = thisPlugin._tracer.startSpan(`${query}`, {
kind: SpanKind.CLIENT,
attributes: {
...MysqlPlugin.COMMON_ATTRIBUTES,
...getConnectionAttributes(connection.config),
},
});

if (typeof query === 'string') {
span.setAttribute(AttributeNames.DB_STATEMENT, query);
} else if (typeof query === 'object') {
if (query.sql) {
span.setAttribute(AttributeNames.DB_STATEMENT, query.sql);
}
let values;

if (query.values) {
span.setAttribute(AttributeNames.MYSQL_VALUES, query.values);
}
if (Array.isArray(_valuesOrCallback)) {
values = _valuesOrCallback;
} else if (arguments[2]) {
values = [_valuesOrCallback];
}

span.setAttribute(
DatabaseAttribute.DB_STATEMENT,
getDbStatement(query, format, values)
);

if (arguments.length === 1) {
const streamableQuery: mysqlTypes.Query = originalQuery.apply(
connection,
Expand All @@ -251,11 +249,6 @@ export class MysqlPlugin extends BasePlugin<typeof mysqlTypes> {
if (typeof arguments[1] === 'function') {
shimmer.wrap(arguments, 1, thisPlugin._patchCallbackQuery(span));
} else if (typeof arguments[2] === 'function') {
if (Array.isArray(_valuesOrCallback)) {
span.setAttribute(AttributeNames.MYSQL_VALUES, _valuesOrCallback);
} else if (arguments[2]) {
span.setAttribute(AttributeNames.MYSQL_VALUES, [_valuesOrCallback]);
}
shimmer.wrap(arguments, 2, thisPlugin._patchCallbackQuery(span));
}

Expand Down
19 changes: 0 additions & 19 deletions plugins/node/opentelemetry-plugin-mysql/src/types.ts

This file was deleted.

75 changes: 41 additions & 34 deletions plugins/node/opentelemetry-plugin-mysql/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,16 @@
*/

import { Attributes } from '@opentelemetry/api';
import { AttributeNames } from './enums';
import { Query } from './types';
import type { ConnectionConfig, PoolActualConfig } from 'mysql';

/**
* Get a span name from a mysql query
*
* @param query mysql Query or string
*/
export function getSpanName(query: string | Query) {
return `mysql.query:${getCommand(query)}`;
}

/**
* Get the low cardinality command name from a query.
*
* @param query mysql Query or string
*/
function getCommand(query: string | Query) {
const queryString = typeof query === 'string' ? query : query.sql;

if (!queryString) {
return 'UNKNOWN_COMMAND';
}

// Command is the first non-whitespace token in the query
const match = queryString.match(/^\s*(\w+)/);
return (match && match[1]) || 'UNKNOWN_COMMAND';
}
import {
DatabaseAttribute,
GeneralAttribute,
} from '@opentelemetry/semantic-conventions';
import type {
ConnectionConfig,
PoolActualConfig,
Query,
QueryOptions,
} from 'mysql';

/**
* Get an Attributes map from a mysql connection config object
Expand All @@ -56,11 +37,11 @@ export function getConnectionAttributes(
const { host, port, database, user } = getConfig(config);

return {
[AttributeNames.PEER_ADDRESS]: getJDBCString(host, port, database),
[AttributeNames.DB_INSTANCE]: database,
[AttributeNames.PEER_HOSTNAME]: host,
[AttributeNames.PEER_PORT]: port,
[AttributeNames.DB_USER]: user,
[GeneralAttribute.NET_PEER_HOSTNAME]: host,
[GeneralAttribute.NET_PEER_PORT]: port,
[GeneralAttribute.NET_PEER_ADDRESS]: getJDBCString(host, port, database),
[DatabaseAttribute.DB_NAME]: database,
[DatabaseAttribute.DB_USER]: user,
};
}

Expand All @@ -87,3 +68,29 @@ function getJDBCString(

return jdbcString;
}

/**
* Conjures up the value for the db.statement attribute by formatting a SQL query.
*
* @returns the database statement being executed.
*/
export function getDbStatement(
query: string | Query | QueryOptions,
format: (
sql: string,
values: any[],
stringifyObjects?: boolean,
timeZone?: string
) => string,
values?: any[]
): string {
if (typeof query === 'string') {
return values ? format(query, values) : query;
} 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;
}
}
Loading

0 comments on commit b3e29eb

Please sign in to comment.