From 247a6fbd38b971522fff8c1c4ea3af4a8ab5949c Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 14 Jan 2026 14:04:54 +0530 Subject: [PATCH 1/9] chore: updated instrumentations with safety check on promises --- .../instrumentation/cloud/gcp/storage.js | 2 +- .../instrumentation/databases/couchbase.js | 4 +- .../databases/elasticsearch.js | 24 +++-- .../instrumentation/databases/ioredis.js | 14 +-- .../instrumentation/databases/mongodb.js | 2 +- .../instrumentation/databases/mssql.js | 2 +- .../instrumentation/databases/mysql.js | 47 +++++---- .../tracing/instrumentation/frameworks/koa.js | 24 ++--- .../tracing/instrumentation/messaging/amqp.js | 95 ++++++++++--------- .../tracing/instrumentation/messaging/bull.js | 21 ++-- .../instrumentation/messaging/kafkaJs.js | 62 ++++++------ .../instrumentation/protocols/nativeFetch.js | 48 +++++----- 12 files changed, 190 insertions(+), 155 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js index c1d4fbb43d..f91612dbe1 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js @@ -416,7 +416,7 @@ function instrumentedOperation(operation, extractorPre, extractorPost, ctx, orig } const promise = original.apply(ctx, originalArgs); - if (promise) { + if (promise && typeof promise.then === 'function') { promise.then( result => finishSpan(null, Array.isArray(result) ? result[0] : result, span, extractorPost), e => finishSpan(e, null, span, extractorPost) diff --git a/packages/core/src/tracing/instrumentation/databases/couchbase.js b/packages/core/src/tracing/instrumentation/databases/couchbase.js index 2d7a0df358..dee2b58472 100644 --- a/packages/core/src/tracing/instrumentation/databases/couchbase.js +++ b/packages/core/src/tracing/instrumentation/databases/couchbase.js @@ -77,7 +77,7 @@ function instrumentConnect(originalConnect) { const prom = originalConnect.apply(originalThis, originalArgs); - if (prom && prom.then) { + if (prom && typeof prom.then === 'function') { prom.then(cluster => { instrumentCluster(cluster, connectionStr); return cluster; @@ -488,7 +488,7 @@ function instrumentOperation({ connectionStr, bucketName, getBucketTypeFn, sql, if (callbackIndex < 0) { const prom = original.apply(originalThis, originalArgs); - if (prom.then && prom.catch) { + if (typeof prom?.then === 'function' && typeof prom?.catch === 'function') { prom .then(result => { if (resultHandler) { diff --git a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js index 4877c53604..46a6f2b774 100644 --- a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js +++ b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js @@ -155,10 +155,14 @@ function instrumentApi(client, actionPath, clusterInfo) { } else { // eslint-disable-next-line no-useless-catch try { - return originalFunction.apply(ctx, originalArgs).then(onSuccess.bind(null, span), error => { - onError(span, error); - throw error; - }); + const promise = originalFunction.apply(ctx, originalArgs); + if (typeof promise?.then === 'function') { + return promise.then(onSuccess.bind(null, span), error => { + onError(span, error); + throw error; + }); + } + return promise; } catch (e) { // Immediately cleanup on synchronous errors. throw e; @@ -448,10 +452,14 @@ function instrumentedRequest(ctx, origEsReq, originalArgs) { } else { // eslint-disable-next-line no-useless-catch try { - return origEsReq.apply(ctx, originalArgs).then(onSuccess.bind(null, span), error => { - onError(span, error); - throw error; - }); + const promise = origEsReq.apply(ctx, originalArgs); + if (typeof promise?.then === 'function') { + return promise.then(onSuccess.bind(null, span), error => { + onError(span, error); + throw error; + }); + } + return promise; } catch (e) { // Immediately cleanup on synchronous errors. throw e; diff --git a/packages/core/src/tracing/instrumentation/databases/ioredis.js b/packages/core/src/tracing/instrumentation/databases/ioredis.js index e88a1c69e3..f20dc059b7 100644 --- a/packages/core/src/tracing/instrumentation/databases/ioredis.js +++ b/packages/core/src/tracing/instrumentation/databases/ioredis.js @@ -97,11 +97,13 @@ function instrumentSendCommand(original) { span.stack = tracingUtil.getStackTrace(wrappedInternalSendCommand); callback = cls.ns.bind(onResult); - command.promise.then( - // make sure that the first parameter is never truthy - callback.bind(null, null), - callback - ); + if (typeof command.promise?.then === 'function') { + command.promise.then( + // make sure that the first parameter is never truthy + callback.bind(null, null), + callback + ); + } return original.apply(client, argsForOriginal); @@ -190,7 +192,7 @@ function instrumentMultiOrPipelineExec(clsContextForMultiOrPipeline, commandName span.ts = Date.now(); const result = original.apply(this, arguments); - if (result.then) { + if (typeof result?.then === 'function') { result.then( results => { endCallback.call(null, clsContextForMultiOrPipeline, span, null, results); diff --git a/packages/core/src/tracing/instrumentation/databases/mongodb.js b/packages/core/src/tracing/instrumentation/databases/mongodb.js index 7582e1917c..b2928bf976 100644 --- a/packages/core/src/tracing/instrumentation/databases/mongodb.js +++ b/packages/core/src/tracing/instrumentation/databases/mongodb.js @@ -457,7 +457,7 @@ function handleCallbackOrPromise(ctx, originalArgs, originalFunction, span) { const resultPromise = originalFunction.apply(ctx, originalArgs); - if (resultPromise && resultPromise.then) { + if (resultPromise && typeof resultPromise.then === 'function') { resultPromise .then(result => { span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/mssql.js b/packages/core/src/tracing/instrumentation/databases/mssql.js index d1cf837774..ddd5f199fe 100644 --- a/packages/core/src/tracing/instrumentation/databases/mssql.js +++ b/packages/core/src/tracing/instrumentation/databases/mssql.js @@ -88,7 +88,7 @@ function instrumentedMethod(ctx, originalFunction, originalArgs, stackTraceRef, } const promise = originalFunction.apply(ctx, originalArgs); - if (typeof promise.then === 'function') { + if (typeof promise?.then === 'function') { promise .then(value => { finishSpan(null, span); diff --git a/packages/core/src/tracing/instrumentation/databases/mysql.js b/packages/core/src/tracing/instrumentation/databases/mysql.js index a33688fa86..d90ff198cf 100644 --- a/packages/core/src/tracing/instrumentation/databases/mysql.js +++ b/packages/core/src/tracing/instrumentation/databases/mysql.js @@ -182,20 +182,23 @@ function instrumentedAccessFunction( if (isPromiseImpl) { const resultPromise = originalFunction.apply(ctx, originalArgs); - resultPromise - .then(result => { - span.d = Date.now() - span.ts; - span.transmit(); - return result; - }) - .catch(error => { - span.ec = 1; - tracingUtil.setErrorDetails(span, error, exports.spanName); - - span.d = Date.now() - span.ts; - span.transmit(); - return error; - }); + if (typeof resultPromise?.then === 'function') { + resultPromise + .then(result => { + span.d = Date.now() - span.ts; + span.transmit(); + return result; + }) + .catch(error => { + span.ec = 1; + tracingUtil.setErrorDetails(span, error, exports.spanName); + + span.d = Date.now() - span.ts; + span.transmit(); + throw error; + }); + } + return resultPromise; } @@ -238,12 +241,16 @@ function shimGetConnection(original) { function shimPromiseConnection(original) { return function getConnection() { - return original.apply(this, arguments).then(connection => { - shimmer.wrap(connection, 'query', shimPromiseQuery); - shimmer.wrap(connection, 'execute', shimPromiseExecute); - - return connection; - }); + const promise = original.apply(this, arguments); + if (typeof promise?.then === 'function') { + return promise.then(connection => { + shimmer.wrap(connection, 'query', shimPromiseQuery); + shimmer.wrap(connection, 'execute', shimPromiseExecute); + + return connection; + }); + } + return promise; }; } diff --git a/packages/core/src/tracing/instrumentation/frameworks/koa.js b/packages/core/src/tracing/instrumentation/frameworks/koa.js index 48b72c57b4..26b12806ba 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/koa.js +++ b/packages/core/src/tracing/instrumentation/frameworks/koa.js @@ -61,18 +61,20 @@ function instrumentedRoutes(thisContext, originalRoutes, originalArgs) { 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); + if (typeof dispatchResult?.then === 'function') { + 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; + }); + } + return dispatchResult; } + 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 diff --git a/packages/core/src/tracing/instrumentation/messaging/amqp.js b/packages/core/src/tracing/instrumentation/messaging/amqp.js index d0fcad6b46..dc01a22ac3 100644 --- a/packages/core/src/tracing/instrumentation/messaging/amqp.js +++ b/packages/core/src/tracing/instrumentation/messaging/amqp.js @@ -249,56 +249,61 @@ function instrumentedChannelModelGet(ctx, originalGet, originalArgs) { kind: constants.ENTRY }); - return originalGet.apply(ctx, originalArgs).then(result => { - if (!result) { - // get did not fetch a new message from RabbitMQ (because the queue has no messages), no need to create a span. - span.cancel(); - return result; - } - const fields = result.fields || {}; - const headers = result.properties && result.properties.headers ? result.properties.headers : {}; - - if (tracingUtil.readAttribCaseInsensitive(headers, constants.traceLevelHeaderName) === '0') { - cls.setTracingLevel('0'); - span.cancel(); - return result; - } + const promise = originalGet.apply(ctx, originalArgs); + if (typeof promise?.then === 'function') { + return promise.then(result => { + if (!result) { + // get did not fetch a new message from RabbitMQ (because the queue has no messages), no need to create a + // span. + span.cancel(); + return result; + } + const fields = result.fields || {}; + const headers = result.properties && result.properties.headers ? result.properties.headers : {}; - const traceId = tracingUtil.readAttribCaseInsensitive(headers, constants.traceIdHeaderName); - const parentSpanId = tracingUtil.readAttribCaseInsensitive(headers, constants.spanIdHeaderName); - if (traceId && parentSpanId) { - span.t = traceId; - span.p = parentSpanId; - } + if (tracingUtil.readAttribCaseInsensitive(headers, constants.traceLevelHeaderName) === '0') { + cls.setTracingLevel('0'); + span.cancel(); + return result; + } - span.ts = Date.now(); - span.stack = tracingUtil.getStackTrace(instrumentedChannelModelGet); - span.data.rabbitmq = { - sort: 'consume' - }; + const traceId = tracingUtil.readAttribCaseInsensitive(headers, constants.traceIdHeaderName); + const parentSpanId = tracingUtil.readAttribCaseInsensitive(headers, constants.spanIdHeaderName); + if (traceId && parentSpanId) { + span.t = traceId; + span.p = parentSpanId; + } - if (ctx.connection.stream) { - span.data.rabbitmq.address = - typeof ctx.connection.stream.getProtocol === 'function' - ? 'amqps://' - : // - `amqp://${ctx.connection.stream.remoteAddress}:${ctx.connection.stream.remotePort}`; - } - if (fields.exchange) { - span.data.rabbitmq.exchange = fields.exchange; - } - if (fields.routingKey) { - span.data.rabbitmq.key = fields.routingKey; - } + span.ts = Date.now(); + span.stack = tracingUtil.getStackTrace(instrumentedChannelModelGet); + span.data.rabbitmq = { + sort: 'consume' + }; + + if (ctx.connection.stream) { + span.data.rabbitmq.address = + typeof ctx.connection.stream.getProtocol === 'function' + ? 'amqps://' + : // + `amqp://${ctx.connection.stream.remoteAddress}:${ctx.connection.stream.remotePort}`; + } + if (fields.exchange) { + span.data.rabbitmq.exchange = fields.exchange; + } + if (fields.routingKey) { + span.data.rabbitmq.key = fields.routingKey; + } - setImmediate(() => { - // Client code is expected to end the span manually, end it automatically in case client code doesn't. Child - // exit spans won't be captured, but at least the RabbitMQ entry span is there. - span.d = Date.now() - span.ts; - span.transmit(); + setImmediate(() => { + // Client code is expected to end the span manually, end it automatically in case client code doesn't. Child + // exit spans won't be captured, but at least the RabbitMQ entry span is there. + span.d = Date.now() - span.ts; + span.transmit(); + }); + return result; }); - return result; - }); + } + return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index f09fc0474d..4f6be2b2c5 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -100,15 +100,18 @@ function instrumentedJobCreate(ctx, originalJobCreate, originalArgs, options) { const promise = originalJobCreate.apply(ctx, originalArgs); - return promise - .then(job => { - finishSpan(null, job, span); - return job; - }) - .catch(err => { - finishSpan(err, null, span); - return err; - }); + if (typeof promise?.then === 'function') { + return promise + .then(job => { + finishSpan(null, job, span); + return job; + }) + .catch(err => { + finishSpan(err, null, span); + return err; + }); + } + return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js index f887225f13..8bb125c31c 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js @@ -96,20 +96,23 @@ function instrumentedSend(ctx, originalSend, originalArgs, topic, messages) { } span.stack = tracingUtil.getStackTrace(instrumentedSend); - return originalSend - .apply(ctx, originalArgs) - .then(result => { - span.d = Date.now() - span.ts; - span.transmit(); - return result; - }) - .catch(error => { - span.ec = 1; - tracingUtil.setErrorDetails(span, error, 'kafka'); - span.d = Date.now() - span.ts; - span.transmit(); - throw error; - }); + const promise = originalSend.apply(ctx, originalArgs); + if (typeof promise?.then === 'function') { + return promise + .then(result => { + span.d = Date.now() - span.ts; + span.transmit(); + return result; + }) + .catch(error => { + span.ec = 1; + tracingUtil.setErrorDetails(span, error, 'kafka'); + span.d = Date.now() - span.ts; + span.transmit(); + throw error; + }); + } + return promise; }); } @@ -175,20 +178,23 @@ function instrumentedSendBatch(ctx, originalSendBatch, originalArgs, topicMessag span.b = { s: messageCount }; } - return originalSendBatch - .apply(ctx, originalArgs) - .then(result => { - span.d = Date.now() - span.ts; - span.transmit(); - return result; - }) - .catch(error => { - span.ec = 1; - tracingUtil.setErrorDetails(span, error, 'kafka'); - span.d = Date.now() - span.ts; - span.transmit(); - throw error; - }); + const promise = originalSendBatch.apply(ctx, originalArgs); + if (typeof promise?.then === 'function') { + return promise + .then(result => { + span.d = Date.now() - span.ts; + span.transmit(); + return result; + }) + .catch(error => { + span.ec = 1; + tracingUtil.setErrorDetails(span, error, 'kafka'); + span.d = Date.now() - span.ts; + span.transmit(); + throw error; + }); + } + return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js index 9b72e210f6..d1bf61061c 100644 --- a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js +++ b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js @@ -171,29 +171,31 @@ function instrument() { injectTraceCorrelationHeaders(originalArgs, span, w3cTraceContext); const fetchPromise = originalFetch.apply(originalThis, originalArgs); - fetchPromise - .then(response => { - span.data.http.status = response.status; - span.ec = response.status >= 500 ? 1 : 0; - capturedHeaders = mergeExtraHeadersFromFetchHeaders( - capturedHeaders, - response.headers, - extraHttpHeadersToCapture - ); - - span.d = Date.now() - span.ts; - if (capturedHeaders != null && Object.keys(capturedHeaders).length > 0) { - span.data.http.header = capturedHeaders; - } - span.transmit(); - }) - .catch(err => { - span.ec = 1; - tracingUtil.setErrorDetails(span, err, 'http'); - - span.d = Date.now() - span.ts; - span.transmit(); - }); + if (typeof fetchPromise?.then === 'function') { + fetchPromise + .then(response => { + span.data.http.status = response.status; + span.ec = response.status >= 500 ? 1 : 0; + capturedHeaders = mergeExtraHeadersFromFetchHeaders( + capturedHeaders, + response.headers, + extraHttpHeadersToCapture + ); + + span.d = Date.now() - span.ts; + if (capturedHeaders != null && Object.keys(capturedHeaders).length > 0) { + span.data.http.header = capturedHeaders; + } + span.transmit(); + }) + .catch(err => { + span.ec = 1; + tracingUtil.setErrorDetails(span, err, 'http'); + + span.d = Date.now() - span.ts; + span.transmit(); + }); + } return fetchPromise; }); From 30c7b0db695b373180d1ca02a69b2bd991b9e08d Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 15 Jan 2026 14:34:47 +0530 Subject: [PATCH 2/9] chore: updated more libs --- .../cloud/aws-sdk/v3/sqs-consumer.js | 54 +++++++++++-------- .../control_flow/graphqlSubscriptions.js | 27 ++++++---- .../tracing/instrumentation/messaging/bull.js | 36 ++++++++----- 3 files changed, 70 insertions(+), 47 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js index cd29f1aba6..70b79a2798 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js @@ -24,17 +24,22 @@ function instrument(SQSConsumer) { span.disableAutoEnd(); const res = orig.apply(this, arguments); - res - .then(() => { - span.d = Date.now() - span.ts; - span.transmitManual(); - }) - .catch(err => { - span.ec = 1; - tracingUtil.setErrorDetails(span, err, 'sqs'); - span.d = Date.now() - span.ts; - span.transmitManual(); - }); + if (res && typeof res.then === 'function') { + res + .then(() => { + span.d = Date.now() - span.ts; + span.transmitManual(); + }) + .catch(err => { + span.ec = 1; + tracingUtil.setErrorDetails(span, err, 'sqs'); + span.d = Date.now() - span.ts; + span.transmitManual(); + }); + } else { + span.d = Date.now() - span.ts; + span.transmitManual(); + } return res; }); @@ -56,17 +61,22 @@ function instrument(SQSConsumer) { span.disableAutoEnd(); const res = orig.apply(this, arguments); - res - .then(() => { - span.d = Date.now() - span.ts; - span.transmitManual(); - }) - .catch(err => { - span.ec = 1; - tracingUtil.setErrorDetails(span, err, 'sqs'); - span.d = Date.now() - span.ts; - span.transmitManual(); - }); + if (res && typeof res.then === 'function') { + res + .then(() => { + span.d = Date.now() - span.ts; + span.transmitManual(); + }) + .catch(err => { + span.ec = 1; + tracingUtil.setErrorDetails(span, err, 'sqs'); + span.d = Date.now() - span.ts; + span.transmitManual(); + }); + } else { + span.d = Date.now() - span.ts; + span.transmitManual(); + } return res; }); diff --git a/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js b/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js index 8197259f91..e7edc1daba 100644 --- a/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js +++ b/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js @@ -62,18 +62,23 @@ function shimPushValue(originalFunction) { function shimPullValue(originalFunction) { return function () { const pullPromise = originalFunction.apply(this, arguments); - return pullPromise.then(result => { - if (result && result.value && result.value[CLS_CONTEXT_SYMBOL]) { - const clsContext = result.value[CLS_CONTEXT_SYMBOL]; - if (isActive && clsContext) { - cls.ns.enter(clsContext); - setImmediate(() => { - cls.ns.exit(clsContext); - }); + + if (pullPromise && typeof pullPromise.then === 'function') { + return pullPromise.then(result => { + if (result && result.value && result.value[CLS_CONTEXT_SYMBOL]) { + const clsContext = result.value[CLS_CONTEXT_SYMBOL]; + if (isActive && clsContext) { + cls.ns.enter(clsContext); + setImmediate(() => { + cls.ns.exit(clsContext); + }); + } } - } - return result; - }); + return result; + }); + } + + return pullPromise; }; } diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index 4f6be2b2c5..f099a45b2f 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -111,6 +111,8 @@ function instrumentedJobCreate(ctx, originalJobCreate, originalArgs, options) { return err; }); } + + finishSpan(null, null, span); return promise; }); } @@ -261,20 +263,26 @@ function instrumentedProcessJob(ctx, originalProcessJob, originalArgs) { const promise = originalProcessJob.apply(ctx, originalArgs); - return promise - .then(data => { - finishSpan(job.failedReason, data, span); - // Make sure the instana foreigner data is removed. - delete options.X_INSTANA_L; - return data; - }) - .catch(err => { - addErrorToSpan(err, span); - finishSpan(null, null, span); - // Make sure the instana foreigner data is removed. - delete options.X_INSTANA_L; - throw err; - }); + if (promise && typeof promise.then === 'function') { + return promise + .then(data => { + finishSpan(job.failedReason, data, span); + // Make sure the instana foreigner data is removed. + delete options.X_INSTANA_L; + return data; + }) + .catch(err => { + addErrorToSpan(err, span); + finishSpan(null, null, span); + // Make sure the instana foreigner data is removed. + delete options.X_INSTANA_L; + throw err; + }); + } + + finishSpan(null, null, span); + delete options.X_INSTANA_L; + return promise; }); } From c96c83621248ea24c22f2c82db248c6bd08d7bd7 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Tue, 20 Jan 2026 11:43:51 +0530 Subject: [PATCH 3/9] chore: corrected the refactoring --- .../instrumentation/cloud/aws-sdk/v3/sqs-consumer.js | 6 ------ .../core/src/tracing/instrumentation/databases/mysql.js | 1 - .../core/src/tracing/instrumentation/frameworks/koa.js | 5 ++--- .../core/src/tracing/instrumentation/messaging/amqp.js | 1 - .../core/src/tracing/instrumentation/messaging/bull.js | 7 ------- .../core/src/tracing/instrumentation/messaging/kafkaJs.js | 2 -- 6 files changed, 2 insertions(+), 20 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js index 70b79a2798..a5ce0781a2 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js @@ -36,9 +36,6 @@ function instrument(SQSConsumer) { span.d = Date.now() - span.ts; span.transmitManual(); }); - } else { - span.d = Date.now() - span.ts; - span.transmitManual(); } return res; @@ -73,9 +70,6 @@ function instrument(SQSConsumer) { span.d = Date.now() - span.ts; span.transmitManual(); }); - } else { - span.d = Date.now() - span.ts; - span.transmitManual(); } return res; diff --git a/packages/core/src/tracing/instrumentation/databases/mysql.js b/packages/core/src/tracing/instrumentation/databases/mysql.js index d90ff198cf..603fa058f2 100644 --- a/packages/core/src/tracing/instrumentation/databases/mysql.js +++ b/packages/core/src/tracing/instrumentation/databases/mysql.js @@ -250,7 +250,6 @@ function shimPromiseConnection(original) { return connection; }); } - return promise; }; } diff --git a/packages/core/src/tracing/instrumentation/frameworks/koa.js b/packages/core/src/tracing/instrumentation/frameworks/koa.js index 26b12806ba..a7eed9244b 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/koa.js +++ b/packages/core/src/tracing/instrumentation/frameworks/koa.js @@ -72,11 +72,10 @@ function instrumentedRoutes(thisContext, originalRoutes, originalArgs) { return resolvedValue; }); } - return dispatchResult; + } else { + return dispatch.apply(this, arguments); } - 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; diff --git a/packages/core/src/tracing/instrumentation/messaging/amqp.js b/packages/core/src/tracing/instrumentation/messaging/amqp.js index dc01a22ac3..6608c1931c 100644 --- a/packages/core/src/tracing/instrumentation/messaging/amqp.js +++ b/packages/core/src/tracing/instrumentation/messaging/amqp.js @@ -303,7 +303,6 @@ function instrumentedChannelModelGet(ctx, originalGet, originalArgs) { return result; }); } - return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index f099a45b2f..39f7cdadc9 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -111,9 +111,6 @@ function instrumentedJobCreate(ctx, originalJobCreate, originalArgs, options) { return err; }); } - - finishSpan(null, null, span); - return promise; }); } @@ -279,10 +276,6 @@ function instrumentedProcessJob(ctx, originalProcessJob, originalArgs) { throw err; }); } - - finishSpan(null, null, span); - delete options.X_INSTANA_L; - return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js index 8bb125c31c..0a44ce1626 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js @@ -112,7 +112,6 @@ function instrumentedSend(ctx, originalSend, originalArgs, topic, messages) { throw error; }); } - return promise; }); } @@ -194,7 +193,6 @@ function instrumentedSendBatch(ctx, originalSendBatch, originalArgs, topicMessag throw error; }); } - return promise; }); } From 78e68606823f8b7c630212ece6ed643bf8098570 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 4 Feb 2026 12:30:33 +0530 Subject: [PATCH 4/9] chore: case 5 added --- .../instrumentation/cloud/azure/blob.js | 6 +- .../instrumentation/databases/couchbase.js | 55 ++++++++++++++----- .../tracing/instrumentation/databases/db2.js | 8 ++- .../instrumentation/databases/mongodb.js | 5 ++ .../instrumentation/databases/mssql.js | 4 ++ .../instrumentation/databases/mysql.js | 5 ++ .../tracing/instrumentation/databases/pg.js | 4 ++ .../instrumentation/databases/prisma.js | 8 ++- .../instrumentation/databases/redis.js | 10 +++- packages/core/src/tracing/tracingUtil.js | 37 +++++++++++++ 10 files changed, 120 insertions(+), 22 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js index d2b076cf13..72b84bc7f7 100644 --- a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js +++ b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js @@ -87,7 +87,8 @@ function instrumentingOperation({ op }; const promise = originalQuery.apply(ctx, argsForOriginalQuery); - if (promise && typeof promise.then === 'function') { + + if (promise && typeof promise?.then === 'function') { promise .then(value => { finishSpan(null, span); @@ -97,6 +98,9 @@ function instrumentingOperation({ finishSpan(error, span); return error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'azstorage', 'blob operation'); + finishSpan(null, span); } return promise; }); diff --git a/packages/core/src/tracing/instrumentation/databases/couchbase.js b/packages/core/src/tracing/instrumentation/databases/couchbase.js index dee2b58472..b3dfc9059f 100644 --- a/packages/core/src/tracing/instrumentation/databases/couchbase.js +++ b/packages/core/src/tracing/instrumentation/databases/couchbase.js @@ -426,7 +426,7 @@ function instrumentTransactions(cluster, connectionStr) { const result = originalFn.apply(this, arguments); - if (result.then && result.catch) { + if (result && result?.then && result?.catch) { result .then(() => { span.d = Date.now() - span.ts; @@ -486,7 +486,17 @@ function instrumentOperation({ connectionStr, bucketName, getBucketTypeFn, sql, const { originalCallback, callbackIndex } = tracingUtil.findCallback(originalArgs); if (callbackIndex < 0) { - const prom = original.apply(originalThis, originalArgs); + // Case 4: Handle synchronous validation errors for promise-based calls + let prom; + try { + prom = original.apply(originalThis, originalArgs); + } catch (syncError) { + span.ec = 1; + tracingUtil.setErrorDetails(span, syncError, 'couchbase'); + span.d = Date.now() - span.ts; + span.transmit(); + throw syncError; + } if (typeof prom?.then === 'function' && typeof prom?.catch === 'function') { prom @@ -505,27 +515,42 @@ function instrumentOperation({ connectionStr, bucketName, getBucketTypeFn, sql, span.d = Date.now() - span.ts; span.transmit(); }); + } else if (prom !== undefined) { + // Case 5: Use utility function + tracingUtil.handleUnexpectedReturnValue(prom, span, 'couchbase', 'operation'); + + span.d = Date.now() - span.ts; + span.transmit(); } return prom; } else { - originalArgs[callbackIndex] = cls.ns.bind(function instanaCallback(err, result) { - if (err) { - span.ec = 1; - tracingUtil.setErrorDetails(span, err, 'couchbase'); - } + // Case 4: Handle synchronous validation errors for callback-based calls + try { + originalArgs[callbackIndex] = cls.ns.bind(function instanaCallback(err, result) { + if (err) { + span.ec = 1; + tracingUtil.setErrorDetails(span, err, 'couchbase'); + } - if (resultHandler) { - resultHandler(span, result); - } + if (resultHandler) { + resultHandler(span, result); + } - span.d = Date.now() - span.ts; - span.transmit(); + span.d = Date.now() - span.ts; + span.transmit(); - return originalCallback.apply(this, arguments); - }); + return originalCallback.apply(this, arguments); + }); - return original.apply(originalThis, originalArgs); + return original.apply(originalThis, originalArgs); + } catch (syncError) { + span.ec = 1; + tracingUtil.setErrorDetails(span, syncError, 'couchbase'); + span.d = Date.now() - span.ts; + span.transmit(); + throw syncError; + } } }); }; diff --git a/packages/core/src/tracing/instrumentation/databases/db2.js b/packages/core/src/tracing/instrumentation/databases/db2.js index c6819d219c..579e5577b5 100644 --- a/packages/core/src/tracing/instrumentation/databases/db2.js +++ b/packages/core/src/tracing/instrumentation/databases/db2.js @@ -314,6 +314,7 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn return originalFunction.apply(ctx, originalArgs); } + // Case 4: Handle synchronous validation errors for promise-based calls const resultPromise = originalFunction.apply(ctx, originalArgs); if (resultPromise && typeof resultPromise.then === 'function' && typeof resultPromise.catch === 'function') { @@ -328,9 +329,12 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn finishSpan(ctx, null, span); return err; }); - - return resultPromise; + } else { + tracingUtil.handleUnexpectedReturnValue(resultPromise, span, 'ibmdb2', 'query'); + finishSpan(ctx, null, span); } + + return resultPromise; }); } diff --git a/packages/core/src/tracing/instrumentation/databases/mongodb.js b/packages/core/src/tracing/instrumentation/databases/mongodb.js index b2928bf976..a2ecf1f663 100644 --- a/packages/core/src/tracing/instrumentation/databases/mongodb.js +++ b/packages/core/src/tracing/instrumentation/databases/mongodb.js @@ -471,6 +471,11 @@ function handleCallbackOrPromise(ctx, originalArgs, originalFunction, span) { span.transmit(); return err; }); + } else { + tracingUtil.handleUnexpectedReturnValue(resultPromise, span, 'mongo', 'command'); + + span.d = Date.now() - span.ts; + span.transmit(); } return resultPromise; diff --git a/packages/core/src/tracing/instrumentation/databases/mssql.js b/packages/core/src/tracing/instrumentation/databases/mssql.js index ddd5f199fe..d01ee7a024 100644 --- a/packages/core/src/tracing/instrumentation/databases/mssql.js +++ b/packages/core/src/tracing/instrumentation/databases/mssql.js @@ -88,6 +88,7 @@ function instrumentedMethod(ctx, originalFunction, originalArgs, stackTraceRef, } const promise = originalFunction.apply(ctx, originalArgs); + if (typeof promise?.then === 'function') { promise .then(value => { @@ -98,6 +99,9 @@ function instrumentedMethod(ctx, originalFunction, originalArgs, stackTraceRef, finishSpan(error, span); return error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'mssql', 'query/execute'); + finishSpan(null, span); } return promise; }); diff --git a/packages/core/src/tracing/instrumentation/databases/mysql.js b/packages/core/src/tracing/instrumentation/databases/mysql.js index 603fa058f2..88f9237f74 100644 --- a/packages/core/src/tracing/instrumentation/databases/mysql.js +++ b/packages/core/src/tracing/instrumentation/databases/mysql.js @@ -197,6 +197,11 @@ function instrumentedAccessFunction( span.transmit(); throw error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(resultPromise, span, 'mysql', 'query/execute'); + + span.d = Date.now() - span.ts; + span.transmit(); } return resultPromise; diff --git a/packages/core/src/tracing/instrumentation/databases/pg.js b/packages/core/src/tracing/instrumentation/databases/pg.js index eed210ad70..b299802775 100644 --- a/packages/core/src/tracing/instrumentation/databases/pg.js +++ b/packages/core/src/tracing/instrumentation/databases/pg.js @@ -85,6 +85,7 @@ function instrumentedQuery(ctx, originalQuery, argsForOriginalQuery) { } const promise = originalQuery.apply(ctx, argsForOriginalQuery); + if (promise && typeof promise.then === 'function') { promise .then(value => { @@ -95,6 +96,9 @@ function instrumentedQuery(ctx, originalQuery, argsForOriginalQuery) { finishSpan(error, span); return error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'pg', 'query'); + finishSpan(null, span); } return promise; }); diff --git a/packages/core/src/tracing/instrumentation/databases/prisma.js b/packages/core/src/tracing/instrumentation/databases/prisma.js index 126b2e6080..7344af34f6 100644 --- a/packages/core/src/tracing/instrumentation/databases/prisma.js +++ b/packages/core/src/tracing/instrumentation/databases/prisma.js @@ -166,11 +166,13 @@ function instrumentedRequest(ctx, originalRequest, argsForOriginalRequest) { provider: providerAndDataSourceUri.provider, url: providerAndDataSourceUri.dataSourceUrl }; + const requestPromise = originalRequest.apply(ctx, argsForOriginalRequest); + if (!requestPromise && typeof requestPromise.then !== 'function') { span.cancel(); return requestPromise; - } else { + } else if (typeof requestPromise?.then === 'function') { return requestPromise .then(value => { finishSpan(null, span); @@ -180,6 +182,10 @@ function instrumentedRequest(ctx, originalRequest, argsForOriginalRequest) { finishSpan(error, span); return error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(requestPromise, span, 'prisma', '_request'); + finishSpan(null, span); + return requestPromise; } }); } diff --git a/packages/core/src/tracing/instrumentation/databases/redis.js b/packages/core/src/tracing/instrumentation/databases/redis.js index 4f88ec8c85..eb831d7c29 100644 --- a/packages/core/src/tracing/instrumentation/databases/redis.js +++ b/packages/core/src/tracing/instrumentation/databases/redis.js @@ -342,11 +342,10 @@ function instrumentCommand(original, command, address, cbStyle) { if (typeof userProvidedCallback !== 'function') { userProvidedCallback = null; modifiedArgs.push(callback); - return original.apply(origCtx, modifiedArgs); } else { modifiedArgs[modifiedArgs.length - 1] = callback; - return original.apply(origCtx, modifiedArgs); } + return original.apply(origCtx, modifiedArgs); } else { const promise = original.apply(origCtx, origArgs); if (typeof promise?.then === 'function') { @@ -360,7 +359,8 @@ function instrumentCommand(original, command, address, cbStyle) { return error; }); } else { - // UNKNOWN CASE + // Case 5: Unsupported/unknown case - use utility function + tracingUtil.handleUnexpectedReturnValue(promise, span, 'redis', `command "${command}"`); onResult(); } return promise; @@ -492,6 +492,10 @@ function instrumentMultiExec(origCtx, origArgs, original, address, isAtomic, cbS onResult(error); return error; }); + } else { + // Case 5: Unsupported/unknown case - use utility function + tracingUtil.handleUnexpectedReturnValue(promise, span, 'redis', 'multi/pipeline operation'); + onResult(); } return promise; diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 17cd2684c3..7b10791fe5 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -410,4 +410,41 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { } catch (err) { logger.error('Failed to set error details on span:', err); } + + /** + * Handles unexpected return values from instrumented functions (Case 5: Unsupported/Bug case). + * Logs a debug message and marks the span as incomplete when the return value is not a promise. + * + * @param {*} returnValue - The return value from the instrumented function + * @param {import('../core').InstanaBaseSpan} targetSpan - The span to mark as incomplete + * @param {string} spanName - The name of the span (e.g., 'redis', 'postgres', 'mysql') + * @param {string} operationContext - Additional context about the operation (e.g., 'query', 'command') + * @returns {boolean} - Returns true if the return value was unexpected (not a promise), false otherwise + */ + exports.handleUnexpectedReturnValue = function handleUnexpectedReturnValue( + returnValue, + targetSpan, + spanName, + operationContext + ) { + if (typeof returnValue?.then === 'function') { + return false; + } + + // Case: This is the unexpected case where returnValue is not a promise + logger.debug( + `${spanName} instrumentation: Unexpected return value from ${operationContext}. ` + + `Expected a promise but got: ${typeof returnValue}. ` + + 'This may indicate an instrumentation bug or unsupported library behavior.' + ); + + // Mark span with incomplete instrumentation info + // This is currently a custom tag, will move to sdk custom tag + if (targetSpan.data && targetSpan.data[spanName]) { + targetSpan.data[spanName].incomplete = true; + targetSpan.data[spanName].incompleteReason = 'unexpected_return_type'; + } + + return true; + }; }; From 6fc09040ea3009dc07cb9cf0090e977972e8c331 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 4 Feb 2026 18:15:29 +0530 Subject: [PATCH 5/9] chore: extended the 5th case --- .../instrumentation/cloud/aws-sdk/v3/dynamodb.js | 2 ++ .../instrumentation/cloud/aws-sdk/v3/sqs-consumer.js | 8 ++++++++ .../src/tracing/instrumentation/cloud/gcp/pubsub.js | 3 +++ .../src/tracing/instrumentation/cloud/gcp/storage.js | 3 +++ .../control_flow/graphqlSubscriptions.js | 7 +++++++ .../tracing/instrumentation/databases/elasticsearch.js | 6 ++++++ .../src/tracing/instrumentation/databases/ioredis.js | 6 ++++++ .../core/src/tracing/instrumentation/frameworks/koa.js | 8 ++++++++ .../core/src/tracing/instrumentation/messaging/amqp.js | 4 ++++ .../core/src/tracing/instrumentation/messaging/bull.js | 9 +++++++++ .../src/tracing/instrumentation/messaging/kafkaJs.js | 10 ++++++++++ .../src/tracing/instrumentation/protocols/graphql.js | 9 +++++++++ .../tracing/instrumentation/protocols/nativeFetch.js | 4 ++++ 13 files changed, 79 insertions(+) diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/dynamodb.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/dynamodb.js index 9c53743cb5..1ca9ab497a 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/dynamodb.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/dynamodb.js @@ -95,6 +95,8 @@ class InstanaAWSDynamoDB extends InstanaAWSProduct { .catch(() => { /* silently ignore failed attempts to get the region */ }); + } else { + tracingUtil.handleUnexpectedReturnValue(regionPromise, span, this.spanName, 'config.region()'); } } } diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js index a5ce0781a2..3e6daa8556 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/sqs-consumer.js @@ -36,6 +36,10 @@ function instrument(SQSConsumer) { span.d = Date.now() - span.ts; span.transmitManual(); }); + } else { + tracingUtil.handleUnexpectedReturnValue(res, span, 'sqs', 'consumer handler'); + span.d = Date.now() - span.ts; + span.transmitManual(); } return res; @@ -70,6 +74,10 @@ function instrument(SQSConsumer) { span.d = Date.now() - span.ts; span.transmitManual(); }); + } else if (res !== undefined) { + tracingUtil.handleUnexpectedReturnValue(res, span, 'sqs', 'consumer batch handler'); + span.d = Date.now() - span.ts; + span.transmitManual(); } return res; diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js index a2adfec562..f3adc42f7f 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js @@ -132,6 +132,9 @@ function instrumentedPublishMessage(ctx, originalPublishMessage, originalArgs) { throw err; } ); + } else { + tracingUtil.handleUnexpectedReturnValue(thenable, span, 'gcps', 'publish message'); + finishSpan(null, null, span); } return thenable; }); diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js index f91612dbe1..9635831bce 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js @@ -421,6 +421,9 @@ function instrumentedOperation(operation, extractorPre, extractorPost, ctx, orig result => finishSpan(null, Array.isArray(result) ? result[0] : result, span, extractorPost), e => finishSpan(e, null, span, extractorPost) ); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'gcs', operation); + finishSpan(null, null, span, extractorPost); } return promise; }); diff --git a/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js b/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js index e7edc1daba..99634c6511 100644 --- a/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js +++ b/packages/core/src/tracing/instrumentation/control_flow/graphqlSubscriptions.js @@ -8,6 +8,7 @@ const shimmer = require('../../shimmer'); const hook = require('../../../util/hook'); +const tracingUtil = require('../../tracingUtil'); const cls = require('../../cls'); let isActive = false; @@ -76,6 +77,12 @@ function shimPullValue(originalFunction) { } return result; }); + } else { + // will the context change ? Maybe check and remove this case + const span = cls.getCurrentSpan(); + if (span) { + tracingUtil.handleUnexpectedReturnValue(pullPromise, span, 'graphql.subscription', 'pull value'); + } } return pullPromise; diff --git a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js index 46a6f2b774..5d64f49db4 100644 --- a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js +++ b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js @@ -161,6 +161,9 @@ function instrumentApi(client, actionPath, clusterInfo) { onError(span, error); throw error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'elasticsearch', `action "${action}"`); + onSuccess(span, {}); } return promise; } catch (e) { @@ -458,6 +461,9 @@ function instrumentedRequest(ctx, origEsReq, originalArgs) { onError(span, error); throw error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'elasticsearch', 'transport request'); + onSuccess(span, {}); } return promise; } catch (e) { diff --git a/packages/core/src/tracing/instrumentation/databases/ioredis.js b/packages/core/src/tracing/instrumentation/databases/ioredis.js index f20dc059b7..acc49ef698 100644 --- a/packages/core/src/tracing/instrumentation/databases/ioredis.js +++ b/packages/core/src/tracing/instrumentation/databases/ioredis.js @@ -103,6 +103,9 @@ function instrumentSendCommand(original) { callback.bind(null, null), callback ); + } else { + tracingUtil.handleUnexpectedReturnValue(command.promise, span, 'redis', `command "${command.name}"`); + callback(null); } return original.apply(client, argsForOriginal); @@ -201,6 +204,9 @@ function instrumentMultiOrPipelineExec(clsContextForMultiOrPipeline, commandName endCallback.call(null, clsContextForMultiOrPipeline, span, error, []); } ); + } else if (result !== undefined) { + tracingUtil.handleUnexpectedReturnValue(result, span, 'redis', `${commandName} exec`); + endCallback.call(null, clsContextForMultiOrPipeline, span, null, []); } return result; }; diff --git a/packages/core/src/tracing/instrumentation/frameworks/koa.js b/packages/core/src/tracing/instrumentation/frameworks/koa.js index a7eed9244b..e00886f6f6 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/koa.js +++ b/packages/core/src/tracing/instrumentation/frameworks/koa.js @@ -8,6 +8,7 @@ const shimmer = require('../../shimmer'); const hook = require('../../../util/hook'); +const tracingUtil = require('../../tracingUtil'); const httpServer = require('../protocols/httpServer'); const cls = require('../../cls'); @@ -71,7 +72,14 @@ function instrumentedRoutes(thisContext, originalRoutes, originalArgs) { } return resolvedValue; }); + } else { + // context same ? check + const span = cls.getCurrentSpan(); + if (span) { + tracingUtil.handleUnexpectedReturnValue(dispatchResult, span, 'http', 'koa router dispatch'); + } } + return dispatchResult; } else { return dispatch.apply(this, arguments); } diff --git a/packages/core/src/tracing/instrumentation/messaging/amqp.js b/packages/core/src/tracing/instrumentation/messaging/amqp.js index 6608c1931c..cc0368f43a 100644 --- a/packages/core/src/tracing/instrumentation/messaging/amqp.js +++ b/packages/core/src/tracing/instrumentation/messaging/amqp.js @@ -302,7 +302,11 @@ function instrumentedChannelModelGet(ctx, originalGet, originalArgs) { }); return result; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'rabbitmq', 'channel.get'); + span.cancel(); } + return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index 39f7cdadc9..3b8b7f17a7 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -110,7 +110,11 @@ function instrumentedJobCreate(ctx, originalJobCreate, originalArgs, options) { finishSpan(err, null, span); return err; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'bull', 'job.create'); + finishSpan(null, null, span); } + return promise; }); } @@ -275,7 +279,12 @@ function instrumentedProcessJob(ctx, originalProcessJob, originalArgs) { delete options.X_INSTANA_L; throw err; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'bull', 'job.process'); + finishSpan(null, null, span); + delete options.X_INSTANA_L; } + return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js index 0a44ce1626..51faafac2a 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js @@ -111,7 +111,12 @@ function instrumentedSend(ctx, originalSend, originalArgs, topic, messages) { span.transmit(); throw error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'kafka', 'producer.send'); + span.d = Date.now() - span.ts; + span.transmit(); } + return promise; }); } @@ -192,7 +197,12 @@ function instrumentedSendBatch(ctx, originalSendBatch, originalArgs, topicMessag span.transmit(); throw error; }); + } else { + tracingUtil.handleUnexpectedReturnValue(promise, span, 'kafka', 'producer.sendBatch'); + span.d = Date.now() - span.ts; + span.transmit(); } + return promise; }); } diff --git a/packages/core/src/tracing/instrumentation/protocols/graphql.js b/packages/core/src/tracing/instrumentation/protocols/graphql.js index 5fcbcef99c..9f421e79dc 100644 --- a/packages/core/src/tracing/instrumentation/protocols/graphql.js +++ b/packages/core/src/tracing/instrumentation/protocols/graphql.js @@ -349,6 +349,15 @@ function shimApolloGatewayExecuteQueryPlanFunction(originalFunction) { throw err; } ); + } else { + tracingUtil.handleUnexpectedReturnValue( + resultPromise, + activeEntrySpan, + 'graphql.execute', + 'Apollo Gateway query plan' + ); + delete activeEntrySpan.postponeTransmitApolloGateway; + finishSpan(activeEntrySpan, {}); } return resultPromise; }; diff --git a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js index d1bf61061c..74ccfcefdf 100644 --- a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js +++ b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js @@ -195,6 +195,10 @@ function instrument() { span.d = Date.now() - span.ts; span.transmit(); }); + } else { + tracingUtil.handleUnexpectedReturnValue(fetchPromise, span, 'http', 'fetch'); + span.d = Date.now() - span.ts; + span.transmit(); } return fetchPromise; From b12c51912570ce5f461513bab4bab9e33394f394 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 5 Feb 2026 10:53:48 +0530 Subject: [PATCH 6/9] chore: test fix --- packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js index f3adc42f7f..36e8472186 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js @@ -132,7 +132,9 @@ function instrumentedPublishMessage(ctx, originalPublishMessage, originalArgs) { throw err; } ); - } else { + } else if (!originalCallback) { + // If there's no callback and no promise, we need to finish the span + // This can happen in some edge cases tracingUtil.handleUnexpectedReturnValue(thenable, span, 'gcps', 'publish message'); finishSpan(null, null, span); } From baaffb5a81061fcd48db9fc30f975a87a5935edc Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 5 Feb 2026 11:18:18 +0530 Subject: [PATCH 7/9] chore: update --- packages/core/src/tracing/tracingUtil.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 7b10791fe5..133f8347a5 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -438,12 +438,18 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { 'This may indicate an instrumentation bug or unsupported library behavior.' ); - // Mark span with incomplete instrumentation info - // This is currently a custom tag, will move to sdk custom tag - if (targetSpan.data && targetSpan.data[spanName]) { - targetSpan.data[spanName].incomplete = true; - targetSpan.data[spanName].incompleteReason = 'unexpected_return_type'; + // using sdk custom tags, we mark this span as incomplete + if (!targetSpan.data.sdk) { + targetSpan.data.sdk = {}; } + if (!targetSpan.data.sdk.custom) { + targetSpan.data.sdk.custom = {}; + } + if (!targetSpan.data.sdk.custom.tags) { + targetSpan.data.sdk.custom.tags = {}; + } + targetSpan.data.sdk.custom.tags.incomplete = true; + targetSpan.data.sdk.custom.tags.incompleteReason = 'unexpected_return_type'; return true; }; From 80d1125df6b8709358bbdc48f88c1c50b959f10f Mon Sep 17 00:00:00 2001 From: Abhilash <70062455+abhilash-sivan@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:36:58 +0530 Subject: [PATCH 8/9] chore: apply suggestion from @abhilash-sivan --- packages/core/src/tracing/tracingUtil.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 133f8347a5..29cd8fe5f9 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -413,7 +413,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { /** * Handles unexpected return values from instrumented functions (Case 5: Unsupported/Bug case). - * Logs a debug message and marks the span as incomplete when the return value is not a promise. + * Logs a debug message and marks the span as incomplete when the return value is unsupported * * @param {*} returnValue - The return value from the instrumented function * @param {import('../core').InstanaBaseSpan} targetSpan - The span to mark as incomplete From 62893b522c1801c8a077243b2f2001ae09524422 Mon Sep 17 00:00:00 2001 From: Abhilash <70062455+abhilash-sivan@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:37:15 +0530 Subject: [PATCH 9/9] chore: apply suggestion from @abhilash-sivan --- packages/core/src/tracing/tracingUtil.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 29cd8fe5f9..1c9565444f 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -412,7 +412,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { } /** - * Handles unexpected return values from instrumented functions (Case 5: Unsupported/Bug case). + * Handles unexpected return values from instrumented functions * Logs a debug message and marks the span as incomplete when the return value is unsupported * * @param {*} returnValue - The return value from the instrumented function