@instana/core
Version:
Core library for Instana's Node.js packages
144 lines (124 loc) • 4.59 kB
JavaScript
/*
* (c) Copyright IBM Corp. 2021
* (c) Copyright Instana Inc. and contributors 2019
*/
;
const shimmer = require('../../shimmer');
const hook = require('../../../util/hook');
const httpServer = require('../protocols/httpServer');
const cls = require('../../cls');
let logger;
let active = false;
exports.activate = function activate() {
active = true;
};
exports.deactivate = function deactivate() {
active = false;
};
exports.init = function init(config) {
logger = config.logger;
// Note: koa-router is deprecated from v9 with @koa/router being the recommended replacement
// We maintain instrumentation for both packages since customers may be using it
hook.onModuleLoad('koa-router', module => {
logDeprecatedWarning();
instrumentRouter(module);
});
hook.onModuleLoad('@koa/router', instrumentRouter, { nativeEsm: true });
};
function instrumentRouter(Router) {
shimmer.wrap(Router.prototype, 'routes', shimRoutes);
// Return Router for ESM compatibility: iitmHook reassigns the return value to exports.default
return Router;
}
function shimRoutes(originalFunction) {
return function () {
return instrumentedRoutes(this, originalFunction, arguments);
};
}
function instrumentedRoutes(thisContext, originalRoutes, originalArgs) {
// We need to hook into the dispatch function, which does not exist on Router.prototype but is created on the fly in
// Router.routes, so we need to hook into Router.routes to get access to the dispatch function.
const dispatch = originalRoutes.apply(thisContext, originalArgs);
// Actually, we could just use ctx._matchedRoute, which would be the path template we are looking for and which gets
// set by koa-router. Unfortunately, this is broken, see
// https://github.com/ZijianHe/koa-router/issues/478 and
// https://github.com/ZijianHe/koa-router/issues/444.
// eslint-disable-next-line no-unused-vars
const instrumentedDispatch = function (ctx, next) {
if (active && cls.isTracing()) {
const dispatchResult = dispatch.apply(this, arguments);
return dispatchResult.then(resolvedValue => {
if (ctx.matched && ctx.matched.length && ctx.matched.length > 0) {
const matchedRouteLayers = ctx.matched.slice();
matchedRouteLayers.sort(byLeastSpecificLayer);
const mostSpecificPath = normalizeLayerPath(matchedRouteLayers[matchedRouteLayers.length - 1].path);
annotateHttpEntrySpanWithPathTemplate(mostSpecificPath);
}
return resolvedValue;
});
} else {
return dispatch.apply(this, arguments);
}
};
// The router attaches itself as a property to the dispatch function and other methods in koa-router rely on this, so
// we need to attach this property to our dispatch function, too.
instrumentedDispatch.router = dispatch.router;
return instrumentedDispatch;
}
/**
* Copied from
* https://github.com/ZijianHe/koa-router/pull/475, which would fix the mentioned issues with ctx._matchedRoute (if
* it got merged).
*
* Sort function for array of Layers. Will sort the layers with least specific first
* and most specific last.
*
* @param {Layer} a
* @param {Layer} b
*/
function byLeastSpecificLayer(a, b) {
const regexpA = a.path && typeof a.path === 'object';
const regexpB = b.path && typeof b.path === 'object';
let pathA = normalizeLayerPath(a.path);
let pathB = normalizeLayerPath(b.path);
const wildA = pathA.endsWith('(.*)');
const wildB = pathB.endsWith('(.*)');
if (wildA && wildB) return pathA.length - pathB.length;
pathA = wildA ? pathA.slice(0, -4) : pathA;
pathB = wildB ? pathB.slice(0, -4) : pathB;
if (pathA !== pathB) {
if (pathA.startsWith(pathB)) return 1;
if (pathB.startsWith(pathA)) return -1;
}
if (wildA) return -1;
if (wildB) return 1;
if (regexpA && !regexpB) {
return -1;
}
if (!regexpA && regexpB) {
return 1;
}
return 0;
}
function normalizeLayerPath(p) {
if (p == null) {
return '';
}
if (typeof p === 'object') {
return p.toString();
}
return p;
}
function annotateHttpEntrySpanWithPathTemplate(pathTemplate) {
const span = cls.getCurrentEntrySpan();
if (!span || span.n !== httpServer.spanName || span.pathTplFrozen) {
return;
}
span.data.http.path_tpl = pathTemplate;
}
function logDeprecatedWarning() {
logger.warn(
// eslint-disable-next-line max-len
'[Deprecation Warning] The support for koa-router library is deprecated and will be removed in the next major release. Please consider using @koa/router instead.'
);
}