Skip to content

Commit 7beb009

Browse files
authored
Merge pull request #30 from gadget-inc/sc/ws-root-spans
2 parents 29b0fde + 5efa38a commit 7beb009

File tree

1 file changed

+110
-6
lines changed
  • packages/opentelemetry-instrumentation-ws/src

1 file changed

+110
-6
lines changed

packages/opentelemetry-instrumentation-ws/src/index.ts

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
/* eslint-disable @typescript-eslint/no-this-alias */
22
/* eslint-disable @typescript-eslint/ban-types */
3-
import { context, Context, diag, propagation, ROOT_CONTEXT, Span, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
4-
import { RPCMetadata, RPCType, setRPCMetadata } from "@opentelemetry/core";
3+
import type { Context, Link, Span } from "@opentelemetry/api";
4+
import { context, diag, propagation, ROOT_CONTEXT, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
5+
import type { RPCMetadata } from "@opentelemetry/core";
6+
import { RPCType, setRPCMetadata } from "@opentelemetry/core";
57
import {
68
InstrumentationBase,
79
InstrumentationNodeModuleDefinition,
@@ -12,11 +14,12 @@ import { getIncomingRequestAttributes } from "@opentelemetry/instrumentation-htt
1214
import { SemanticAttributes } from "@opentelemetry/semantic-conventions";
1315
import type * as http from "http";
1416
import type * as https from "http";
15-
import { IncomingMessage } from "http";
17+
import type { IncomingMessage } from "http";
1618
import isPromise from "is-promise";
17-
import { Duplex } from "stream";
18-
import WS, { ErrorEvent, Server, WebSocket } from "ws";
19-
import { WSInstrumentationConfig } from "./types";
19+
import type { Duplex } from "stream";
20+
import type WS from "ws";
21+
import type { ErrorEvent, Server, WebSocket } from "ws";
22+
import type { WSInstrumentationConfig } from "./types";
2023

2124
const endSpan = (traced: () => any | Promise<any>, span: Span) => {
2225
try {
@@ -160,8 +163,11 @@ export class WSInstrumentation extends InstrumentationBase<WS> {
160163
}
161164

162165
if (address != null) {
166+
const [root, links] = getRootLinks(WSInstrumentationContext.CONNECT_ROOT);
163167
connectingSpan = self.tracer.startSpan(`WS connect`, {
164168
kind: SpanKind.CLIENT,
169+
root,
170+
links,
165171
attributes: {
166172
[SemanticAttributes.MESSAGING_SYSTEM]: "ws",
167173
[SemanticAttributes.MESSAGING_DESTINATION_KIND]: "websocket",
@@ -201,8 +207,11 @@ export class WSInstrumentation extends InstrumentationBase<WS> {
201207
}
202208

203209
this.once("open", () => {
210+
const [root, links] = getRootLinks(WSInstrumentationContext.OPEN_ROOT);
204211
this._openSpan = self.tracer.startSpan(`WS open`, {
205212
kind: connectingSpan ? SpanKind.CLIENT : SpanKind.SERVER,
213+
root,
214+
links,
206215
attributes: {
207216
[SemanticAttributes.MESSAGING_SYSTEM]: "ws",
208217
[SemanticAttributes.MESSAGING_DESTINATION_KIND]: "websocket",
@@ -250,8 +259,11 @@ export class WSInstrumentation extends InstrumentationBase<WS> {
250259
options = {};
251260
}
252261

262+
const [root, links] = getRootLinks(WSInstrumentationContext.SEND_ROOT);
253263
const span = self.tracer.startSpan(`WS send`, {
254264
kind: SpanKind.CLIENT,
265+
root,
266+
links,
255267
attributes: {
256268
[SemanticAttributes.MESSAGING_DESTINATION]: this.url,
257269
},
@@ -296,8 +308,11 @@ export class WSInstrumentation extends InstrumentationBase<WS> {
296308
const self = this;
297309

298310
return function (this: ExtendedWebsocket, ...args: any[]) {
311+
const [root, links] = getRootLinks(WSInstrumentationContext.CLOSE_ROOT);
299312
const span = self.tracer.startSpan(`WS close`, {
300313
kind: SpanKind.CLIENT,
314+
root,
315+
links,
301316
attributes: {
302317
[SemanticAttributes.MESSAGING_DESTINATION]: this.url,
303318
},
@@ -387,8 +402,11 @@ export class WSInstrumentation extends InstrumentationBase<WS> {
387402
callback: (client: WebSocket, request: IncomingMessage) => void
388403
) {
389404
const parentSpan = self._requestSpans.get(request);
405+
const [root, links] = getRootLinks(WSInstrumentationContext.UPGRADE_ROOT, parentSpan);
390406
const span = self.tracer.startSpan(`WS upgrade`, {
391407
kind: SpanKind.SERVER,
408+
root,
409+
links,
392410
attributes: getIncomingRequestAttributes(request, {
393411
component: "WS",
394412
hookAttributes: {
@@ -430,3 +448,89 @@ export class WSInstrumentation extends InstrumentationBase<WS> {
430448
};
431449
};
432450
}
451+
452+
/**
453+
* Context keys for the WSInstrumentation.
454+
*/
455+
export const WSInstrumentationContext = Object.freeze({
456+
/**
457+
* Whether the "WS connect" span is a root span.
458+
*/
459+
CONNECT_ROOT: Symbol.for("opentelemetry-instrumentation-ws/connect-root"),
460+
461+
/**
462+
* Whether the "WS open" span is a root span.
463+
*/
464+
OPEN_ROOT: Symbol.for("opentelemetry-instrumentation-ws/open-root"),
465+
466+
/**
467+
* Whether the "WS send" span is a root span.
468+
*/
469+
SEND_ROOT: Symbol.for("opentelemetry-instrumentation-ws/send-root"),
470+
471+
/**
472+
* Whether the "WS close" span is a root span.
473+
*/
474+
CLOSE_ROOT: Symbol.for("opentelemetry-instrumentation-ws/close-root"),
475+
476+
/**
477+
* Whether the "WS upgrade" span is a root span.
478+
*/
479+
UPGRADE_ROOT: Symbol.for("opentelemetry-instrumentation-ws/upgrade-root"),
480+
});
481+
482+
export type WSInstrumentationContext = (typeof WSInstrumentationContext)[keyof typeof WSInstrumentationContext];
483+
484+
function getRootLinks(contextKey: WSInstrumentationContext, parentSpan = trace.getSpan(context.active())): [boolean | undefined, Link[]] {
485+
const links: Link[] = [];
486+
const root = context.active().getValue(contextKey) as boolean | undefined;
487+
if (root && parentSpan) {
488+
links.push({ context: parentSpan.spanContext() });
489+
}
490+
return [root, links];
491+
}
492+
493+
/**
494+
* Makes all "WS connect" spans created in the callback be root spans.
495+
* @param callback - The callback to execute.
496+
* @returns The result of the callback.
497+
*/
498+
export function withWSConnectRoot<T>(callback: () => T): T {
499+
return context.with(context.active().setValue(WSInstrumentationContext.CONNECT_ROOT, true), callback);
500+
}
501+
502+
/**
503+
* Makes all "WS open" spans created in the callback be root spans.
504+
* @param callback - The callback to execute.
505+
* @returns The result of the callback.
506+
*/
507+
export function withWSOpenRoot<T>(callback: () => T): T {
508+
return context.with(context.active().setValue(WSInstrumentationContext.OPEN_ROOT, true), callback);
509+
}
510+
511+
/**
512+
* Makes all "WS send" spans created in the callback be root spans.
513+
* @param callback - The callback to execute.
514+
* @returns The result of the callback.
515+
*/
516+
export function withWSSendRoot<T>(callback: () => T): T {
517+
return context.with(context.active().setValue(WSInstrumentationContext.SEND_ROOT, true), callback);
518+
}
519+
520+
/**
521+
* Makes all "WS close" spans created in the callback be root spans.
522+
* @param callback - The callback to execute.
523+
* @returns The result of the callback.
524+
*/
525+
export function withWSCloseRoot<T>(callback: () => T): T {
526+
return context.with(context.active().setValue(WSInstrumentationContext.CLOSE_ROOT, true), callback);
527+
}
528+
529+
/**
530+
* Makes all "WS upgrade" spans created in the callback be root spans.
531+
* @param callback - The callback to execute.
532+
* @returns The result of the callback.
533+
*/
534+
export function withWSUpgradeRoot<T>(callback: () => T): T {
535+
return context.with(context.active().setValue(WSInstrumentationContext.UPGRADE_ROOT, true), callback);
536+
}

0 commit comments

Comments
 (0)