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" ;
57import {
68 InstrumentationBase ,
79 InstrumentationNodeModuleDefinition ,
@@ -12,11 +14,12 @@ import { getIncomingRequestAttributes } from "@opentelemetry/instrumentation-htt
1214import { SemanticAttributes } from "@opentelemetry/semantic-conventions" ;
1315import type * as http from "http" ;
1416import type * as https from "http" ;
15- import { IncomingMessage } from "http" ;
17+ import type { IncomingMessage } from "http" ;
1618import 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
2124const 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