Skip to content

Commit d32c6d9

Browse files
committed
feat(instrumentation-net): support net.* semconv migration
This adds support for using `OTEL_SEMCONV_STABILITY_OPT_IN` for controlled migration to stable `net.*` semconv. The `net.*` attributes are controlled by the `http[/dup]` token in `OTEL_SEMCONV_STABILITY_OPT_IN` (as [discussed here](open-telemetry/opentelemetry-js#5663 (comment))). Refs: open-telemetry/opentelemetry-js#5663
1 parent b2e5483 commit d32c6d9

File tree

8 files changed

+230
-83
lines changed

8 files changed

+230
-83
lines changed

package-lock.json

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

packages/instrumentation-net/README.md

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,28 @@ registerInstrumentations({
4141

4242
## Semantic Conventions
4343

44-
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)
45-
46-
Attributes added to `connect` spans:
47-
48-
| Attribute | Short Description |
49-
| ------------------------- | ------------------------------------------------------------------------ |
50-
| `net.transport` | `IP.TCP`, `pipe` or `Unix` |
51-
| `net.peer.name` | Host name or the IPC file path |
52-
| `net.peer.ip` (for TCP) | Remote address of the peer (dotted decimal for IPv4 or RFC5952 for IPv6) |
53-
| `net.peer.port` (for TCP) | Remote port number |
54-
| `net.host.ip` (for TCP) | Like net.peer.ip but for the host IP. Useful in case of a multi-IP host |
55-
| `net.host.port` (for TCP) | Like net.peer.port but for the host port |
44+
This instrumentation implements Semantic Conventions (semconv) v1.7.0. Since then, many networking-related semantic conventions (in semconv v1.21.0 and v1.23.1) 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:
45+
46+
1. Upgrade to the latest version of this instrumentation package.
47+
2. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http/dup` to emit both old and stable semantic conventions. (The [`http` token is used to control the `net.*` attributes](https://github.com/open-telemetry/opentelemetry-js/issues/5663#issuecomment-3349204546).)
48+
3. Modify alerts, dashboards, metrics, and other processes in your Observability system to use the stable semantic conventions.
49+
4. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http` to emit only the stable semantic conventions.
50+
51+
By default, if `OTEL_SEMCONV_STABILITY_OPT_IN` is not set or does not include `http`, then the old v1.7.0 semconv is used.
52+
The intent is to provide an approximate 6 month time window for users of this instrumentation to migrate to the new networking semconv, after which a new minor version will use the new semconv by default and drop support for the old semconv.
53+
See [the HTTP migration guide](https://opentelemetry.io/docs/specs/semconv/non-normative/http-migration/) and [deprecated network attributes](https://opentelemetry.io/docs/specs/semconv/registry/attributes/network/#deprecated-network-attributes) for details.
54+
55+
Attributes collected:
56+
57+
| Old semconv | Stable semconv | Description |
58+
| --------------- | ----------------------- | --------------------------------------------------------------------------------- |
59+
| `net.transport` | `network.transport` | One of `pipe`, `unix`, `ip_tcp` (old) or `tcp` (stable) |
60+
| `net.peer.name` | `server.address` | Host name or the IPC file path |
61+
| `net.peer.port` | `server.port` | Remote port number |
62+
| `net.peer.ip` | `network.peer.address` | Peer address of the network connection - IP address or Unix domain socket name. |
63+
| `net.host.ip` | `network.local.address` | Local address of the network connection - IP address or Unix domain socket name. |
64+
| `net.host.port` | `network.local.port` | Local port number of the network connection. |
65+
5666

5767
## Useful links
5868

packages/instrumentation-net/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
"@opentelemetry/sdk-trace-node": "^2.0.0"
5252
},
5353
"dependencies": {
54-
"@opentelemetry/instrumentation": "^0.208.0"
54+
"@opentelemetry/instrumentation": "^0.208.0",
55+
"@opentelemetry/semantic-conventions": "^1.33.0"
5556
},
5657
"homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-net#readme"
5758
}

packages/instrumentation-net/src/instrumentation.ts

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,25 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Span, SpanStatusCode, context, trace } from '@opentelemetry/api';
17+
import { Span, SpanStatusCode, context, trace, type Attributes } from '@opentelemetry/api';
1818
import {
1919
InstrumentationBase,
2020
InstrumentationConfig,
2121
InstrumentationNodeModuleDefinition,
2222
isWrapped,
2323
safeExecuteInTheMiddle,
24+
SemconvStability,
25+
semconvStabilityFromStr,
2426
} from '@opentelemetry/instrumentation';
27+
import {
28+
ATTR_NETWORK_LOCAL_ADDRESS,
29+
ATTR_NETWORK_LOCAL_PORT,
30+
ATTR_NETWORK_PEER_ADDRESS,
31+
ATTR_NETWORK_TRANSPORT,
32+
ATTR_SERVER_ADDRESS,
33+
ATTR_SERVER_PORT,
34+
NETWORK_TRANSPORT_VALUE_TCP,
35+
} from '@opentelemetry/semantic-conventions';
2536
import {
2637
ATTR_NET_HOST_IP,
2738
ATTR_NET_HOST_PORT,
@@ -33,16 +44,27 @@ import {
3344
} from './semconv';
3445
import { TLSAttributes } from './types';
3546
import { NormalizedOptions, SocketEvent } from './internal-types';
36-
import { getNormalizedArgs, IPC_TRANSPORT } from './utils';
47+
import { getNormalizedArgs, OLD_IPC_TRANSPORT_VALUE, STABLE_IPC_TRANSPORT_VALUE } from './utils';
3748
/** @knipignore */
3849
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
3950
import { Socket } from 'net';
4051
import { TLSSocket } from 'tls';
4152
import type * as net from 'net';
4253

4354
export class NetInstrumentation extends InstrumentationBase {
55+
private _netSemconvStability!: SemconvStability;
56+
4457
constructor(config: InstrumentationConfig = {}) {
4558
super(PACKAGE_NAME, PACKAGE_VERSION, config);
59+
this._setSemconvStabilityFromEnv();
60+
}
61+
62+
// Used for testing.
63+
private _setSemconvStabilityFromEnv() {
64+
this._netSemconvStability = semconvStabilityFromStr(
65+
'http',
66+
process.env.OTEL_SEMCONV_STABILITY_OPT_IN
67+
);
4668
}
4769

4870
init(): InstrumentationNodeModuleDefinition[] {
@@ -182,34 +204,43 @@ export class NetInstrumentation extends InstrumentationBase {
182204
private _startGenericSpan(socket: Socket) {
183205
const span = this.tracer.startSpan('connect');
184206

185-
registerListeners(socket, span);
207+
registerListeners(socket, span, false, this._netSemconvStability);
186208

187209
return span;
188210
}
189211

190212
private _startIpcSpan(options: NormalizedOptions, socket: Socket) {
191-
const span = this.tracer.startSpan('ipc.connect', {
192-
attributes: {
193-
[ATTR_NET_TRANSPORT]: IPC_TRANSPORT,
194-
[ATTR_NET_PEER_NAME]: options.path,
195-
},
196-
});
213+
const attributes: Attributes = {};
214+
if (this._netSemconvStability & SemconvStability.OLD) {
215+
attributes[ATTR_NET_TRANSPORT] = OLD_IPC_TRANSPORT_VALUE;
216+
attributes[ATTR_NET_PEER_NAME] = options.path;
217+
}
218+
if (this._netSemconvStability & SemconvStability.STABLE) {
219+
attributes[ATTR_NETWORK_TRANSPORT] = STABLE_IPC_TRANSPORT_VALUE;
220+
attributes[ATTR_SERVER_ADDRESS] = options.path;
221+
}
222+
const span = this.tracer.startSpan('ipc.connect', { attributes });
197223

198-
registerListeners(socket, span);
224+
registerListeners(socket, span, false, this._netSemconvStability);
199225

200226
return span;
201227
}
202228

203229
private _startTcpSpan(options: NormalizedOptions, socket: Socket) {
204-
const span = this.tracer.startSpan('tcp.connect', {
205-
attributes: {
206-
[ATTR_NET_TRANSPORT]: NET_TRANSPORT_VALUE_IP_TCP,
207-
[ATTR_NET_PEER_NAME]: options.host,
208-
[ATTR_NET_PEER_PORT]: options.port,
209-
},
210-
});
230+
const attributes: Attributes = {};
231+
if (this._netSemconvStability & SemconvStability.OLD) {
232+
attributes[ATTR_NET_TRANSPORT] = NET_TRANSPORT_VALUE_IP_TCP;
233+
attributes[ATTR_NET_PEER_NAME] = options.host;
234+
attributes[ATTR_NET_PEER_PORT] = options.port;
235+
}
236+
if (this._netSemconvStability & SemconvStability.STABLE) {
237+
attributes[ATTR_NETWORK_TRANSPORT] = NETWORK_TRANSPORT_VALUE_TCP;
238+
attributes[ATTR_SERVER_ADDRESS] = options.host;
239+
attributes[ATTR_SERVER_PORT] = options.port;
240+
}
241+
const span = this.tracer.startSpan('tcp.connect', { attributes });
211242

212-
registerListeners(socket, span, { hostAttributes: true });
243+
registerListeners(socket, span, true, this._netSemconvStability);
213244

214245
return span;
215246
}
@@ -239,17 +270,25 @@ function spanErrorHandler(span: Span) {
239270
function registerListeners(
240271
socket: Socket,
241272
span: Span,
242-
{ hostAttributes = false }: { hostAttributes?: boolean } = {}
273+
hostAttributes: boolean,
274+
netSemconvStability: SemconvStability,
243275
) {
244276
const setSpanError = spanErrorHandler(span);
245277
const setSpanEnd = spanEndHandler(span);
246278

247279
const setHostAttributes = () => {
248-
span.setAttributes({
249-
[ATTR_NET_PEER_IP]: socket.remoteAddress,
250-
[ATTR_NET_HOST_IP]: socket.localAddress,
251-
[ATTR_NET_HOST_PORT]: socket.localPort,
252-
});
280+
const attributes: Attributes = {};
281+
if (netSemconvStability & SemconvStability.OLD) {
282+
attributes[ATTR_NET_PEER_IP] = socket.remoteAddress;
283+
attributes[ATTR_NET_HOST_IP] = socket.localAddress;
284+
attributes[ATTR_NET_HOST_PORT] = socket.localPort;
285+
}
286+
if (netSemconvStability & SemconvStability.STABLE) {
287+
attributes[ATTR_NETWORK_PEER_ADDRESS] = socket.remoteAddress;
288+
attributes[ATTR_NETWORK_LOCAL_ADDRESS] = socket.localAddress;
289+
attributes[ATTR_NETWORK_LOCAL_PORT] = socket.localPort;
290+
}
291+
span.setAttributes(attributes);
253292
};
254293

255294
socket.once(SocketEvent.ERROR, setSpanError);

packages/instrumentation-net/src/utils.ts

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

17+
import { platform } from 'os';
18+
import {
19+
NETWORK_TRANSPORT_VALUE_PIPE,
20+
NETWORK_TRANSPORT_VALUE_UNIX,
21+
} from '@opentelemetry/semantic-conventions';
1722
import { NormalizedOptions } from './internal-types';
1823
import { NET_TRANSPORT_VALUE_PIPE } from './semconv';
19-
import { platform } from 'os';
2024

21-
// Currently the `IPC_TRANSPORT` values are for 'net.transport'. In semconv
22-
// v1.21.0 a breaking change (https://github.com/open-telemetry/opentelemetry-specification/pull/3426)
23-
// replaced 'net.transport' with 'network.transport'. The deprecated
24-
// 'net.transport' *removed* the 'unix' value (not sure if intentional). As a
25-
// result, the JS `@opentelemetry/semantic-conventions` package does not export
26-
// a `NET_TRANSPORT_VALUE_UNIX`.
27-
//
28-
// (TODO: update instrumentation-net (per PR-3426) to use 'network.transport',
29-
// then the `NETWORK_TRANSPORT_VALUE_UNIX` constant can be used.)
30-
export const IPC_TRANSPORT =
25+
// There is no `NET_TRANSPORT_VALUE_UNIX` because breaking change
26+
// https://github.com/open-telemetry/opentelemetry-specification/pull/3426
27+
// *removed* it. This was from before semconv got more careful of removals.
28+
export const OLD_IPC_TRANSPORT_VALUE =
3129
platform() === 'win32' ? NET_TRANSPORT_VALUE_PIPE : 'unix';
30+
export const STABLE_IPC_TRANSPORT_VALUE =
31+
platform() === 'win32' ? NETWORK_TRANSPORT_VALUE_PIPE : NETWORK_TRANSPORT_VALUE_UNIX;
3232

3333
function getHost(args: unknown[]) {
3434
return typeof args[1] === 'string' ? args[1] : 'localhost';

0 commit comments

Comments
 (0)