Skip to content

Commit 622638d

Browse files
feat(instrumentation-memcached): support net.* and db.* semconv migra… (#3169)
Co-authored-by: Marylia Gutierrez <[email protected]>
1 parent 87d5ab4 commit 622638d

File tree

6 files changed

+239
-42
lines changed

6 files changed

+239
-42
lines changed

packages/instrumentation-memcached/README.md

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,32 @@ registerInstrumentations({
4444

4545
### Configuration Options
4646

47-
| Option | Type | Example | Description |
48-
| ------- | ---- | ------- | ----------- |
47+
| Option | Type | Example | Description |
48+
| --------------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
4949
| `enhancedDatabaseReporting` | `boolean` | `false` | Include full command statement in the span - **leaks potentially sensitive information to your spans**. Defaults to `false`. |
5050

5151
## Semantic Conventions
5252

53-
This package uses `@opentelemetry/semantic-conventions` version `1.22+`, which implements Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md)
53+
This instrumentation implements Semantic Conventions (semconv) v1.7.0. Since then, networking (in semconv v1.23.1) and database (in semconv v1.33.0) semantic conventions were stabilized. As of `@opentelemetry/[email protected]` support has been added for migrating to the stable semantic conventions using the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable as follows:
54+
55+
1. Upgrade to the latest version of this instrumentation package.
56+
2. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http/dup,database/dup` to emit both old and stable semantic conventions. (The `http` token is used to control the `net.*` attributes, the `database` token to control to `db.*` attributes.)
57+
3. Modify alerts, dashboards, metrics, and other processes in your Observability system to use the stable semantic conventions.
58+
4. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http,database` to emit only the stable semantic conventions.
59+
60+
By default, if `OTEL_SEMCONV_STABILITY_OPT_IN` includes neither of the above tokens, the old v1.7.0 semconv is used.
61+
The intent is to provide an approximate 6 month time window for users of this instrumentation to migrate to the new database and networking semconv, after which a new minor version will use the new semconv by default and drop support for the old semconv.
62+
See [the HTTP migration guide](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/) and the [database migration guide](https://opentelemetry.io/docs/specs/semconv/non-normative/db-migration/) for details.
5463

5564
Attributes collected:
5665

57-
| Attribute | Short Description |
58-
|-----------------|-----------------------------------------------------------------------------|
59-
| `db.operation` | The name of the operation being executed. |
60-
| `db.statement` | The database statement being executed. |
61-
| `db.system` | An identifier for the database management system (DBMS) product being used. |
62-
| `net.peer.name` | Remote hostname or similar. |
63-
| `net.peer.port` | Remote port number. |
66+
| Old semconv | Stable semconv | Description |
67+
| --------------- | ------------------- | --------------------------------------------------------------------------------------- |
68+
| `db.system` | `db.system.name` | 'memcached' |
69+
| `db.operation` | `db.operation.name` | The name of the operation being executed. |
70+
| `db.statement` | `db.query.text` | The database statement being executed (only if `enhancedDatabaseReporting` is enabled). |
71+
| `net.peer.name` | `server.address` | Remote hostname or similar. |
72+
| `net.peer.port` | `server.port` | Remote port number. |
6473

6574
## Useful links
6675

packages/instrumentation-memcached/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
"@opentelemetry/contrib-test-utils": "^0.54.0",
5454
"@opentelemetry/sdk-trace-base": "^2.0.0",
5555
"@opentelemetry/sdk-trace-node": "^2.0.0",
56-
"@opentelemetry/semantic-conventions": "^1.27.0",
5756
"@types/mocha": "10.0.10",
5857
"@types/node": "18.18.14",
5958
"cross-env": "7.0.3",
@@ -63,6 +62,7 @@
6362
"typescript": "5.0.4"
6463
},
6564
"dependencies": {
65+
"@opentelemetry/semantic-conventions": "^1.33.0",
6666
"@opentelemetry/instrumentation": "^0.207.0",
6767
"@types/memcached": "^2.2.6"
6868
},

packages/instrumentation-memcached/src/instrumentation.ts

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,55 @@ import {
1919
isWrapped,
2020
InstrumentationBase,
2121
InstrumentationNodeModuleDefinition,
22+
SemconvStability,
23+
semconvStabilityFromStr,
2224
} from '@opentelemetry/instrumentation';
2325
import type * as Memcached from 'memcached';
2426
import {
27+
DB_SYSTEM_NAME_VALUE_MEMCACHED,
2528
DB_SYSTEM_VALUE_MEMCACHED,
2629
ATTR_DB_OPERATION,
2730
ATTR_DB_STATEMENT,
2831
ATTR_DB_SYSTEM,
2932
} from './semconv';
33+
import {
34+
ATTR_DB_OPERATION_NAME,
35+
ATTR_DB_QUERY_TEXT,
36+
ATTR_DB_SYSTEM_NAME,
37+
} from '@opentelemetry/semantic-conventions';
38+
3039
import * as utils from './utils';
3140
import { InstrumentationConfig } from './types';
3241
/** @knipignore */
3342
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
3443

3544
export class MemcachedInstrumentation extends InstrumentationBase<InstrumentationConfig> {
3645
static readonly COMPONENT = 'memcached';
37-
static readonly COMMON_ATTRIBUTES = {
38-
[ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_MEMCACHED,
39-
};
4046
static readonly DEFAULT_CONFIG: InstrumentationConfig = {
4147
enhancedDatabaseReporting: false,
4248
};
4349

50+
private _netSemconvStability!: SemconvStability;
51+
private _dbSemconvStability!: SemconvStability;
52+
4453
constructor(config: InstrumentationConfig = {}) {
4554
super(PACKAGE_NAME, PACKAGE_VERSION, {
4655
...MemcachedInstrumentation.DEFAULT_CONFIG,
4756
...config,
4857
});
58+
this._setSemconvStabilityFromEnv();
59+
}
60+
61+
// Used for testing.
62+
private _setSemconvStabilityFromEnv() {
63+
this._netSemconvStability = semconvStabilityFromStr(
64+
'http',
65+
process.env.OTEL_SEMCONV_STABILITY_OPT_IN
66+
);
67+
this._dbSemconvStability = semconvStabilityFromStr(
68+
'database',
69+
process.env.OTEL_SEMCONV_STABILITY_OPT_IN
70+
);
4971
}
5072

5173
override setConfig(config: InstrumentationConfig = {}) {
@@ -90,15 +112,24 @@ export class MemcachedInstrumentation extends InstrumentationBase<Instrumentatio
90112
if (typeof queryCompiler !== 'function') {
91113
return original.apply(this, arguments as any);
92114
}
115+
116+
const attributes: api.Attributes = {
117+
'memcached.version': moduleVersion,
118+
};
119+
120+
if (instrumentation._dbSemconvStability & SemconvStability.OLD) {
121+
attributes[ATTR_DB_SYSTEM] = DB_SYSTEM_VALUE_MEMCACHED;
122+
}
123+
if (instrumentation._dbSemconvStability & SemconvStability.STABLE) {
124+
attributes[ATTR_DB_SYSTEM_NAME] = DB_SYSTEM_NAME_VALUE_MEMCACHED;
125+
}
126+
93127
// The name will be overwritten later
94128
const span = instrumentation.tracer.startSpan(
95129
'unknown memcached command',
96130
{
97131
kind: api.SpanKind.CLIENT,
98-
attributes: {
99-
'memcached.version': moduleVersion,
100-
...MemcachedInstrumentation.COMMON_ATTRIBUTES,
101-
},
132+
attributes,
102133
}
103134
);
104135
const parentContext = api.context.active();
@@ -134,16 +165,35 @@ export class MemcachedInstrumentation extends InstrumentationBase<Instrumentatio
134165
const callback = query.callback;
135166

136167
span.updateName(`memcached ${query.type}`);
137-
span.setAttributes({
168+
169+
const attributes: api.Attributes = {
138170
'db.memcached.key': query.key,
139171
'db.memcached.lifetime': query.lifetime,
140-
[ATTR_DB_OPERATION]: query.type,
141-
[ATTR_DB_STATEMENT]: instrumentation.getConfig()
142-
.enhancedDatabaseReporting
143-
? query.command
144-
: undefined,
145-
...utils.getPeerAttributes(client, server, query),
146-
});
172+
...utils.getPeerAttributes(
173+
client,
174+
server,
175+
query,
176+
instrumentation._netSemconvStability
177+
),
178+
};
179+
180+
if (instrumentation._dbSemconvStability & SemconvStability.OLD) {
181+
attributes[ATTR_DB_OPERATION] = query.type;
182+
}
183+
if (instrumentation._dbSemconvStability & SemconvStability.STABLE) {
184+
attributes[ATTR_DB_OPERATION_NAME] = query.type;
185+
}
186+
187+
if (instrumentation.getConfig().enhancedDatabaseReporting) {
188+
if (instrumentation._dbSemconvStability & SemconvStability.OLD) {
189+
attributes[ATTR_DB_STATEMENT] = query.command;
190+
}
191+
if (instrumentation._dbSemconvStability & SemconvStability.STABLE) {
192+
attributes[ATTR_DB_QUERY_TEXT] = query.command;
193+
}
194+
}
195+
196+
span.setAttributes(attributes);
147197

148198
query.callback = api.context.bind(
149199
callbackContext,

packages/instrumentation-memcached/src/semconv.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,12 @@ export const ATTR_NET_PEER_PORT = 'net.peer.port' as const;
8484
* @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
8585
*/
8686
export const DB_SYSTEM_VALUE_MEMCACHED = 'memcached' as const;
87+
88+
/**
89+
* Enum value "memcached" for attribute {@link ATTR_DB_SYSTEM_NAME}.
90+
*
91+
* [Memcached](https://memcached.org/)
92+
*
93+
* @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
94+
*/
95+
export const DB_SYSTEM_NAME_VALUE_MEMCACHED = 'memcached' as const;

packages/instrumentation-memcached/src/utils.ts

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,23 @@
1515
*/
1616

1717
import type * as Memcached from 'memcached';
18-
import { ATTR_NET_PEER_NAME, ATTR_NET_PEER_PORT } from './semconv';
18+
import {
19+
ATTR_NET_PEER_NAME,
20+
ATTR_NET_PEER_PORT,
21+
} from './semconv';
22+
import {
23+
ATTR_SERVER_ADDRESS,
24+
ATTR_SERVER_PORT,
25+
} from '@opentelemetry/semantic-conventions';
26+
import { SemconvStability } from '@opentelemetry/instrumentation';
27+
import { Attributes } from '@opentelemetry/api';
1928

2029
export const getPeerAttributes = (
2130
client: any /* Memcached, but the type definitions are lacking */,
2231
server: string | undefined,
23-
query: Memcached.CommandData
24-
) => {
32+
query: Memcached.CommandData,
33+
netSemconvStability: SemconvStability
34+
): Attributes => {
2535
if (!server) {
2636
if (client.servers.length === 1) {
2737
server = client.servers[0];
@@ -47,15 +57,32 @@ export const getPeerAttributes = (
4757
const [host, port] = server && server.split(':');
4858
if (host && port) {
4959
const portNumber = parseInt(port, 10);
50-
if (!isNaN(portNumber)) {
51-
return {
52-
[ATTR_NET_PEER_NAME]: host,
53-
[ATTR_NET_PEER_PORT]: portNumber,
54-
};
60+
const attrs: Attributes = {};
61+
62+
if (netSemconvStability & SemconvStability.OLD) {
63+
attrs[ATTR_NET_PEER_NAME] = host;
64+
if (!isNaN(portNumber)) {
65+
attrs[ATTR_NET_PEER_PORT] = portNumber;
66+
}
67+
}
68+
if (netSemconvStability & SemconvStability.STABLE) {
69+
attrs[ATTR_SERVER_ADDRESS] = host;
70+
if (!isNaN(portNumber)) {
71+
attrs[ATTR_SERVER_PORT] = portNumber;
72+
}
73+
}
74+
75+
return attrs;
76+
}
77+
if (host) {
78+
const attrs: Attributes = {};
79+
if (netSemconvStability & SemconvStability.OLD) {
80+
attrs[ATTR_NET_PEER_NAME] = host;
81+
}
82+
if (netSemconvStability & SemconvStability.STABLE) {
83+
attrs[ATTR_SERVER_ADDRESS] = host;
5584
}
56-
return {
57-
[ATTR_NET_PEER_NAME]: host,
58-
};
85+
return attrs;
5986
}
6087
}
6188
return {};

0 commit comments

Comments
 (0)