UNPKG

@opentelemetry/instrumentation-socket.io

Version:

OpenTelemetry instrumentation for `socket.io` messaging server implementation for Socket.IO communication

308 lines 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SocketIoInstrumentation = void 0; /* * Copyright The OpenTelemetry Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const api_1 = require("@opentelemetry/api"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const semconv_1 = require("./semconv"); const semconv_obsolete_1 = require("./semconv-obsolete"); const AttributeNames_1 = require("./AttributeNames"); /** @knipignore */ const version_1 = require("./version"); const utils_1 = require("./utils"); const reservedEvents = [ 'connect', 'connect_error', 'disconnect', 'disconnecting', 'newListener', 'removeListener', ]; class SocketIoInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super(version_1.PACKAGE_NAME, version_1.PACKAGE_VERSION, (0, utils_1.normalizeConfig)(config)); } init() { const socketInstrumentation = new instrumentation_1.InstrumentationNodeModuleFile('socket.io/dist/socket.js', ['>=3 <5'], (moduleExports, moduleVersion) => { if (moduleExports === undefined || moduleExports === null) { return moduleExports; } if (moduleVersion === undefined) { return moduleExports; } if ((0, instrumentation_1.isWrapped)(moduleExports?.Socket?.prototype?.on)) { this._unwrap(moduleExports.Socket.prototype, 'on'); } this._wrap(moduleExports.Socket.prototype, 'on', this._patchOn(moduleVersion)); if ((0, instrumentation_1.isWrapped)(moduleExports?.Socket?.prototype?.emit)) { this._unwrap(moduleExports.Socket.prototype, 'emit'); } this._wrap(moduleExports.Socket.prototype, 'emit', this._patchEmit(moduleVersion)); return moduleExports; }, moduleExports => { if ((0, instrumentation_1.isWrapped)(moduleExports?.Socket?.prototype?.on)) { this._unwrap(moduleExports.Socket.prototype, 'on'); } if ((0, instrumentation_1.isWrapped)(moduleExports?.Socket?.prototype?.emit)) { this._unwrap(moduleExports.Socket.prototype, 'emit'); } return moduleExports; }); const broadcastOperatorInstrumentation = new instrumentation_1.InstrumentationNodeModuleFile('socket.io/dist/broadcast-operator.js', ['>=4 <5'], (moduleExports, moduleVersion) => { if (moduleExports === undefined || moduleExports === null) { return moduleExports; } if (moduleVersion === undefined) { return moduleExports; } if ((0, instrumentation_1.isWrapped)(moduleExports?.BroadcastOperator?.prototype?.emit)) { this._unwrap(moduleExports.BroadcastOperator.prototype, 'emit'); } this._wrap(moduleExports.BroadcastOperator.prototype, 'emit', this._patchEmit(moduleVersion)); return moduleExports; }, moduleExports => { if ((0, instrumentation_1.isWrapped)(moduleExports?.BroadcastOperator?.prototype?.emit)) { this._unwrap(moduleExports.BroadcastOperator.prototype, 'emit'); } return moduleExports; }); const namespaceInstrumentation = new instrumentation_1.InstrumentationNodeModuleFile('socket.io/dist/namespace.js', ['<4'], (moduleExports, moduleVersion) => { if (moduleExports === undefined || moduleExports === null) { return moduleExports; } if (moduleVersion === undefined) { return moduleExports; } if ((0, instrumentation_1.isWrapped)(moduleExports?.Namespace?.prototype?.emit)) { this._unwrap(moduleExports.Namespace.prototype, 'emit'); } this._wrap(moduleExports.Namespace.prototype, 'emit', this._patchEmit(moduleVersion)); return moduleExports; }, moduleExports => { if ((0, instrumentation_1.isWrapped)(moduleExports?.Namespace?.prototype?.emit)) { this._unwrap(moduleExports.Namespace.prototype, 'emit'); } }); const socketInstrumentationLegacy = new instrumentation_1.InstrumentationNodeModuleFile('socket.io/lib/socket.js', ['2'], (moduleExports, moduleVersion) => { if (moduleExports === undefined || moduleExports === null) { return moduleExports; } if (moduleVersion === undefined) { return moduleExports; } if ((0, instrumentation_1.isWrapped)(moduleExports.prototype?.on)) { this._unwrap(moduleExports.prototype, 'on'); } this._wrap(moduleExports.prototype, 'on', this._patchOn(moduleVersion)); if ((0, instrumentation_1.isWrapped)(moduleExports.prototype?.emit)) { this._unwrap(moduleExports.prototype, 'emit'); } this._wrap(moduleExports.prototype, 'emit', this._patchEmit(moduleVersion)); return moduleExports; }, moduleExports => { if ((0, instrumentation_1.isWrapped)(moduleExports.prototype?.on)) { this._unwrap(moduleExports.prototype, 'on'); } if ((0, instrumentation_1.isWrapped)(moduleExports.prototype?.emit)) { this._unwrap(moduleExports.prototype, 'emit'); } return moduleExports; }); const namespaceInstrumentationLegacy = new instrumentation_1.InstrumentationNodeModuleFile('socket.io/lib/namespace.js', ['2'], (moduleExports, moduleVersion) => { if (moduleExports === undefined || moduleExports === null) { return moduleExports; } if (moduleVersion === undefined) { return moduleExports; } if ((0, instrumentation_1.isWrapped)(moduleExports?.prototype?.emit)) { this._unwrap(moduleExports.prototype, 'emit'); } this._wrap(moduleExports.prototype, 'emit', this._patchEmit(moduleVersion)); return moduleExports; }, moduleExports => { if ((0, instrumentation_1.isWrapped)(moduleExports?.prototype?.emit)) { this._unwrap(moduleExports.prototype, 'emit'); } }); return [ new instrumentation_1.InstrumentationNodeModuleDefinition('socket.io', ['>=3 <5'], (moduleExports, moduleVersion) => { if (moduleExports === undefined || moduleExports === null) { return moduleExports; } if (moduleVersion === undefined) { return moduleExports; } if ((0, instrumentation_1.isWrapped)(moduleExports?.Server?.prototype?.on)) { this._unwrap(moduleExports.Server.prototype, 'on'); } this._wrap(moduleExports.Server.prototype, 'on', this._patchOn(moduleVersion)); return moduleExports; }, moduleExports => { if ((0, instrumentation_1.isWrapped)(moduleExports?.Server?.prototype?.on)) { this._unwrap(moduleExports.Server.prototype, 'on'); } return moduleExports; }, [ broadcastOperatorInstrumentation, namespaceInstrumentation, socketInstrumentation, ]), new instrumentation_1.InstrumentationNodeModuleDefinition('socket.io', ['2'], (moduleExports, moduleVersion) => { if (moduleExports === undefined || moduleExports === null) { return moduleExports; } if (moduleVersion === undefined) { return moduleExports; } if ((0, instrumentation_1.isWrapped)(moduleExports?.prototype?.on)) { this._unwrap(moduleExports.prototype, 'on'); } this._wrap(moduleExports.prototype, 'on', this._patchOn(moduleVersion)); return moduleExports; }, (moduleExports, moduleVersion) => { if ((0, instrumentation_1.isWrapped)(moduleExports?.prototype?.on)) { this._unwrap(moduleExports.prototype, 'on'); } return moduleExports; }, [namespaceInstrumentationLegacy, socketInstrumentationLegacy]), ]; } setConfig(config = {}) { return super.setConfig((0, utils_1.normalizeConfig)(config)); } _patchOn(moduleVersion) { const self = this; return (original) => { return function (ev, originalListener) { if (!self.getConfig().traceReserved && reservedEvents.includes(ev)) { return original.apply(this, arguments); } if (self.getConfig().onIgnoreEventList?.includes(ev)) { return original.apply(this, arguments); } const wrappedListener = function (...args) { const eventName = ev; const namespace = this.name || this.adapter?.nsp?.name; const span = self.tracer.startSpan(`${semconv_obsolete_1.MESSAGING_OPERATION_VALUE_RECEIVE} ${namespace}`, { kind: api_1.SpanKind.CONSUMER, attributes: { [semconv_1.ATTR_MESSAGING_SYSTEM]: 'socket.io', [semconv_obsolete_1.ATTR_MESSAGING_DESTINATION]: namespace, [semconv_1.ATTR_MESSAGING_OPERATION]: semconv_obsolete_1.MESSAGING_OPERATION_VALUE_RECEIVE, [AttributeNames_1.SocketIoInstrumentationAttributes.SOCKET_IO_EVENT_NAME]: eventName, }, }); const { onHook } = self.getConfig(); if (onHook) { (0, instrumentation_1.safeExecuteInTheMiddle)(() => onHook(span, { moduleVersion, payload: args }), e => { if (e) self._diag.error('onHook error', e); }, true); } return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => self.endSpan(() => originalListener.apply(this, arguments), span)); }; return original.apply(this, [ev, wrappedListener]); }; }; } endSpan(traced, span) { try { const result = traced(); if ((0, utils_1.isPromise)(result)) { return result.then(value => { span.end(); return value; }, err => { span.recordException(err); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err?.message, }); span.end(); throw err; }); } else { span.end(); return result; } } catch (error) { span.recordException(error); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error?.message }); span.end(); throw error; } } _patchEmit(moduleVersion) { const self = this; return (original) => { return function (ev, ...args) { if (!self.getConfig().traceReserved && reservedEvents.includes(ev)) { return original.apply(this, arguments); } if (self.getConfig().emitIgnoreEventList?.includes(ev)) { return original.apply(this, arguments); } const messagingSystem = 'socket.io'; const eventName = ev; const attributes = { [semconv_1.ATTR_MESSAGING_SYSTEM]: messagingSystem, [semconv_obsolete_1.ATTR_MESSAGING_DESTINATION_KIND]: semconv_obsolete_1.MESSAGING_DESTINATION_KIND_VALUE_TOPIC, [AttributeNames_1.SocketIoInstrumentationAttributes.SOCKET_IO_EVENT_NAME]: eventName, }; const rooms = (0, utils_1.extractRoomsAttributeValue)(this); if (rooms.length) { attributes[AttributeNames_1.SocketIoInstrumentationAttributes.SOCKET_IO_ROOMS] = rooms; } const namespace = this.name || this.adapter?.nsp?.name || this.sockets?.name; if (namespace) { attributes[AttributeNames_1.SocketIoInstrumentationAttributes.SOCKET_IO_NAMESPACE] = namespace; attributes[semconv_obsolete_1.ATTR_MESSAGING_DESTINATION] = namespace; } const span = self.tracer.startSpan(`send ${namespace}`, { kind: api_1.SpanKind.PRODUCER, attributes, }); const { emitHook } = self.getConfig(); if (emitHook) { (0, instrumentation_1.safeExecuteInTheMiddle)(() => emitHook(span, { moduleVersion, payload: args }), e => { if (e) self._diag.error('emitHook error', e); }, true); } try { return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => original.apply(this, arguments)); } catch (error) { span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message, }); throw error; } finally { span.end(); } }; }; } } exports.SocketIoInstrumentation = SocketIoInstrumentation; //# sourceMappingURL=socket.io.js.map