@instana/core
Advanced tools
Comparing version 2.21.0 to 2.21.1
@@ -6,2 +6,16 @@ # Change Log | ||
## [2.21.1](https://github.com/instana/nodejs/compare/v2.21.0...v2.21.1) (2023-05-02) | ||
### Bug Fixes | ||
* **db2:** capture the correct destination dsn per client ([9529690](https://github.com/instana/nodejs/commit/9529690070871fddd2d31b0b646badc320dde56b)) | ||
* **elasticsearch:** capture the correct destination host per client ([cc23d05](https://github.com/instana/nodejs/commit/cc23d057a9d60a3a179e20451e0bc336e3c9a56d)) | ||
* **nats:** capture the correct destination nats address per client ([59e5ddf](https://github.com/instana/nodejs/commit/59e5ddfbbe85a724bfc040e140e63bf906706f2f)) | ||
* **nats-streaming:** capture correct destination address per client ([678d702](https://github.com/instana/nodejs/commit/678d70276dcb761eeb64dc3c848157267458192c)) | ||
# [2.21.0](https://github.com/instana/nodejs/compare/v2.20.2...v2.21.0) (2023-04-21) | ||
@@ -8,0 +22,0 @@ |
{ | ||
"name": "@instana/core", | ||
"version": "2.21.0", | ||
"version": "2.21.1", | ||
"description": "Core library for Instana's Node.js packages", | ||
@@ -137,3 +137,3 @@ "main": "src/index.js", | ||
}, | ||
"gitHead": "2c570cf285754fa54ed88f7e8c1cab7df35e3d43" | ||
"gitHead": "8a2dd3f98de2b29b9c8faf145cec0dd471c98c2d" | ||
} |
@@ -15,3 +15,2 @@ /* | ||
let isActive = false; | ||
let connectionStr; | ||
@@ -66,3 +65,3 @@ const CLOSE_TIMEOUT_IN_MS = process.env.DB2_CLOSE_TIMEOUT_IN_MS || 1000 * 30; | ||
// There is no other format to pass in the connection. | ||
connectionStr = arguments[0]; | ||
this._instanaConnectionString = arguments[0]; | ||
return originalFunction.apply(this, arguments); | ||
@@ -252,3 +251,3 @@ }; | ||
return cls.ns.runAndReturn(() => { | ||
const span = createSpan(stmt, instrumentQueryHelper); | ||
const span = createSpan(stmt, instrumentQueryHelper, ctx._instanaConnectionString); | ||
@@ -362,3 +361,3 @@ // CASE: querySync | ||
const span = createSpan(originalArgs[0], instrumentExecuteHelper); | ||
const span = createSpan(originalArgs[0], instrumentExecuteHelper, ctx._instanaConnectionString); | ||
@@ -387,3 +386,3 @@ // NOTE: returns row count | ||
// NOTE: start one span per execute! | ||
const span = createSpan(originalArgs[0], instrumentExecuteHelper); | ||
const span = createSpan(originalArgs[0], instrumentExecuteHelper, ctx._instanaConnectionString); | ||
@@ -405,3 +404,3 @@ const result = originalExecuteSync.apply(this, arguments); | ||
// NOTE: start one span per execute! | ||
const span = createSpan(originalArgs[0], instrumentExecuteHelper); | ||
const span = createSpan(originalArgs[0], instrumentExecuteHelper, ctx._instanaConnectionString); | ||
@@ -441,3 +440,3 @@ const args = arguments; | ||
return cls.ns.runAndReturn(() => { | ||
const span = createSpan(stmt, instrumentQueryResultHelper); | ||
const span = createSpan(stmt, instrumentQueryResultHelper, ctx._instanaConnectionString); | ||
@@ -483,3 +482,3 @@ if (!isAsync) { | ||
function createSpan(stmt, fn) { | ||
function createSpan(stmt, fn, connectionStr) { | ||
// eslint-disable-next-line max-len | ||
@@ -486,0 +485,0 @@ // https://github.ibm.com/instana/backend/blob/develop/forge/src/main/java/com/instana/forge/connection/database/ibmdb2/IbmDb2Span.java |
@@ -33,3 +33,3 @@ /* | ||
let parsedConnectionUrl; | ||
const connectionUrlCache = {}; | ||
@@ -45,7 +45,5 @@ function instrument(es, esModuleFilename) { | ||
const connectionString = client.connectionPool.connections[0].url; | ||
try { | ||
parsedConnectionUrl = new URL(connectionString); | ||
} catch (parseErrr) { | ||
/* ignore */ | ||
} | ||
// We will almost certainly need the host and port from the connection URL later in shimGetConnection when | ||
// capturing an actual request. We can as well parse it now and put it in the cache. | ||
parseAndCacheConnectionUrl(connectionString); | ||
} | ||
@@ -197,4 +195,5 @@ | ||
parseConnectionFromResult(span, result); | ||
getConnectionDetailsFromResultMeta(span, result); | ||
span.d = Date.now() - span.ts; | ||
span.transmit(); | ||
@@ -211,3 +210,3 @@ return result; | ||
if (error.meta && error.meta.meta) { | ||
parseConnectionFromResult(span, error.meta); | ||
getConnectionDetailsFromResultMeta(span, error.meta); | ||
} | ||
@@ -217,4 +216,37 @@ span.transmit(); | ||
function parseConnectionFromResult(span, result) { | ||
// NOTE: Does not work for v8 | ||
function parseAndCacheConnectionUrl(connectionUrl) { | ||
if (connectionUrl && connectionUrl instanceof url.URL) { | ||
// The connection URL is already in the form of a URL object. No need to parse it. | ||
return connectionUrl; | ||
} else if (typeof connectionUrl === 'string') { | ||
// The connection URL is only available as a string. We need to parse it to extract the host and the port. | ||
// Deliberately checking for undefined, since we store a failure to parse an URL as null. | ||
if (connectionUrlCache[connectionUrl] !== undefined) { | ||
return connectionUrlCache[connectionUrl]; | ||
} | ||
try { | ||
const parsedConnectionUrl = new URL(connectionUrl); | ||
// We do not want to spend the CPU cycles to parse the URL for each ES request. When we have parsed a given URL | ||
// once, we cache the resulting result and never parse that particular URL again. | ||
connectionUrlCache[connectionUrl] = parsedConnectionUrl; | ||
return parsedConnectionUrl; | ||
} catch (e) { | ||
// We also cache the fact that we failed to parse a given URL, otherwise we would try to parse it again on every | ||
// request. | ||
connectionUrlCache[connectionUrl] = null; | ||
return null; | ||
} | ||
} | ||
} | ||
function getConnectionDetailsFromResultMeta(span, result) { | ||
if (span.data.elasticsearch.address) { | ||
// We have already annotated the connection details, probably via shimGetConnection, no need to deal with connection | ||
// details again for this span. | ||
return; | ||
} | ||
// NOTE: This does not work for version 8. | ||
// Result can also be a part of the error object, both have the meta.connection attribute. | ||
@@ -228,5 +260,2 @@ // For the error object it is in error.meta.meta.connection (see onError). | ||
} | ||
} else if (parsedConnectionUrl) { | ||
span.data.elasticsearch.address = parsedConnectionUrl.hostname; | ||
span.data.elasticsearch.port = parsedConnectionUrl.port; | ||
} | ||
@@ -328,2 +357,3 @@ } | ||
shimmer.wrap(es.Transport.prototype, 'request', shimRequest); | ||
shimmer.wrap(es.Transport.prototype, 'getConnection', shimGetConnection); | ||
} else { | ||
@@ -336,2 +366,22 @@ logger.error( | ||
// Transport#request calls Transport#getConnection internally to determine which connection to use. That is, | ||
// Transport#getConnection is called while the Elasticsearch exit span is active, and we can use it to capture the | ||
// connection details. | ||
function shimGetConnection(originalGetConnection) { | ||
return function () { | ||
const connectionInfo = originalGetConnection.apply(this, arguments); | ||
if (connectionInfo && connectionInfo.url) { | ||
const span = cls.getCurrentSpan(); | ||
if (span && span.n === 'elasticsearch') { | ||
const parsedConnectionUrl = parseAndCacheConnectionUrl(connectionInfo.url); | ||
if (parsedConnectionUrl) { | ||
span.data.elasticsearch.address = parsedConnectionUrl.hostname; | ||
span.data.elasticsearch.port = parsedConnectionUrl.port; | ||
} | ||
} | ||
} | ||
return connectionInfo; | ||
}; | ||
} | ||
function shimRequest(esReq) { | ||
@@ -338,0 +388,0 @@ return function () { |
@@ -16,3 +16,4 @@ /* | ||
let isActive = false; | ||
let clientHasBeenInstrumented = false; | ||
let clientHasBeenInstrumentedV1 = false; | ||
let clientHasBeenInstrumentedV2 = false; | ||
@@ -34,31 +35,48 @@ exports.init = function init() { | ||
const client = originalFunction.apply(this, arguments); | ||
if (!clientHasBeenInstrumented) { | ||
const isPromise = client && client.then && client.catch; | ||
const isPromise = client && client.then && client.catch; | ||
if (isPromise) { | ||
// Nats 2.x | ||
// https://github.com/nats-io/nats.deno | ||
if (isPromise) { | ||
client.then(nc => { | ||
connectionObject = nc; | ||
client.then(nc => { | ||
connectionObject = nc; | ||
let natsUrl = 'nats://'; | ||
if (nc.protocol && nc.protocol.server && nc.protocol.server.listen) { | ||
natsUrl = `nats://${nc.protocol.server.listen}`; | ||
} | ||
let natsUrl = 'nats://'; | ||
if (nc.protocol && nc.protocol.server && nc.protocol.server.listen) { | ||
natsUrl = `nats://${nc.protocol.server.listen}`; | ||
} | ||
shimmer.wrap(nc.constructor.prototype, 'publish', shimPublish.bind(null, natsUrl, true)); | ||
shimmer.wrap(nc.constructor.prototype, 'request', shimRequest.bind(null, natsUrl)); | ||
shimmer.wrap(nc.constructor.prototype, 'subscribe', shimSubscribe.bind(null, natsUrl, true)); | ||
nc._natsUrl = natsUrl; | ||
clientHasBeenInstrumented = true; | ||
return nc; | ||
}); | ||
if (!clientHasBeenInstrumentedV2) { | ||
shimmer.wrap(nc.constructor.prototype, 'publish', shimPublish.bind(null, true)); | ||
shimmer.wrap(nc.constructor.prototype, 'request', shimRequest.bind(null)); | ||
shimmer.wrap(nc.constructor.prototype, 'subscribe', shimSubscribe.bind(null, true)); | ||
clientHasBeenInstrumentedV2 = true; | ||
} | ||
return nc; | ||
}); | ||
} else { | ||
// Nats 1.x | ||
if (client.options.url) { | ||
// Passing in options.url is one way to specify a server, | ||
// see https://github.com/nats-io/nats.js/tree/v1.4.12#basic-authentication | ||
client._natsUrl = client.options.url; | ||
} else if (Array.isArray(client.options.servers)) { | ||
// Providing a servers array is another way to specify the NATS server(s), | ||
// see https://github.com/nats-io/nats.js/tree/v1.4.12#clustered-usage | ||
client._natsUrl = client.options.servers[0]; | ||
} else { | ||
shimmer.wrap(client.constructor.prototype, 'publish', shimPublish.bind(null, client.options.url, false)); | ||
// default value if client is created without arguments | ||
client._natsUrl = 'nats://localhost:4222'; | ||
} | ||
if (!clientHasBeenInstrumentedV1) { | ||
shimmer.wrap(client.constructor.prototype, 'publish', shimPublish.bind(null, false)); | ||
// nats.requestOne uses nats.request internally, so there is no need to instrument requestOne separately | ||
shimmer.wrap(client.constructor.prototype, 'request', shimRequest.bind(null, client.options.url)); | ||
shimmer.wrap(client.constructor.prototype, 'request', shimRequest.bind(null)); | ||
shimmer.wrap(client.constructor.prototype, 'subscribe', shimSubscribe.bind(null, client.options.url, true)); | ||
clientHasBeenInstrumented = true; | ||
shimmer.wrap(client.constructor.prototype, 'subscribe', shimSubscribe.bind(null, true)); | ||
clientHasBeenInstrumentedV1 = true; | ||
} | ||
@@ -71,3 +89,3 @@ } | ||
function shimPublish(natsUrl, isLatest, originalFunction) { | ||
function shimPublish(isLatest, originalFunction) { | ||
return function () { | ||
@@ -79,3 +97,3 @@ const originalArgs = new Array(arguments.length); | ||
return instrumentedPublish(this, originalFunction, originalArgs, natsUrl, isLatest); | ||
return instrumentedPublish(this, originalFunction, originalArgs, isLatest); | ||
}; | ||
@@ -133,3 +151,3 @@ } | ||
function instrumentedPublish(ctx, originalPublish, originalArgs, natsUrl, isLatest) { | ||
function instrumentedPublish(ctx, originalPublish, originalArgs, isLatest) { | ||
const skipTracingResult = cls.skipExitTracing({ isActive: true, extendedResponse: true }); | ||
@@ -160,3 +178,3 @@ if (skipTracingResult.skip) { | ||
sort: 'publish', | ||
address: natsUrl, | ||
address: ctx._natsUrl, | ||
subject | ||
@@ -207,3 +225,3 @@ }; | ||
function shimRequest(natsUrl, originalFunction) { | ||
function shimRequest(originalFunction) { | ||
// nats 1.x: | ||
@@ -224,3 +242,3 @@ // nats.request uses nats.publish internally, we only need to cls-bind the callback here (it is not passed down to | ||
return instrumentedPublish(this, originalFunction, arguments, natsUrl); | ||
return instrumentedPublish(this, originalFunction, arguments); | ||
} else { | ||
@@ -242,3 +260,3 @@ for (let i = 3; i >= 0; i--) { | ||
function shimSubscribe(natsUrl, isLatest, originalFunction) { | ||
function shimSubscribe(isLatest, originalFunction) { | ||
return function () { | ||
@@ -249,7 +267,7 @@ const originalSubscribeArgs = new Array(arguments.length); | ||
} | ||
return instrumentedSubscribe(this, originalFunction, originalSubscribeArgs, natsUrl, isLatest); | ||
return instrumentedSubscribe(this, originalFunction, originalSubscribeArgs, isLatest); | ||
}; | ||
} | ||
function instrumentedSubscribe(ctx, originalSubscribe, originalSubscribeArgs, natsUrl, isLatest) { | ||
function instrumentedSubscribe(ctx, originalSubscribe, originalSubscribeArgs, isLatest) { | ||
const subject = originalSubscribeArgs[0]; | ||
@@ -278,7 +296,10 @@ let callbackIndex = -1; | ||
originalSubscribeArgs[callbackIndex].callback = function (err, msg) { | ||
return instrumentedSubscribeCallback(natsUrl, subject, originalCallback, null, isLatest).bind(this)(err, msg); | ||
return instrumentedSubscribeCallback(ctx._natsUrl, subject, originalCallback, null, isLatest).bind(this)( | ||
err, | ||
msg | ||
); | ||
}; | ||
} else { | ||
originalCallback = originalSubscribeArgs[callbackIndex]; | ||
originalSubscribeArgs[callbackIndex] = instrumentedSubscribeCallback(natsUrl, subject, originalCallback); | ||
originalSubscribeArgs[callbackIndex] = instrumentedSubscribeCallback(ctx._natsUrl, subject, originalCallback); | ||
} | ||
@@ -298,3 +319,3 @@ | ||
await new Promise(resolve => { | ||
instrumentedSubscribeCallback(natsUrl, subject, resolve, currentCtx, isLatest)(null, msg); | ||
instrumentedSubscribeCallback(ctx._natsUrl, subject, resolve, currentCtx, isLatest)(null, msg); | ||
}); | ||
@@ -301,0 +322,0 @@ |
@@ -6,2 +6,5 @@ /* | ||
// Note: nats-streaming is in the process of being deprecated. | ||
// https://docs.nats.io/legacy/stan#warning-deprecation-notice | ||
'use strict'; | ||
@@ -30,5 +33,6 @@ | ||
const client = originalFunction.apply(this, arguments); | ||
client._natsUrl = client.options.url; | ||
if (!clientHasBeenInstrumented) { | ||
shimmer.wrap(client.constructor.prototype, 'publish', shimPublish.bind(null, client.options.url)); | ||
shimmer.wrap(client.constructor.prototype, 'subscribe', shimSubscribe.bind(null, client.options.url)); | ||
shimmer.wrap(client.constructor.prototype, 'publish', shimPublish); | ||
shimmer.wrap(client.constructor.prototype, 'subscribe', shimSubscribe); | ||
clientHasBeenInstrumented = true; | ||
@@ -40,3 +44,3 @@ } | ||
function shimPublish(natsUrl, originalFunction) { | ||
function shimPublish(originalFunction) { | ||
return function () { | ||
@@ -48,7 +52,7 @@ const originalArgs = new Array(arguments.length); | ||
return instrumentedPublish(this, originalFunction, originalArgs, natsUrl); | ||
return instrumentedPublish(this, originalFunction, originalArgs); | ||
}; | ||
} | ||
function instrumentedPublish(ctx, originalPublish, originalArgs, natsUrl) { | ||
function instrumentedPublish(ctx, originalPublish, originalArgs) { | ||
if (cls.skipExitTracing({ isActive })) { | ||
@@ -67,3 +71,3 @@ return originalPublish.apply(ctx, originalArgs); | ||
sort: 'publish', | ||
address: natsUrl, | ||
address: ctx._natsUrl, | ||
subject | ||
@@ -93,7 +97,7 @@ }; | ||
function shimSubscribe(natsUrl, originalFunction) { | ||
function shimSubscribe(originalFunction) { | ||
return function () { | ||
const subscription = originalFunction.apply(this, arguments); | ||
if (subscription) { | ||
shimmer.wrap(subscription, 'emit', shimSubscriptionEmit.bind(null, natsUrl, arguments[0])); | ||
shimmer.wrap(subscription, 'emit', shimSubscriptionEmit.bind(null, arguments[0])); | ||
} | ||
@@ -104,3 +108,3 @@ return subscription; | ||
function shimSubscriptionEmit(natsUrl, subject, originalFunction) { | ||
function shimSubscriptionEmit(subject, originalFunction) { | ||
return function (type) { | ||
@@ -112,3 +116,3 @@ if (isActive && (type === 'message' || type === 'error')) { | ||
} | ||
return instrumentedEmit(this, originalFunction, originalArgs, natsUrl, subject); | ||
return instrumentedEmit(this, originalFunction, originalArgs, subject); | ||
} | ||
@@ -119,5 +123,5 @@ return originalFunction.apply(this, arguments); | ||
function instrumentedEmit(ctx, originalEmit, originalArgs, natsUrl, subject) { | ||
function instrumentedEmit(ctx, originalEmit, originalArgs, subject) { | ||
if (originalArgs[0] === 'message') { | ||
return captureMessageSpan(ctx, originalEmit, originalArgs, natsUrl, subject); | ||
return captureMessageSpan(ctx, originalEmit, originalArgs, subject); | ||
} else if (originalArgs[0] === 'error') { | ||
@@ -128,6 +132,7 @@ return captureErrorInCurrentSpan(ctx, originalEmit, originalArgs); | ||
function captureMessageSpan(ctx, originalEmit, originalArgs, natsUrl, subject) { | ||
function captureMessageSpan(ctx, originalEmit, originalArgs, subject) { | ||
let span; | ||
const activeSpan = cls.getCurrentSpan(); | ||
let natsUrl; | ||
if (activeSpan && activeSpan.n === 'nats' && constants.isEntrySpan(activeSpan)) { | ||
@@ -139,2 +144,3 @@ // Expected case: The raw nats instrumentation kicks in earlier than the nats-streaming instrumentation, so we | ||
span.n = 'nats.streaming'; | ||
natsUrl = span.data.nats.address; | ||
} else if (activeSpan) { | ||
@@ -141,0 +147,0 @@ // Unexpected: There is already an active span, but it is not a raw nats entry span. Abort tracing this |
725629
19147