UNPKG

skywalking-backend-js-netease

Version:

The NodeJS agent for Apache SkyWalking

244 lines 11.6 kB
"use strict"; /*! * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 * * http://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. * */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const SwPlugin_1 = require("../core/SwPlugin"); const ContextManager_1 = tslib_1.__importDefault(require("../trace/context/ContextManager")); const Component_1 = require("../trace/Component"); const Tag_1 = tslib_1.__importDefault(require("../Tag")); const Tracing_pb_1 = require("../proto/language-agent/Tracing_pb"); const ContextCarrier_1 = require("../trace/context/ContextCarrier"); const SegmentRef_1 = tslib_1.__importDefault(require("../trace/context/SegmentRef")); const DummySpan_1 = tslib_1.__importDefault(require("../trace/span/DummySpan")); const AgentConfig_1 = require("../config/AgentConfig"); const AgentConfig_2 = tslib_1.__importDefault(require("../config/AgentConfig")); const CustomMonitor_1 = tslib_1.__importStar(require("./CustomMonitor")); const fs = tslib_1.__importStar(require("fs")); const path = tslib_1.__importStar(require("path")); const gTraceIdKey = 'ext.trace.id'; const operantionSuffix = 'operation.name.suffix'; const persistConnKey = 'conn.persist.count'; class WsPlugin { constructor() { this.module = 'ws'; // nodejs websocket this.versions = '*'; } getVersion(installer) { let indexPath = installer.resolve(this.module); let packageSJonStr = fs.readFileSync(`${path.dirname(indexPath)}${path.sep}package.json`, { encoding: 'utf-8' }); const pkg = JSON.parse(packageSJonStr); return pkg.version; } install(installer) { const ws = installer.require('ws'); // Server.handleUpgrade(req: http.IncomingMessage, socket: (net.Socket|tls.Socket), head: Buffer, cb: Function) this.interceptOperation(ws.Server, 'handleUpgrade'); this.interceptLocal(ws.Server, 'emit', ['connection'], 1); this.interceptLocal(ws, 'emit'); this.interceptExit(ws, 'send'); } interceptOperation(Cls, operation) { const _original = Cls.prototype[operation]; if (!_original) { return; } Cls.prototype[operation] = function (req, socket, head, cb) { var _a, _b; const carrier = ContextCarrier_1.ContextCarrier.from(req.headers || {}); const operationName = (socket.encrypted ? 'wss:' : 'ws:') + (req.url || '/').replace(/\?.*/g, ''); const method = req.method || 'GET'; const span = AgentConfig_1.ignoreHttpMethodCheck(method) ? DummySpan_1.default.create() : ContextManager_1.default.current.newEntrySpan(operationName, carrier, Component_1.Component.HTTP_SERVER); const innerCarrier = new ContextCarrier_1.ContextCarrier(span.context.segment.relatedTraces[0], span.context.segment.segmentId, span.id, AgentConfig_2.default.serviceName, AgentConfig_2.default.serviceInstance, (socket.encrypted ? 'wss:' : 'ws:') + (req.url || '/'), span.peer, []); span.start(); span.component = Component_1.Component.HTTP_SERVER; span.tag(Tag_1.default.httpMethod(method)); span.tag(Tag_1.default.httpURL((socket.encrypted ? 'wss:' : 'ws:') + (req.url || '/'))); span.layer = Tracing_pb_1.SpanLayer.HTTP; span.peer = (typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) || (socket.remoteFamily === 'IPv6' ? `[${socket.remoteAddress}]:${socket.remotePort}` : `${socket.remoteAddress}:${socket.remotePort}`); span.tag({ key: gTraceIdKey, overridable: true, val: span.context.segment.relatedTraces[0].toString() }); // collect const customId = CustomMonitor_1.default(req, span); const hasCB = typeof arguments[arguments.length - 1] === 'function'; const self = this; try { if (hasCB) { const wrappedCallback = SwPlugin_1.wrapCallback(span, cb, false); // in case of immediate synchronous callback arguments[arguments.length - 1] = function () { arguments[0]._SKYWALKING_WS_OBJECT = { operationName, method, url: (socket.encrypted ? 'wss:' : 'ws:') + (req.url || '/'), peer: span.peer, innerCarrier, startTime: span.startTime, wss: self, customId, }; wrappedCallback.apply(this, arguments); }; } let ret = _original.apply(this, arguments); span.tag({ key: persistConnKey, overridable: true, val: `${((_a = this.clients) === null || _a === void 0 ? void 0 : _a.size) || 0}`, }); if (!hasCB) { if (ret && typeof ret.then === 'function') { // generic Promise check ret = SwPlugin_1.wrapPromise(span, ret); } else { // no callback passed in and no Promise or Cursor returned, play it safe span.stop(); return ret; } } span.async(); return ret; } catch (err) { span.error(err); span.tag({ key: persistConnKey, overridable: true, val: `${((_b = this.clients) === null || _b === void 0 ? void 0 : _b.size) || 0}`, }); span.stop(); throw err; } }; } interceptLocal(Cls, operation, events, objIdx) { const _original = Cls.prototype[operation]; if (!_original) { return; } Cls.prototype[operation] = function (...args) { var _a; const event = args[0]; if (events && !events.includes(event)) { return _original.apply(this, arguments); } const self = objIdx ? args[objIdx] : this; if (!self._SKYWALKING_WS_OBJECT) { return _original.apply(this, arguments); } const { operationName, method, url, peer, innerCarrier, startTime, wss, customId } = self._SKYWALKING_WS_OBJECT; const span = AgentConfig_1.ignoreHttpMethodCheck(method) ? DummySpan_1.default.create() : ContextManager_1.default.current.newEntrySpan(operationName); const ref = SegmentRef_1.default.fromCarrier(innerCarrier); ref.refType = 'CrossThread'; span.context.segment.relate(innerCarrier.traceId); span.refer(ref); span.start(); span.component = Component_1.Component.HTTP_SERVER; span.tag(Tag_1.default.httpMethod(method)); span.tag(Tag_1.default.httpURL(url)); span.layer = Tracing_pb_1.SpanLayer.HTTP; span.peer = peer; span.tag({ key: gTraceIdKey, overridable: true, val: ref.traceId.toString() }); span.tag({ key: operantionSuffix, overridable: true, val: ' on ' + event }); span.tag({ key: 'root.start.time', overridable: true, val: `${startTime}` }); CustomMonitor_1.tagCustomIds(customId, span); try { if (event === 'error') { span.error(arguments[1]); } return _original.apply(this, arguments); } catch (err) { span.error(err); throw err; } finally { span.tag({ key: persistConnKey, overridable: true, val: `${((_a = wss === null || wss === void 0 ? void 0 : wss.clients) === null || _a === void 0 ? void 0 : _a.size) || 0}` }); span.stop(); } }; } interceptExit(Cls, operation) { const _original = Cls.prototype[operation]; if (!_original) { return; } Cls.prototype[operation] = function (...args) { const self = this; if (!self._SKYWALKING_WS_OBJECT) { return _original.apply(this, arguments); } const { operationName, method, url, peer, innerCarrier, startTime, customId } = self._SKYWALKING_WS_OBJECT; const span = AgentConfig_1.ignoreHttpMethodCheck(method) ? DummySpan_1.default.create() : ContextManager_1.default.current.newEntrySpan(operationName); const ref = SegmentRef_1.default.fromCarrier(innerCarrier); ref.refType = 'CrossThread'; span.context.segment.relate(innerCarrier.traceId); span.refer(ref); span.start(); span.component = Component_1.Component.HTTP; span.tag(Tag_1.default.httpMethod(method)); span.tag(Tag_1.default.httpURL(url)); span.layer = Tracing_pb_1.SpanLayer.HTTP; span.peer = peer; span.tag({ key: operantionSuffix, overridable: true, val: ' on ' + operation }); span.tag({ key: 'root.start.time', overridable: true, val: `${startTime}` }); CustomMonitor_1.tagCustomIds(customId, span); const hasCB = typeof arguments[arguments.length - 1] === 'function'; try { if (hasCB) { const wrappedCallback = SwPlugin_1.wrapCallback(span, arguments[arguments.length - 1], 0); arguments[arguments.length - 1] = function () { // in case of immediate synchronous callback wrappedCallback.apply(this, arguments); }; } let ret = _original.apply(this, arguments); if (!hasCB) { if (ret && typeof ret.then === 'function') { // generic Promise check ret = SwPlugin_1.wrapPromise(span, ret); } else { span.stop(); return ret; } } span.async(); return ret; } catch (err) { span.error(err); span.stop(); throw err; } }; } } exports.default = new WsPlugin(); //# sourceMappingURL=WsPlugin.js.map