diff --git a/plugins/node/instrumentation-tedious/test/instrumentation.test.ts b/plugins/node/instrumentation-tedious/test/instrumentation.test.ts index 0f8f8a74b6..5c18e8404d 100644 --- a/plugins/node/instrumentation-tedious/test/instrumentation.test.ts +++ b/plugins/node/instrumentation-tedious/test/instrumentation.test.ts @@ -16,7 +16,10 @@ import { context, trace, SpanStatusCode, SpanKind } from '@opentelemetry/api'; import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import * as util from 'util'; import * as testUtils from '@opentelemetry/contrib-test-utils'; import { @@ -334,7 +337,10 @@ function assertSpan(span: ReadableSpan, expected: any) { assert(span); assert.strictEqual(span.name, expected.name); assert.strictEqual(span.kind, SpanKind.CLIENT); - assert.strictEqual(span.attributes[SemanticAttributes.DB_SYSTEM], 'mssql'); + assert.strictEqual( + span.attributes[SemanticAttributes.DB_SYSTEM], + DbSystemValues.MSSQL + ); assert.strictEqual( span.attributes[SemanticAttributes.DB_NAME], expected.database ?? database diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/dynamodb.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/dynamodb.ts index e6f23bd42a..0cf383f27f 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/dynamodb.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/dynamodb.ts @@ -15,7 +15,10 @@ */ import { Span, SpanKind, Tracer } from '@opentelemetry/api'; import { RequestMetadata, ServiceExtension } from './ServiceExtension'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import { AwsSdkInstrumentationConfig, NormalizedRequest, @@ -30,7 +33,7 @@ export class DynamodbServiceExtension implements ServiceExtension { const operation = normalizedRequest.commandName; const spanAttributes = { - [SemanticAttributes.DB_SYSTEM]: 'dynamodb', + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.DYNAMODB, [SemanticAttributes.DB_NAME]: normalizedRequest.commandInput?.TableName, [SemanticAttributes.DB_OPERATION]: operation, [SemanticAttributes.DB_STATEMENT]: JSON.stringify( @@ -61,7 +64,7 @@ export class DynamodbServiceExtension implements ServiceExtension { const operation = response.request.commandName; if (operation === 'BatchGetItem') { - if ('ConsumedCapacity' in response.data) { + if (Array.isArray(response.data?.ConsumedCapacity)) { span.setAttribute( SemanticAttributes.AWS_DYNAMODB_CONSUMED_CAPACITY, response.data.ConsumedCapacity.map( diff --git a/plugins/node/opentelemetry-instrumentation-aws-sdk/test/dynamodb.test.ts b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/dynamodb.test.ts index dab0144f4f..41b715770a 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-sdk/test/dynamodb.test.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-sdk/test/dynamodb.test.ts @@ -23,7 +23,10 @@ import * as AWS from 'aws-sdk'; import { AWSError } from 'aws-sdk'; import { mockV2AwsSend } from './testing-utils'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import * as expect from 'expect'; import type { ConsumedCapacity as ConsumedCapacityV2 } from 'aws-sdk/clients/dynamodb'; import type { ConsumedCapacity as ConsumedCapacityV3 } from '@aws-sdk/client-dynamodb'; @@ -73,7 +76,9 @@ describe('DynamoDB', () => { const spans = getTestSpans(); expect(spans.length).toStrictEqual(1); const attrs = spans[0].attributes; - expect(attrs[SemanticAttributes.DB_SYSTEM]).toStrictEqual('dynamodb'); + expect(attrs[SemanticAttributes.DB_SYSTEM]).toStrictEqual( + DbSystemValues.DYNAMODB + ); expect(attrs[SemanticAttributes.DB_NAME]).toStrictEqual('test-table'); expect(attrs[SemanticAttributes.DB_OPERATION]).toStrictEqual('Query'); expect( @@ -120,7 +125,9 @@ describe('DynamoDB', () => { const spans = getTestSpans(); expect(spans.length).toStrictEqual(1); const attrs = spans[0].attributes; - expect(attrs[SemanticAttributes.DB_SYSTEM]).toStrictEqual('dynamodb'); + expect(attrs[SemanticAttributes.DB_SYSTEM]).toStrictEqual( + DbSystemValues.DYNAMODB + ); expect(attrs[SemanticAttributes.DB_OPERATION]).toStrictEqual( 'BatchGetItem' ); @@ -165,7 +172,9 @@ describe('DynamoDB', () => { const spans = getTestSpans(); expect(spans.length).toStrictEqual(1); const attrs = spans[0].attributes; - expect(attrs[SemanticAttributes.DB_SYSTEM]).toStrictEqual('dynamodb'); + expect(attrs[SemanticAttributes.DB_SYSTEM]).toStrictEqual( + DbSystemValues.DYNAMODB + ); expect(attrs[SemanticAttributes.DB_OPERATION]).toStrictEqual( 'BatchGetItem' ); @@ -187,5 +196,50 @@ describe('DynamoDB', () => { } ); }); + + it('should populate BatchGetIem when consumedCapacity is undefined', done => { + mockV2AwsSend(responseMockSuccess, { + Responses: { 'test-table': [{ key1: { S: 'val1' } }] }, + UnprocessedKeys: {}, + ConsumedCapacity: undefined, + } as AWS.DynamoDB.Types.BatchGetItemOutput); + + const dynamodb = new AWS.DynamoDB.DocumentClient(); + const dynamodb_params = { + RequestItems: { + 'test-table': { + Keys: [{ key1: { S: 'val1' } }], + ProjectionExpression: 'id', + }, + }, + ReturnConsumedCapacity: 'NONE', + }; + dynamodb.batchGet( + dynamodb_params, + ( + err: AWSError, + data: AWS.DynamoDB.DocumentClient.BatchGetItemOutput + ) => { + const spans = getTestSpans(); + expect(spans.length).toStrictEqual(1); + const attrs = spans[0].attributes; + expect(attrs[SemanticAttributes.DB_SYSTEM]).toStrictEqual('dynamodb'); + expect(attrs[SemanticAttributes.DB_OPERATION]).toStrictEqual( + 'BatchGetItem' + ); + expect( + attrs[SemanticAttributes.AWS_DYNAMODB_TABLE_NAMES] + ).toStrictEqual(['test-table']); + expect( + attrs[SemanticAttributes.AWS_DYNAMODB_CONSUMED_CAPACITY] + ).toBeUndefined(); + expect( + JSON.parse(attrs[SemanticAttributes.DB_STATEMENT] as string) + ).toEqual(dynamodb_params); + expect(err).toBeFalsy(); + done(); + } + ); + }); }); }); diff --git a/plugins/node/opentelemetry-instrumentation-ioredis/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-ioredis/src/instrumentation.ts index f2cb655187..dcd067bd85 100644 --- a/plugins/node/opentelemetry-instrumentation-ioredis/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-ioredis/src/instrumentation.ts @@ -22,7 +22,10 @@ import { isWrapped, } from '@opentelemetry/instrumentation'; import { IORedisInstrumentationConfig, IORedisCommand } from './types'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import { safeExecuteInTheMiddle } from '@opentelemetry/instrumentation'; import { endSpan, defaultDbStatementSerializer } from './utils'; import { VERSION } from './version'; @@ -34,8 +37,6 @@ const DEFAULT_CONFIG: IORedisInstrumentationConfig = { export class IORedisInstrumentation extends InstrumentationBase< typeof ioredisTypes > { - static readonly DB_SYSTEM = 'redis'; - constructor(_config: IORedisInstrumentationConfig = {}) { super( '@opentelemetry/instrumentation-ioredis', @@ -113,7 +114,7 @@ export class IORedisInstrumentation extends InstrumentationBase< const span = instrumentation.tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, attributes: { - [SemanticAttributes.DB_SYSTEM]: IORedisInstrumentation.DB_SYSTEM, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.REDIS, [SemanticAttributes.DB_STATEMENT]: dbStatementSerializer( cmd.name, cmd.args @@ -186,7 +187,7 @@ export class IORedisInstrumentation extends InstrumentationBase< const span = instrumentation.tracer.startSpan('connect', { kind: SpanKind.CLIENT, attributes: { - [SemanticAttributes.DB_SYSTEM]: IORedisInstrumentation.DB_SYSTEM, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.REDIS, [SemanticAttributes.DB_STATEMENT]: 'connect', }, }); diff --git a/plugins/node/opentelemetry-instrumentation-ioredis/test/ioredis.test.ts b/plugins/node/opentelemetry-instrumentation-ioredis/test/ioredis.test.ts index 879d729653..b57adcb76b 100644 --- a/plugins/node/opentelemetry-instrumentation-ioredis/test/ioredis.test.ts +++ b/plugins/node/opentelemetry-instrumentation-ioredis/test/ioredis.test.ts @@ -39,7 +39,10 @@ import { DbStatementSerializer, IORedisRequestHookInformation, } from '../src/types'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; const memoryExporter = new InMemorySpanExporter(); @@ -51,7 +54,7 @@ const CONFIG = { const URL = `redis://${CONFIG.host}:${CONFIG.port}`; const DEFAULT_ATTRIBUTES = { - [SemanticAttributes.DB_SYSTEM]: IORedisInstrumentation.DB_SYSTEM, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.REDIS, [SemanticAttributes.NET_PEER_NAME]: CONFIG.host, [SemanticAttributes.NET_PEER_PORT]: CONFIG.port, [SemanticAttributes.DB_CONNECTION_STRING]: URL, diff --git a/plugins/node/opentelemetry-instrumentation-knex/src/utils.ts b/plugins/node/opentelemetry-instrumentation-knex/src/utils.ts index ec8c9bdb5d..6a54e7c878 100644 --- a/plugins/node/opentelemetry-instrumentation-knex/src/utils.ts +++ b/plugins/node/opentelemetry-instrumentation-knex/src/utils.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { DbSystemValues } from '@opentelemetry/semantic-conventions'; + type Exception = { new (message: string): Exception; constructor: Exception; @@ -50,8 +52,8 @@ export const cloneErrorWithNewMessage = (err: Exception, message: string) => { }; const systemMap = new Map([ - ['sqlite3', 'sqlite'], - ['pg', 'postgresql'], + ['sqlite3', DbSystemValues.SQLITE], + ['pg', DbSystemValues.POSTGRESQL], ]); export const mapSystem = (knexSystem: string) => { return systemMap.get(knexSystem) || knexSystem; diff --git a/plugins/node/opentelemetry-instrumentation-memcached/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-memcached/src/instrumentation.ts index 7ffca113b9..2c7b22b2ad 100644 --- a/plugins/node/opentelemetry-instrumentation-memcached/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-memcached/src/instrumentation.ts @@ -21,7 +21,10 @@ import { InstrumentationNodeModuleDefinition, } from '@opentelemetry/instrumentation'; import type * as Memcached from 'memcached'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import * as utils from './utils'; import { InstrumentationConfig } from './types'; import { VERSION } from './version'; @@ -29,7 +32,7 @@ import { VERSION } from './version'; export class Instrumentation extends InstrumentationBase { static readonly COMPONENT = 'memcached'; static readonly COMMON_ATTRIBUTES = { - [SemanticAttributes.DB_SYSTEM]: Instrumentation.COMPONENT, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.MEMCACHED, }; static readonly DEFAULT_CONFIG: InstrumentationConfig = { enhancedDatabaseReporting: false, diff --git a/plugins/node/opentelemetry-instrumentation-memcached/test/index.test.ts b/plugins/node/opentelemetry-instrumentation-memcached/test/index.test.ts index 1c86a16d54..34e4ae807f 100644 --- a/plugins/node/opentelemetry-instrumentation-memcached/test/index.test.ts +++ b/plugins/node/opentelemetry-instrumentation-memcached/test/index.test.ts @@ -25,7 +25,10 @@ import { import type * as Memcached from 'memcached'; import * as assert from 'assert'; import Instrumentation from '../src'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import * as util from 'util'; const instrumentation = new Instrumentation(); @@ -37,7 +40,7 @@ const CONFIG = { }; const DEFAULT_ATTRIBUTES = { - [SemanticAttributes.DB_SYSTEM]: Instrumentation.COMPONENT, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.MEMCACHED, [SemanticAttributes.NET_PEER_NAME]: CONFIG.host, [SemanticAttributes.NET_PEER_PORT]: CONFIG.port, }; diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-mongodb/src/instrumentation.ts index cc539d9406..5e078f9ccd 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/src/instrumentation.ts @@ -29,7 +29,10 @@ import { isWrapped, safeExecuteInTheMiddle, } from '@opentelemetry/instrumentation'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import type * as mongodb from 'mongodb'; import { CursorState, @@ -563,7 +566,7 @@ export class MongoDBInstrumentation extends InstrumentationBase< ) { // add database related attributes span.setAttributes({ - [SemanticAttributes.DB_SYSTEM]: 'mongodb', + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.MONGODB, [SemanticAttributes.DB_NAME]: dbName, [SemanticAttributes.DB_MONGODB_COLLECTION]: dbCollection, }); diff --git a/plugins/node/opentelemetry-instrumentation-mysql/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-mysql/src/instrumentation.ts index 00968d7dce..795e55b9f7 100644 --- a/plugins/node/opentelemetry-instrumentation-mysql/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-mysql/src/instrumentation.ts @@ -26,7 +26,10 @@ import { InstrumentationNodeModuleDefinition, isWrapped, } from '@opentelemetry/instrumentation'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import type * as mysqlTypes from 'mysql'; import { MySQLInstrumentationConfig } from './types'; import { getConnectionAttributes, getDbStatement, getSpanName } from './utils'; @@ -42,9 +45,8 @@ type getConnectionCallbackType = ( export class MySQLInstrumentation extends InstrumentationBase< typeof mysqlTypes > { - static readonly COMPONENT = 'mysql'; static readonly COMMON_ATTRIBUTES = { - [SemanticAttributes.DB_SYSTEM]: MySQLInstrumentation.COMPONENT, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.MYSQL, }; constructor(config?: MySQLInstrumentationConfig) { diff --git a/plugins/node/opentelemetry-instrumentation-mysql/test/index.test.ts b/plugins/node/opentelemetry-instrumentation-mysql/test/index.test.ts index 438f875499..3f3c9c808f 100644 --- a/plugins/node/opentelemetry-instrumentation-mysql/test/index.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mysql/test/index.test.ts @@ -16,7 +16,10 @@ import { context, trace, SpanStatusCode } from '@opentelemetry/api'; import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import * as testUtils from '@opentelemetry/contrib-test-utils'; import { BasicTracerProvider, @@ -716,7 +719,10 @@ function assertSpan( values?: any, errorMessage?: string ) { - assert.strictEqual(span.attributes[SemanticAttributes.DB_SYSTEM], 'mysql'); + assert.strictEqual( + span.attributes[SemanticAttributes.DB_SYSTEM], + DbSystemValues.MYSQL + ); assert.strictEqual(span.attributes[SemanticAttributes.DB_NAME], database); assert.strictEqual(span.attributes[SemanticAttributes.NET_PEER_PORT], port); assert.strictEqual(span.attributes[SemanticAttributes.NET_PEER_NAME], host); diff --git a/plugins/node/opentelemetry-instrumentation-mysql2/README.md b/plugins/node/opentelemetry-instrumentation-mysql2/README.md index dc6ce0ae4a..7fd9096ff9 100644 --- a/plugins/node/opentelemetry-instrumentation-mysql2/README.md +++ b/plugins/node/opentelemetry-instrumentation-mysql2/README.md @@ -43,6 +43,14 @@ registerInstrumentations({ }) ``` +### MySQL2 Instrumentation Options + +You can set the following instrumentation options: + +| Options | Type | Description | +| ------- | ---- | ----------- | +| `responseHook` | `MySQL2InstrumentationExecutionResponseHook` (function) | Function for adding custom attributes from db response | + ## Useful links - For more information on OpenTelemetry, visit: diff --git a/plugins/node/opentelemetry-instrumentation-mysql2/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-mysql2/src/instrumentation.ts index 16f194190d..2fd2c15330 100644 --- a/plugins/node/opentelemetry-instrumentation-mysql2/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-mysql2/src/instrumentation.ts @@ -19,8 +19,12 @@ import { InstrumentationBase, InstrumentationNodeModuleDefinition, isWrapped, + safeExecuteInTheMiddle, } from '@opentelemetry/instrumentation'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import type * as mysqlTypes from 'mysql2'; import { MySQL2InstrumentationConfig } from './types'; import { @@ -36,9 +40,8 @@ type formatType = typeof mysqlTypes.format; export class MySQL2Instrumentation extends InstrumentationBase< typeof mysqlTypes > { - static readonly COMPONENT = 'mysql'; static readonly COMMON_ATTRIBUTES = { - [SemanticAttributes.DB_SYSTEM]: MySQL2Instrumentation.COMPONENT, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.MYSQL, }; constructor(config?: MySQL2InstrumentationConfig) { @@ -107,13 +110,29 @@ export class MySQL2Instrumentation extends InstrumentationBase< ), }, }); - const endSpan = once((err?: any) => { + const endSpan = once((err?: any, results?: any) => { if (err) { span.setStatus({ code: api.SpanStatusCode.ERROR, message: err.message, }); + } else { + const config: MySQL2InstrumentationConfig = thisPlugin._config; + if (typeof config.responseHook === 'function') { + safeExecuteInTheMiddle( + () => { + config.responseHook!(span, { queryResults: results }); + }, + err => { + if (err) { + thisPlugin._diag.warn('Failed executing responseHook', err); + } + }, + true + ); + } } + span.end(); }); @@ -136,8 +155,8 @@ export class MySQL2Instrumentation extends InstrumentationBase< .once('error', err => { endSpan(err); }) - .once('result', () => { - endSpan(); + .once('result', results => { + endSpan(undefined, results); }); return streamableQuery; @@ -169,7 +188,7 @@ export class MySQL2Instrumentation extends InstrumentationBase< results?: any, fields?: mysqlTypes.FieldPacket[] ) { - endSpan(err); + endSpan(err, results); return originalCallback(...arguments); }; }; diff --git a/plugins/node/opentelemetry-instrumentation-mysql2/src/types.ts b/plugins/node/opentelemetry-instrumentation-mysql2/src/types.ts index 16df8a745e..c14743495b 100644 --- a/plugins/node/opentelemetry-instrumentation-mysql2/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-mysql2/src/types.ts @@ -15,5 +15,22 @@ */ import { InstrumentationConfig } from '@opentelemetry/instrumentation'; +import type { Span } from '@opentelemetry/api'; -export type MySQL2InstrumentationConfig = InstrumentationConfig; +export interface MySQL2ResponseHookInformation { + queryResults: any; +} + +export interface MySQL2InstrumentationExecutionResponseHook { + (span: Span, responseHookInfo: MySQL2ResponseHookInformation): void; +} + +export interface MySQL2InstrumentationConfig extends InstrumentationConfig { + /** + * Hook that allows adding custom span attributes based on the data + * returned MySQL2 queries. + * + * @default undefined + */ + responseHook?: MySQL2InstrumentationExecutionResponseHook; +} diff --git a/plugins/node/opentelemetry-instrumentation-mysql2/test/mysql.test.ts b/plugins/node/opentelemetry-instrumentation-mysql2/test/mysql.test.ts index 698d2f14f5..f456ba368c 100644 --- a/plugins/node/opentelemetry-instrumentation-mysql2/test/mysql.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mysql2/test/mysql.test.ts @@ -17,7 +17,10 @@ import * as semver from 'semver'; import { context, trace, SpanStatusCode } from '@opentelemetry/api'; import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import * as testUtils from '@opentelemetry/contrib-test-utils'; import { BasicTracerProvider, @@ -26,7 +29,7 @@ import { SimpleSpanProcessor, } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; -import { MySQL2Instrumentation } from '../src'; +import { MySQL2Instrumentation, MySQL2InstrumentationConfig } from '../src'; const LIB_VERSION = testUtils.getPackageVersion('mysql2'); const port = Number(process.env.MYSQL_PORT) || 33306; @@ -644,6 +647,126 @@ describe('mysql@2.x', () => { ); }); }); + + describe('#responseHook', () => { + const queryResultAttribute = 'query_result'; + + after(() => { + instrumentation.setConfig({}); + }); + + describe('invalid repsonse hook', () => { + before(() => { + instrumentation.disable(); + instrumentation.setTracerProvider(provider); + const config: MySQL2InstrumentationConfig = { + responseHook: (span, responseHookInfo) => { + throw new Error('random failure!'); + }, + }; + instrumentation.setConfig(config); + instrumentation.enable(); + }); + + it('should not affect the behavior of the query', done => { + const span = provider.getTracer('default').startSpan('test span'); + context.with(trace.setSpan(context.active(), span), () => { + const sql = 'SELECT 1+1 as solution'; + connection.query(sql, (err, res: mysqlTypes.RowDataPacket[]) => { + assert.ifError(err); + assert.ok(res); + assert.strictEqual(res[0].solution, 2); + done(); + }); + }); + }); + }); + + describe('valid response hook', () => { + before(() => { + instrumentation.disable(); + instrumentation.setTracerProvider(provider); + const config: MySQL2InstrumentationConfig = { + responseHook: (span, responseHookInfo) => { + span.setAttribute( + queryResultAttribute, + JSON.stringify(responseHookInfo.queryResults) + ); + }, + }; + instrumentation.setConfig(config); + instrumentation.enable(); + }); + + it('should extract data from responseHook - connection', done => { + const span = provider.getTracer('default').startSpan('test span'); + context.with(trace.setSpan(context.active(), span), () => { + const sql = 'SELECT 1+1 as solution'; + connection.query(sql, (err, res: mysqlTypes.RowDataPacket[]) => { + assert.ifError(err); + assert.ok(res); + assert.strictEqual(res[0].solution, 2); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + assertSpan(spans[0], sql); + assert.strictEqual( + spans[0].attributes[queryResultAttribute], + JSON.stringify(res) + ); + done(); + }); + }); + }); + + it('should extract data from responseHook - pool', done => { + const span = provider.getTracer('default').startSpan('test span'); + context.with(trace.setSpan(context.active(), span), () => { + const sql = 'SELECT 1+1 as solution'; + pool.getConnection((err, conn) => { + conn.query(sql, (err, res: mysqlTypes.RowDataPacket[]) => { + assert.ifError(err); + assert.ok(res); + assert.strictEqual(res[0].solution, 2); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + assertSpan(spans[0], sql); + assert.strictEqual( + spans[0].attributes[queryResultAttribute], + JSON.stringify(res) + ); + done(); + }); + }); + }); + }); + + it('should extract data from responseHook - poolCluster', done => { + poolCluster.getConnection((err, poolClusterConnection) => { + assert.ifError(err); + const span = provider.getTracer('default').startSpan('test span'); + context.with(trace.setSpan(context.active(), span), () => { + const sql = 'SELECT 1+1 as solution'; + poolClusterConnection.query( + sql, + (err, res: mysqlTypes.RowDataPacket[]) => { + assert.ifError(err); + assert.ok(res); + assert.strictEqual(res[0].solution, 2); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + assertSpan(spans[0], sql); + assert.strictEqual( + spans[0].attributes[queryResultAttribute], + JSON.stringify(res) + ); + done(); + } + ); + }); + }); + }); + }); + }); }); function assertSpan( @@ -652,7 +775,10 @@ function assertSpan( values?: any, errorMessage?: string ) { - assert.strictEqual(span.attributes[SemanticAttributes.DB_SYSTEM], 'mysql'); + assert.strictEqual( + span.attributes[SemanticAttributes.DB_SYSTEM], + DbSystemValues.MYSQL + ); assert.strictEqual(span.attributes[SemanticAttributes.DB_NAME], database); assert.strictEqual(span.attributes[SemanticAttributes.NET_PEER_PORT], port); assert.strictEqual(span.attributes[SemanticAttributes.NET_PEER_NAME], host); diff --git a/plugins/node/opentelemetry-instrumentation-redis/src/utils.ts b/plugins/node/opentelemetry-instrumentation-redis/src/utils.ts index 2e8aaa944a..4f74b17a19 100644 --- a/plugins/node/opentelemetry-instrumentation-redis/src/utils.ts +++ b/plugins/node/opentelemetry-instrumentation-redis/src/utils.ts @@ -32,7 +32,10 @@ import { } from './types'; import { EventEmitter } from 'events'; import { RedisInstrumentation } from './'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; import { safeExecuteInTheMiddle } from '@opentelemetry/instrumentation'; const endSpan = (span: Span, err?: Error | null) => { @@ -102,7 +105,7 @@ export const getTracedInternalSendCommand = ( { kind: SpanKind.CLIENT, attributes: { - [SemanticAttributes.DB_SYSTEM]: RedisInstrumentation.COMPONENT, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.REDIS, [SemanticAttributes.DB_STATEMENT]: dbStatementSerializer( cmd.command, cmd.args diff --git a/plugins/node/opentelemetry-instrumentation-redis/test/redis.test.ts b/plugins/node/opentelemetry-instrumentation-redis/test/redis.test.ts index a5f491520d..09842809fc 100644 --- a/plugins/node/opentelemetry-instrumentation-redis/test/redis.test.ts +++ b/plugins/node/opentelemetry-instrumentation-redis/test/redis.test.ts @@ -31,7 +31,10 @@ import { } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import { RedisInstrumentation } from '../src'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { + DbSystemValues, + SemanticAttributes, +} from '@opentelemetry/semantic-conventions'; const instrumentation = new RedisInstrumentation(); instrumentation.enable(); @@ -50,7 +53,7 @@ const CONFIG = { const URL = `redis://${CONFIG.host}:${CONFIG.port}`; const DEFAULT_ATTRIBUTES = { - [SemanticAttributes.DB_SYSTEM]: RedisInstrumentation.COMPONENT, + [SemanticAttributes.DB_SYSTEM]: DbSystemValues.REDIS, [SemanticAttributes.NET_PEER_NAME]: CONFIG.host, [SemanticAttributes.NET_PEER_PORT]: CONFIG.port, [SemanticAttributes.DB_CONNECTION_STRING]: URL,