Skip to content

Commit 4384c7b

Browse files
maryliagtrentm
andauthored
feat(instrumentation-pg): update to stable semantic conventions with db-migration support (#2881)
Co-authored-by: Trent Mick <[email protected]>
1 parent bcf32cd commit 4384c7b

File tree

10 files changed

+499
-167
lines changed

10 files changed

+499
-167
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/instrumentation-pg/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,48 @@ PostgreSQL instrumentation has few options available to choose from. You can set
5151
| `requireParentSpan` | `boolean` | If true, requires a parent span to create new spans (default false) |
5252
| `addSqlCommenterCommentToQueries` | `boolean` | If true, adds [sqlcommenter](https://github.com/open-telemetry/opentelemetry-sqlcommenter) specification compliant comment to queries with tracing context (default false). _NOTE: A comment will not be added to queries that already contain `--` or `/* ... */` in them, even if these are not actually part of comments_ |
5353

54+
## Semantic Conventions
55+
56+
Prior to version `0.55.0`, this instrumentation created spans and metrics targeting an experimental semantic convention Version 1.27.0.
57+
58+
Database semantic conventions (semconv) were stabilized in v1.34.0, and a [migration process](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/non-normative/db-migration.md) was defined.
59+
`@opentelemetry/instrumentation-pg` versions 0.55.0 and later include support for migrating to stable Database semantic conventions, as described below.
60+
The intent is to provide an approximate 6 month time window for users of this instrumentation to migrate to the new Database semconv, after which a new minor version will use the new semconv by default and drop support for the old semconv.
61+
62+
To select which semconv version(s) is emitted from this instrumentation, use the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable.
63+
64+
- `database`: emit the new (stable) v1.34.0+ semantics
65+
- `database/dup`: emit **both** the old v1.27.0 and the new (stable) v1.34.0+ semantics
66+
- By default, if `OTEL_SEMCONV_STABILITY_OPT_IN` includes neither of the above tokens, the old v1.27.0 semconv is used.
67+
68+
### Attributes collected
69+
70+
| v1.27.0 semconv | v1.34.0 semconv | Short Description |
71+
| ----------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
72+
| `db.connection_string` | Removed | String used to connect to the database |
73+
| `db.user` | Removed | User used to connect to the database |
74+
| `db.name` | Removed, integrated into the new `db.namespace` | The name of the database. |
75+
| (not included) | `db.namespace` | The name of the database, fully qualified within the server address and port. |
76+
| `db.statement` | `db.query.text` | The database query being executed. |
77+
| `db.system` | `db.system.name` | The database management system (DBMS) product as identified by the client instrumentation. |
78+
| `net.peer.port` | `server.port` | Remote port number. |
79+
| `net.peer.name` | `server.address` | Remote hostname or similar. |
80+
81+
Metrics Exported:
82+
83+
- [`db.client.operation.duration`](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/database-metrics.md#metric-dbclientoperationduration)
84+
85+
### Upgrading Semantic Conventions
86+
87+
When upgrading to the new semantic conventions, it is recommended to do so in the following order:
88+
89+
1. Upgrade `@opentelemetry/opentelemetry-instrumentation-pg` to the latest version
90+
2. Set `OTEL_SEMCONV_STABILITY_OPT_IN=database/dup` to emit both old and new semantic conventions
91+
3. Modify alerts, dashboards, metrics, and other processes to expect the new semantic conventions
92+
4. Set `OTEL_SEMCONV_STABILITY_OPT_IN=database` to emit only the new semantic conventions
93+
94+
This will cause both the old and new semantic conventions to be emitted during the transition period.
95+
5496
## Useful links
5597

5698
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>

packages/instrumentation-pg/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"dependencies": {
7676
"@opentelemetry/core": "^2.0.0",
7777
"@opentelemetry/instrumentation": "^0.203.0",
78-
"@opentelemetry/semantic-conventions": "^1.27.0",
78+
"@opentelemetry/semantic-conventions": "^1.34.0",
7979
"@opentelemetry/sql-common": "^0.41.0",
8080
"@types/pg": "8.15.4",
8181
"@types/pg-pool": "2.0.6"

packages/instrumentation-pg/src/instrumentation.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
InstrumentationNodeModuleDefinition,
2020
safeExecuteInTheMiddle,
2121
InstrumentationNodeModuleFile,
22+
SemconvStability,
23+
semconvStabilityFromStr,
2224
} from '@opentelemetry/instrumentation';
2325
import {
2426
context,
@@ -54,18 +56,19 @@ import {
5456
hrTimeToMilliseconds,
5557
} from '@opentelemetry/core';
5658
import {
57-
DBSYSTEMVALUES_POSTGRESQL,
58-
SEMATTRS_DB_SYSTEM,
5959
ATTR_ERROR_TYPE,
6060
ATTR_SERVER_PORT,
6161
ATTR_SERVER_ADDRESS,
62+
ATTR_DB_NAMESPACE,
63+
ATTR_DB_OPERATION_NAME,
64+
ATTR_DB_SYSTEM_NAME,
65+
METRIC_DB_CLIENT_OPERATION_DURATION,
6266
} from '@opentelemetry/semantic-conventions';
6367
import {
6468
METRIC_DB_CLIENT_CONNECTION_COUNT,
6569
METRIC_DB_CLIENT_CONNECTION_PENDING_REQUESTS,
66-
METRIC_DB_CLIENT_OPERATION_DURATION,
67-
ATTR_DB_NAMESPACE,
68-
ATTR_DB_OPERATION_NAME,
70+
ATTR_DB_SYSTEM,
71+
DB_SYSTEM_VALUE_POSTGRESQL,
6972
} from './semconv';
7073

7174
function extractModuleExports(module: any) {
@@ -88,9 +91,14 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
8891
idle: 0,
8992
pending: 0,
9093
};
94+
private _semconvStability: SemconvStability;
9195

9296
constructor(config: PgInstrumentationConfig = {}) {
9397
super(PACKAGE_NAME, PACKAGE_VERSION, config);
98+
this._semconvStability = semconvStabilityFromStr(
99+
'database',
100+
process.env.OTEL_SEMCONV_STABILITY_OPT_IN
101+
);
94102
}
95103

96104
override _updateMetricInstruments() {
@@ -247,7 +255,10 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
247255

248256
const span = plugin.tracer.startSpan(SpanNames.CONNECT, {
249257
kind: SpanKind.CLIENT,
250-
attributes: utils.getSemanticAttributesFromConnection(this),
258+
attributes: utils.getSemanticAttributesFromConnection(
259+
this,
260+
plugin._semconvStability
261+
),
251262
});
252263

253264
if (callback) {
@@ -272,14 +283,19 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
272283

273284
private recordOperationDuration(attributes: Attributes, startTime: HrTime) {
274285
const metricsAttributes: Attributes = {};
275-
const keysToCopy = [
276-
SEMATTRS_DB_SYSTEM,
286+
const keysToCopy: string[] = [
277287
ATTR_DB_NAMESPACE,
278288
ATTR_ERROR_TYPE,
279289
ATTR_SERVER_PORT,
280290
ATTR_SERVER_ADDRESS,
281291
ATTR_DB_OPERATION_NAME,
282292
];
293+
if (this._semconvStability & SemconvStability.OLD) {
294+
keysToCopy.push(ATTR_DB_SYSTEM);
295+
}
296+
if (this._semconvStability & SemconvStability.STABLE) {
297+
keysToCopy.push(ATTR_DB_SYSTEM_NAME);
298+
}
283299

284300
keysToCopy.forEach(key => {
285301
if (key in attributes) {
@@ -327,7 +343,7 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
327343
: undefined;
328344

329345
const attributes: Attributes = {
330-
[SEMATTRS_DB_SYSTEM]: DBSYSTEMVALUES_POSTGRESQL,
346+
[ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_POSTGRESQL,
331347
[ATTR_DB_NAMESPACE]: this.database,
332348
[ATTR_SERVER_PORT]: this.connectionParameters.port,
333349
[ATTR_SERVER_ADDRESS]: this.connectionParameters.host,
@@ -348,6 +364,7 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
348364
this,
349365
plugin.tracer,
350366
instrumentationConfig,
367+
plugin._semconvStability,
351368
queryConfig
352369
);
353370

@@ -548,7 +565,10 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
548565
// setup span
549566
const span = plugin.tracer.startSpan(SpanNames.POOL_CONNECT, {
550567
kind: SpanKind.CLIENT,
551-
attributes: utils.getSemanticAttributesFromPool(this.options),
568+
attributes: utils.getSemanticAttributesFromPoolConnection(
569+
this.options,
570+
plugin._semconvStability
571+
),
552572
});
553573

554574
plugin._setPoolConnectEventListeners(this);

packages/instrumentation-pg/src/internal-types.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export type PostgresCallback = (err: Error, res: object) => unknown;
3232
export interface PgParsedConnectionParams {
3333
database?: string;
3434
host?: string;
35+
namespace?: string;
3536
port?: number;
3637
user?: string;
3738
}
@@ -47,17 +48,18 @@ export type PgPoolCallback = (
4748
) => void;
4849

4950
export interface PgPoolOptionsParams {
51+
allowExitOnIdle: boolean;
52+
connectionString?: string; // connection string if provided directly
5053
database: string;
5154
host: string;
52-
port: number;
53-
user: string;
5455
idleTimeoutMillis: number; // the minimum amount of time that an object may sit idle in the pool before it is eligible for eviction due to idle time
55-
maxClient: number; // maximum size of the pool
56-
connectionString?: string; // connection string if provided directly
5756
max: number;
58-
maxUses: number;
59-
allowExitOnIdle: boolean;
57+
maxClient: number; // maximum size of the pool
6058
maxLifetimeSeconds: number;
59+
maxUses: number;
60+
namespace: string;
61+
port: number;
62+
user: string;
6163
}
6264

6365
export const EVENT_LISTENERS_SET = Symbol(

packages/instrumentation-pg/src/semconv.ts

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
* limitations under the License.
1515
*/
1616

17+
/*
18+
* This file contains a copy of unstable semantic convention definitions
19+
* used by this package.
20+
* @see https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions#unstable-semconv
21+
*/
22+
1723
/**
1824
* The name of the connection pool; unique within the instrumented application. In case the connection pool implementation doesn't provide a name, instrumentation **SHOULD** use a combination of parameters that would make the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name following different patterns **SHOULD** document it.
1925
*
@@ -22,7 +28,7 @@
2228
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
2329
*/
2430
export const ATTR_DB_CLIENT_CONNECTION_POOL_NAME =
25-
'db.client.connection.pool.name';
31+
'db.client.connection.pool.name' as const;
2632

2733
/**
2834
* The state of a connection in the pool
@@ -31,70 +37,114 @@ export const ATTR_DB_CLIENT_CONNECTION_POOL_NAME =
3137
*
3238
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
3339
*/
34-
export const ATTR_DB_CLIENT_CONNECTION_STATE = 'db.client.connection.state';
40+
export const ATTR_DB_CLIENT_CONNECTION_STATE =
41+
'db.client.connection.state' as const;
42+
43+
/**
44+
* Deprecated, use `server.address`, `server.port` attributes instead.
45+
*
46+
* @example "Server=(localdb)\\v11.0;Integrated Security=true;"
47+
*
48+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
49+
*
50+
* @deprecated Replaced by `server.address` and `server.port`.
51+
*/
52+
export const ATTR_DB_CONNECTION_STRING = 'db.connection_string' as const;
3553

3654
/**
37-
* The name of the database, fully qualified within the server address and port.
55+
* Deprecated, use `db.namespace` instead.
3856
*
3957
* @example customers
40-
* @example test.users
58+
* @example main
59+
*
60+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
4161
*
42-
* @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.
43-
* Semantic conventions for individual database systems **SHOULD** document what `db.namespace` means in the context of that system.
44-
* It is **RECOMMENDED** to capture the value as provided by the application without attempting to do any case normalization.
45-
* This attribute has stability level RELEASE CANDIDATE.
62+
* @deprecated Replaced by `db.namespace`.
63+
*/
64+
export const ATTR_DB_NAME = 'db.name' as const;
65+
66+
/**
67+
* The database statement being executed.
68+
*
69+
* @example SELECT * FROM wuser_table
70+
* @example SET mykey "WuValue"
4671
*
4772
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
73+
*
74+
* @deprecated Replaced by `db.query.text`.
4875
*/
49-
export const ATTR_DB_NAMESPACE = 'db.namespace';
76+
export const ATTR_DB_STATEMENT = 'db.statement' as const;
5077

5178
/**
52-
* The name of the operation or command being executed.
79+
* Deprecated, use `db.system.name` instead.
80+
*
81+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
5382
*
54-
* @example findAndModify
55-
* @example HMSET
56-
* @example SELECT
83+
* @deprecated Replaced by `db.system.name`.
84+
*/
85+
export const ATTR_DB_SYSTEM = 'db.system' as const;
86+
87+
/**
88+
* Deprecated, no replacement at this time.
5789
*
58-
* @note It is **RECOMMENDED** to capture the value as provided by the application without attempting to do any case normalization.
59-
* If the operation name is parsed from the query text, it **SHOULD** be the first operation name found in the query.
60-
* For batch operations, if the individual operations are known to have the same operation name then that operation name **SHOULD** be used prepended by `BATCH `, otherwise `db.operation.name` **SHOULD** be `BATCH` or some other database system specific term if more applicable.
61-
* This attribute has stability level RELEASE CANDIDATE.
90+
* @example readonly_user
91+
* @example reporting_user
6292
*
6393
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
94+
*
95+
* @deprecated Removed, no replacement at this time.
6496
*/
65-
export const ATTR_DB_OPERATION_NAME = 'db.operation.name';
97+
export const ATTR_DB_USER = 'db.user' as const;
6698

6799
/**
68-
* Enum value "used" for attribute {@link ATTR_DB_CLIENT_CONNECTION_STATE}.
100+
* Deprecated, use `server.address` on client spans and `client.address` on server spans.
101+
*
102+
* @example example.com
103+
*
104+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
105+
*
106+
* @deprecated Replaced by `server.address` on client spans and `client.address` on server spans.
107+
*/
108+
export const ATTR_NET_PEER_NAME = 'net.peer.name' as const;
109+
110+
/**
111+
* Deprecated, use `server.port` on client spans and `client.port` on server spans.
112+
*
113+
* @example 8080
114+
*
115+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
116+
*
117+
* @deprecated Replaced by `server.port` on client spans and `client.port` on server spans.
69118
*/
70-
export const DB_CLIENT_CONNECTION_STATE_VALUE_USED = 'used';
119+
export const ATTR_NET_PEER_PORT = 'net.peer.port' as const;
71120

72121
/**
73122
* Enum value "idle" for attribute {@link ATTR_DB_CLIENT_CONNECTION_STATE}.
74123
*/
75-
export const DB_CLIENT_CONNECTION_STATE_VALUE_IDLE = 'idle';
124+
export const DB_CLIENT_CONNECTION_STATE_VALUE_IDLE = 'idle' as const;
76125

77126
/**
78-
* The number of connections that are currently in state described by the `state` attribute
79-
*
80-
* @experimental This metric is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
127+
* Enum value "used" for attribute {@link ATTR_DB_CLIENT_CONNECTION_STATE}.
81128
*/
82-
export const METRIC_DB_CLIENT_CONNECTION_COUNT = 'db.client.connection.count';
129+
export const DB_CLIENT_CONNECTION_STATE_VALUE_USED = 'used' as const;
83130

84131
/**
85-
* The number of current pending requests for an open connection
132+
* Enum value "postgresql" for attribute {@link ATTR_DB_SYSTEM}.
133+
*/
134+
export const DB_SYSTEM_VALUE_POSTGRESQL = 'postgresql' as const;
135+
136+
/**
137+
* The number of connections that are currently in state described by the `state` attribute
86138
*
87139
* @experimental This metric is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
88140
*/
89-
export const METRIC_DB_CLIENT_CONNECTION_PENDING_REQUESTS =
90-
'db.client.connection.pending_requests';
141+
export const METRIC_DB_CLIENT_CONNECTION_COUNT =
142+
'db.client.connection.count' as const;
91143

92144
/**
93-
* Duration of database client operations.
94-
*
95-
* @note Batch operations **SHOULD** be recorded as a single operation.
145+
* The number of current pending requests for an open connection
96146
*
97147
* @experimental This metric is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
98148
*/
99-
export const METRIC_DB_CLIENT_OPERATION_DURATION =
100-
'db.client.operation.duration';
149+
export const METRIC_DB_CLIENT_CONNECTION_PENDING_REQUESTS =
150+
'db.client.connection.pending_requests' as const;

0 commit comments

Comments
 (0)