Skip to content

Commit 382680c

Browse files
committed
- Incorporate semantic conventions suggestions.
1 parent 365d523 commit 382680c

File tree

4 files changed

+340
-126
lines changed

4 files changed

+340
-126
lines changed

plugins/node/opentelemetry-instrumentation-oracledb/src/OracleTelemetryTraceHandler.ts

Lines changed: 95 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,18 @@ import {
3131
ATTR_SERVER_ADDRESS,
3232
SEMATTRS_NET_TRANSPORT,
3333
SEMATTRS_DB_USER,
34-
SEMATTRS_DB_STATEMENT,
3534
} from '@opentelemetry/semantic-conventions';
3635
import {
3736
ATTR_DB_SYSTEM,
3837
ATTR_DB_NAMESPACE,
3938
ATTR_DB_OPERATION_NAME,
39+
ATTR_DB_STATEMENT,
40+
ATTR_DB_OPERATION_PARAMETER,
4041
} from './semconv';
4142

4243
import type * as oracleDBTypes from 'oracledb';
4344
type TraceHandlerBaseCtor = new () => any;
45+
const OUT_BIND = 3003; // bindinfo direction value.
4446

4547
// Local modules.
4648
import { AttributeNames } from './constants';
@@ -98,19 +100,31 @@ export function getOracleTelemetryTraceHandlerClass(
98100
);
99101
}
100102

103+
// It returns db.namespace as mentioned in semantic conventions
104+
// Ex: ORCL1|PDB1|db_high.adb.oraclecloud.com
105+
private _getDBNameSpace(
106+
instanceName?: string,
107+
pdbName?: string,
108+
serviceName?: string
109+
): string | undefined {
110+
if (instanceName == null && pdbName == null && serviceName == null) {
111+
return undefined;
112+
}
113+
return `${instanceName ?? ''}|${pdbName ?? ''}|${serviceName ?? ''}`;
114+
}
115+
101116
// Returns the connection related Attributes for
102117
// semantic standards and module custom keys.
103118
private _getConnectionSpanAttributes(config: SpanConnectionConfig) {
104119
return {
105120
[ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_ORACLE,
106121
[SEMATTRS_NET_TRANSPORT]: config.protocol,
107122
[SEMATTRS_DB_USER]: config.user,
108-
[AttributeNames.ORACLE_INSTANCE]: config.instanceName,
109-
[AttributeNames.ORACLE_PDBNAME]: config.pdbName,
110-
[AttributeNames.ORACLE_POOL_MIN]: config.poolMin,
111-
[AttributeNames.ORACLE_POOL_MAX]: config.poolMax,
112-
[AttributeNames.ORACLE_POOL_INCR]: config.poolIncrement,
113-
[ATTR_DB_NAMESPACE]: config.serviceName,
123+
[ATTR_DB_NAMESPACE]: this._getDBNameSpace(
124+
config.instanceName,
125+
config.pdbName,
126+
config.serviceName
127+
),
114128
[SEMATTRS_DB_CONNECTION_STRING]: config.connectString,
115129
[ATTR_SERVER_ADDRESS]: config.hostName,
116130
[ATTR_SERVER_PORT]: config.port,
@@ -126,36 +140,68 @@ export function getOracleTelemetryTraceHandlerClass(
126140
);
127141
}
128142

129-
// Transforms the bind values array into string values.
143+
// Transforms the bind values array or bindinfo into an object
144+
// 'db.operation.parameter'.
145+
// Ex:
146+
// db.operation.parameter.0 = "someval" // for bind by position
147+
// db.operation.parameter.name = "someval" // for bind by name
130148
// It is only called if config 'enhancedDatabaseReporting' is true.
131149
private _getValues(values: any) {
132-
let convertedValues;
150+
const convertedValues: Record<string, string> = {};
151+
133152
try {
134153
if (Array.isArray(values)) {
135-
// bind by position
136-
convertedValues = values.map(value => {
137-
if (value == null) {
138-
return 'null';
139-
} else if (
140-
value instanceof Buffer ||
141-
this._isLobInstance(value)
142-
) {
143-
return value.toString();
144-
} else if (typeof value === 'object') {
145-
return JSON.stringify(value);
146-
} else {
147-
// number, string, boolean,
148-
return value.toString();
154+
// Handle indexed (positional) parameters
155+
values.forEach((value, index) => {
156+
const key = `${ATTR_DB_OPERATION_PARAMETER}.${index}`;
157+
const extractedValue = this._extractValue(value);
158+
if (extractedValue !== undefined) {
159+
convertedValues[key] = extractedValue;
149160
}
150161
});
151-
return convertedValues;
162+
} else if (values && typeof values === 'object') {
163+
// Handle named parameters
164+
for (const [paramName, value] of Object.entries(values)) {
165+
const key = `${ATTR_DB_OPERATION_PARAMETER}.${paramName}`;
166+
let inVal: any = value;
167+
168+
if (inVal && typeof inVal === 'object') {
169+
// Check bind info if present.
170+
if (inVal.dir === OUT_BIND) {
171+
// outbinds
172+
convertedValues[key] = '';
173+
continue;
174+
}
175+
if ('val' in inVal) {
176+
inVal = inVal.val;
177+
}
178+
}
179+
const extractedValue = this._extractValue(inVal);
180+
if (extractedValue !== undefined) {
181+
convertedValues[key] = extractedValue;
182+
}
183+
}
152184
}
153185
} catch (e) {
154186
diag.error('failed to stringify bind values:', values, e);
155187
}
156188
return convertedValues;
157189
}
158190

191+
private _extractValue(value: any): string | undefined {
192+
if (value == null) {
193+
return 'null';
194+
}
195+
if (value instanceof Buffer || this._isLobInstance(value)) {
196+
return value.toString();
197+
}
198+
if (typeof value === 'object') {
199+
return JSON.stringify(value);
200+
}
201+
// number, string, boolean,
202+
return value.toString();
203+
}
204+
159205
// Updates the call level attributes in span.
160206
// roundTrip flag will skip dumping bind values for
161207
// internal roundtrip spans generated for oracledb exported functions.
@@ -176,14 +222,14 @@ export function getOracleTelemetryTraceHandlerClass(
176222
this._instrumentConfig.dbStatementDump ||
177223
this._instrumentConfig.enhancedDatabaseReporting
178224
) {
179-
span.setAttribute(SEMATTRS_DB_STATEMENT, callConfig.statement);
225+
span.setAttribute(ATTR_DB_STATEMENT, callConfig.statement);
180226
if (
181227
this._instrumentConfig.enhancedDatabaseReporting &&
182228
!roundTrip
183229
) {
184230
const values = this._getValues(callConfig.values);
185231
if (values) {
186-
span.setAttribute(AttributeNames.ORACLE_BIND_VALUES, values);
232+
span.setAttributes(values);
187233
}
188234
}
189235
}
@@ -233,13 +279,30 @@ export function getOracleTelemetryTraceHandlerClass(
233279
}
234280
}
235281

236-
// Updates the spanName with suffix, serviceName seperated by delimiter, space
237-
// Ex: 'oracledb.Pool.getConnection freepdb'
238-
// This function is called when connectLevelConfig has serviceName populated.
282+
// Updates the spanName following the format
283+
// {FunctionName:[sqlCommand] db.namespace}
284+
// Ex: 'oracledb.Pool.getConnection:[SELECT] ORCL1|PDB1|db_high.adb.oraclecloud.com'
285+
// This function is called when connectLevelConfig has required paramters populated.
239286
private _updateSpanName(traceContext: TraceSpanData) {
240-
const dbName = traceContext.connectLevelConfig?.serviceName ?? '';
241-
traceContext.userContext.span.updateName(
242-
`${traceContext.operation}${dbName ? ` ${dbName}` : ''}`
287+
const { connectLevelConfig, callLevelConfig, userContext, operation } =
288+
traceContext;
289+
if (
290+
![
291+
SpanNames.EXECUTE,
292+
SpanNames.EXECUTE_MANY,
293+
SpanNames.EXECUTE_MSG,
294+
].includes(operation as SpanNames)
295+
) {
296+
// Ignore for connection establishment functions.
297+
return;
298+
}
299+
300+
const { instanceName, pdbName, serviceName } = connectLevelConfig;
301+
const dbName = this._getDBNameSpace(instanceName, pdbName, serviceName);
302+
const sqlCommand =
303+
callLevelConfig?.statement?.split(' ')[0].toUpperCase() || '';
304+
userContext.span.updateName(
305+
`${operation}:${sqlCommand}${dbName && ` ${dbName}`}`
243306
);
244307
}
245308

plugins/node/opentelemetry-instrumentation-oracledb/src/constants.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@
1919
// Oracle specific attributes not covered by standard semantic conventions.
2020
// See: https://github.com/open-telemetry/semantic-conventions/pull/1911
2121
export enum AttributeNames {
22-
ORACLE_BIND_VALUES = 'oracledb.bind_values',
23-
ORACLE_POOL_MIN = 'oracledb.pool_min',
24-
ORACLE_POOL_MAX = 'oracledb.pool_max',
25-
ORACLE_POOL_INCR = 'oracledb.pool_incr',
26-
ORACLE_INSTANCE = 'oracledb.instance',
27-
ORACLE_PDBNAME = 'oracledb.pdbname',
28-
ORACLE_IMPLICIT_RELEASE = 'oracledb.implicit_release',
22+
ORACLE_IMPLICIT_RELEASE = 'oracle.db.implicit_release',
2923
}
3024

3125
// Contains span names produced by instrumentation

plugins/node/opentelemetry-instrumentation-oracledb/src/semconv.ts

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
export const ATTR_DB_SYSTEM = 'db.system.name';
2525

2626
/**
27-
* The servicename associated with the connection.
27+
* The database associated with the connection, qualified by the instance name, database name and service name.
2828
*
29-
* @example FREEPDB1
30-
* @example inventory.example.org
29+
* @example ORCL1|PDB1|db_high.adb.oraclecloud.com
30+
* @example ORCL1|DB1|db_low.adb.oraclecloud.com
3131
*
32-
* @note If a database system has multiple namespace components, they **SHOULD** be concatenated (potentially using database system specific conventions) from most general to most specific namespace component, and more specific namespaces **SHOULD NOT** be captured without the more general namespaces, to ensure that "startswith" queries for the more general namespaces will be valid.
33-
* Semantic conventions for individual database systems **SHOULD** document what `db.namespace` means in the context of that system.
34-
* It is **RECOMMENDED** to capture the value as provided by the application without attempting to do any case normalization.
32+
* @note It **SHOULD** be set to the combination of instance name, database name and
33+
* service name following the `{instance_name}|{database_name}|{service_name}` pattern.
34+
* For CDB architecture, database name would be pdb name. For Non-CDB, it would be
35+
* **DB_NAME** parameter.
3536
* This attribute has stability level RELEASE CANDIDATE.
3637
*
3738
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
@@ -53,3 +54,42 @@ export const ATTR_DB_NAMESPACE = 'db.namespace';
5354
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
5455
*/
5556
export const ATTR_DB_OPERATION_NAME = 'db.operation.name';
57+
58+
/**
59+
* The database query being executed.
60+
*
61+
* @example SELECT * FROM wuser_table where username = :1 // bind by position
62+
* @example SELECT * FROM wuser_table where username = :name // bind by name
63+
* @example SELECT * FROM wuser_table where username = 'John' // literals
64+
*
65+
* @note For sanitization see [Sanitization of `db.query.text`](../database/database-spans.md#sanitization-of-dbquerytext).
66+
* For batch operations, if the individual operations are known to have the same query text then
67+
* that query text **SHOULD** be used, otherwise all of the individual query texts **SHOULD**
68+
* be concatenated with separator `; ` or some other database system specific separator if more applicable.
69+
*
70+
* Non-parameterized or Parameterized query text **SHOULD NOT** be collected by default unless
71+
* explicitly configured and sanitized to exclude sensitive data, e.g. by redacting all
72+
* literal values present in the query text. See Sanitization of `db.query.text`.
73+
*
74+
* Parameterized query text MUST also NOT be collected by default unless explicitly configured.
75+
* The query parameter values themselves are opt-in, see [`db.operation.parameter.<key>`](../attributes-registry/db.md))
76+
*
77+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
78+
*/
79+
export const ATTR_DB_STATEMENT = 'db.query.text';
80+
81+
/**
82+
* A database operation parameter, with <key> being the parameter name,
83+
* and the attribute value being a string representation of the parameter value.
84+
*
85+
* @example someval
86+
* @example 55
87+
*
88+
* @note If a parameter has no name and instead is referenced only by index, then
89+
* <key> **SHOULD** be the 0-based index. If `db.query.text` is also captured, then
90+
* `db.operation.parameter.<key>` **SHOULD** match up with the parameterized placeholders
91+
* present in db.query.text
92+
*
93+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
94+
*/
95+
export const ATTR_DB_OPERATION_PARAMETER = 'db.operation.parameter';

0 commit comments

Comments
 (0)