From d8f50c01f11bd9907bfd2947ac6fb7e41cfc862d Mon Sep 17 00:00:00 2001 From: Abhilash Date: Fri, 21 Nov 2025 11:59:57 +0530 Subject: [PATCH 01/39] feat: added error stack replacement logic - 1 --- .../test/tracing/databases/mysql/app.js | 53 +++++++++++++++++++ .../test/tracing/databases/mysql/test.js | 49 +++++++++++++++++ .../test/tracing/databases/pg_native/test.js | 46 ++++++++++++++++ .../test/tracing/misc/stack_trace/test.js | 9 +++- .../protocols/http/client/clientApp.js | 38 +++++++++++++ .../protocols/http/client/errorHelper.js | 23 ++++++++ .../protocols/http/client/serverApp.js | 4 ++ .../tracing/protocols/http/client/test.js | 30 +++++++++++ .../instrumentation/databases/mysql.js | 6 ++- .../instrumentation/databases/pgNative.js | 1 + .../instrumentation/protocols/httpClient.js | 11 ++++ packages/core/src/tracing/tracingUtil.js | 31 +++++++++++ 12 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 packages/collector/test/tracing/protocols/http/client/errorHelper.js diff --git a/packages/collector/test/tracing/databases/mysql/app.js b/packages/collector/test/tracing/databases/mysql/app.js index 05d8ce333a..12fa8d9fd9 100644 --- a/packages/collector/test/tracing/databases/mysql/app.js +++ b/packages/collector/test/tracing/databases/mysql/app.js @@ -200,6 +200,14 @@ app.post('/valuesAndCall', (req, res) => { } }); +app.post('/error', (req, res) => { + if (driver === 'mysql2/promise') { + triggerErrorWithPromises(req, res); + } else { + triggerError(req, res); + } +}); + app.listen(port, () => { log( `Listening on port: ${process.env.APP_PORT} (driver: ${driver}, access: ${accessFunction}, cluster: ${useCluster})` @@ -305,6 +313,51 @@ function insertValuesWithPromisesAndCall(req, res) { }); } +function triggerError(req, res) { + pool.getConnection((err, connection) => { + if (err) { + log('Failed to get connection', err); + res.sendStatus(500); + return; + } + + // Execute an invalid SQL query to trigger an error + connection[accessFunction]('SELECT * FROM non_existent_table', queryError => { + connection.release(); + + if (queryError) { + log('Expected error occurred', queryError); + res.sendStatus(500); + return; + } + + res.sendStatus(200); + }); + }); +} + +function triggerErrorWithPromises(req, res) { + pool + .getConnection() + .then(connection => { + // Execute an invalid SQL query to trigger an error + wrapAccess(connection, 'SELECT * FROM non_existent_table', null, queryError => { + connection.release(); + + if (queryError) { + log('Expected error occurred', queryError); + res.sendStatus(500); + } else { + res.sendStatus(200); + } + }); + }) + .catch(err => { + log('Failed to get connection', err); + res.sendStatus(500); + }); +} + function log() { const args = Array.prototype.slice.call(arguments); args[0] = logPrefix + args[0]; diff --git a/packages/collector/test/tracing/databases/mysql/test.js b/packages/collector/test/tracing/databases/mysql/test.js index 0b0c4f2f69..de0ac70b18 100644 --- a/packages/collector/test/tracing/databases/mysql/test.js +++ b/packages/collector/test/tracing/databases/mysql/test.js @@ -320,4 +320,53 @@ function test(env, agentControls) { }) ); })); + + it('must replace stack trace with error stack when query fails', () => + controls + .sendRequest({ + method: 'POST', + path: '/error' + }) + .then(() => + testUtils.retry(() => + agentControls.getSpans().then(spans => { + expect(spans.length).to.equal(2); + const entrySpan = testUtils.expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid') + ]); + + const mysqlSpan = testUtils.expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(entrySpan.t), + span => expect(span.p).to.equal(entrySpan.s), + span => expect(span.n).to.equal('mysql'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.ec).to.equal(1), + span => expect(span.data.mysql.error).to.exist + ]); + + expect(mysqlSpan.stack).to.be.an('array'); + expect(mysqlSpan.stack.length).to.be.greaterThan(0); + + mysqlSpan.stack.forEach(frame => { + expect(frame).to.have.property('m'); + expect(frame).to.have.property('c'); + expect(frame.m).to.be.a('string'); + expect(frame.c).to.be.a('string'); + }); + + const hasRelevantFrame = mysqlSpan.stack.some( + frame => + frame.c.includes('app.js') || + frame.c.includes('mysql') || + frame.m.includes('query') || + frame.m.includes('Query') + ); + expect(hasRelevantFrame).to.be.true; + }) + ) + )); } diff --git a/packages/collector/test/tracing/databases/pg_native/test.js b/packages/collector/test/tracing/databases/pg_native/test.js index b3cf30b6ae..3c2831db6d 100644 --- a/packages/collector/test/tracing/databases/pg_native/test.js +++ b/packages/collector/test/tracing/databases/pg_native/test.js @@ -189,6 +189,52 @@ mochaSuiteFn('tracing/pg-native', function () { ); })); + it('must replace span stack with error stack when error occurs', () => + controls + .sendRequest({ + method: 'POST', + path: '/error', + simple: false + }) + .then(response => { + expect(response).to.exist; + expect(response.error).to.contain('Error: ERROR:'); + expect(response.error).to.contain('relation "nonexistanttable" does not exist'); + + return retry(() => + agentControls.getSpans().then(spans => { + const httpEntry = verifyHttpEntry(spans, '/error'); + const pgExit = expectAtLeastOneMatching(spans, span => { + verifyPgExitBase(span, httpEntry, 'SELECT name, email FROM nonexistanttable'); + expect(span.error).to.not.exist; + expect(span.ec).to.equal(1); + expect(span.data.pg.error).to.contain('relation "nonexistanttable" does not exist'); + }); + + expect(pgExit.stack).to.be.an('array'); + expect(pgExit.stack.length).to.be.greaterThan(0); + + pgExit.stack.forEach(frame => { + expect(frame).to.have.property('m'); + expect(frame).to.have.property('c'); + expect(frame.m).to.be.a('string'); + expect(frame.c).to.be.a('string'); + }); + + const hasErrorFrame = pgExit.stack.some( + frame => + frame.c.includes('pg-native') || + frame.c.includes('app.js') || + frame.m.includes('query') || + frame.m.includes('Query') + ); + expect(hasErrorFrame).to.be.true; + + verifyHttpExit(spans, httpEntry); + }) + ); + })); + it('must suppress', () => controls .sendRequest({ diff --git a/packages/collector/test/tracing/misc/stack_trace/test.js b/packages/collector/test/tracing/misc/stack_trace/test.js index 26ca4db039..d3c9c480f2 100644 --- a/packages/collector/test/tracing/misc/stack_trace/test.js +++ b/packages/collector/test/tracing/misc/stack_trace/test.js @@ -89,6 +89,10 @@ const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : descri await expressProxyControls.stop(); }); + beforeEach(async () => { + await agentControls.clearReceivedTraceData(); + }); + beforeEach(async () => { await agentControls.waitUntilAppIsCompletelyInitialized(expressControls.getPid()); }); @@ -107,7 +111,7 @@ const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : descri .then(() => testUtils.retry(() => agentControls.getSpans().then(spans => { - expect(spans.length).to.equal(6); + expect(spans.length).to.equal(3); testUtils.expectAtLeastOneMatching(spans, [ span => expect(span.n).to.equal('node.http.server'), span => expect(span.stack).to.have.lengthOf(0) @@ -126,10 +130,11 @@ const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : descri .then(() => testUtils.retry(() => agentControls.getSpans().then(spans => { - expect(spans.length).to.equal(9); + expect(spans.length).to.equal(3); testUtils.expectAtLeastOneMatching(spans, [ span => expect(span.n).to.equal('node.http.client'), span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.data.http.status).to.equal(201), span => expect(span.stack[2].m).to.equal('fetch'), span => expect(span.stack[2].c).to.contains('node-fetch') ]); diff --git a/packages/collector/test/tracing/protocols/http/client/clientApp.js b/packages/collector/test/tracing/protocols/http/client/clientApp.js index bb7127cdb9..f0bd42f1d7 100644 --- a/packages/collector/test/tracing/protocols/http/client/clientApp.js +++ b/packages/collector/test/tracing/protocols/http/client/clientApp.js @@ -29,6 +29,7 @@ const baseUrl = `${protocol}://user:password@localhost:${process.env.SERVER_PORT const sslDir = path.join(__dirname, '..', '..', '..', '..', 'apps', 'ssl'); const key = fs.readFileSync(path.join(sslDir, 'key')); const cert = fs.readFileSync(path.join(sslDir, 'cert')); +const { handleErrorResponse } = require('./errorHelper'); const app = express(); @@ -376,6 +377,43 @@ app.get('/without-port', (req, res) => { request.end(); }); +app.get('/trigger-error', (req, res) => { + const options = { + hostname: 'localhost', + port: process.env.SERVER_PORT, + method: 'GET', + path: '/error-endpoint', + ca: cert + }; + + log('Initiating call to error endpoint'); + + const request = httpModule.request(options, response => { + let data = ''; + response.on('data', chunk => { + data += chunk; + }); + response.on('end', () => { + log(`Error endpoint responded with status ${response.statusCode}`); + try { + handleErrorResponse(response.statusCode, data); + res.status(response.statusCode).json({ message: 'Error endpoint called', body: data }); + } catch (error) { + log('Caught error from handleErrorResponse:', error.message); + + res.status(500).json({ error: error.message, stack: error.stack }); + } + }); + }); + + request.on('error', err => { + log('Error in downstream call:', err.message); + res.sendStatus(500); + }); + + request.end(); +}); + function createUrl(req, urlPath) { const pathWithQuery = req.query.withQuery ? `${urlPath}?q1=some&pass=verysecret&q2=value` : urlPath; return req.query.urlObject ? new URL(pathWithQuery, baseUrl) : baseUrl + pathWithQuery; diff --git a/packages/collector/test/tracing/protocols/http/client/errorHelper.js b/packages/collector/test/tracing/protocols/http/client/errorHelper.js new file mode 100644 index 0000000000..a2c8ee2eb4 --- /dev/null +++ b/packages/collector/test/tracing/protocols/http/client/errorHelper.js @@ -0,0 +1,23 @@ +/* + * (c) Copyright IBM Corp. 2025 + */ + +'use strict'; + +/** + * Helper function that throws an error when a 500 status is received. + * This is used to test error stack replacement in HTTP client instrumentation. + */ +function handleErrorResponse(statusCode, responseBody) { + if (statusCode >= 500) { + const error = new Error(`Server error: ${statusCode}`); + error.statusCode = statusCode; + error.responseBody = responseBody; + throw error; + } + return { statusCode, responseBody }; +} + +module.exports = { + handleErrorResponse +}; diff --git a/packages/collector/test/tracing/protocols/http/client/serverApp.js b/packages/collector/test/tracing/protocols/http/client/serverApp.js index df186c9974..9e19ad9dbf 100644 --- a/packages/collector/test/tracing/protocols/http/client/serverApp.js +++ b/packages/collector/test/tracing/protocols/http/client/serverApp.js @@ -67,6 +67,10 @@ app.put('/continue', (req, res) => { res.json({ response: 'yada yada yada' }); }); +app.get('/error-endpoint', (req, res) => { + res.status(500).json({ error: 'Internal Server Error' }); +}); + if (process.env.APP_USES_HTTPS === 'true') { const sslDir = path.join(__dirname, '..', '..', '..', '..', 'apps', 'ssl'); require('https') diff --git a/packages/collector/test/tracing/protocols/http/client/test.js b/packages/collector/test/tracing/protocols/http/client/test.js index 7bd5ad978b..a741f4a740 100644 --- a/packages/collector/test/tracing/protocols/http/client/test.js +++ b/packages/collector/test/tracing/protocols/http/client/test.js @@ -979,6 +979,36 @@ function registerTests(appUsesHttps) { }) ) )); + + it('must replace span stack with error stack when response status >= 500', () => + clientControls + .sendRequest({ + method: 'GET', + path: '/trigger-error', + simple: false + }) + .then(() => + retry(() => + globalAgent.instance.getSpans().then(spans => { + const httpClientSpan = expectExactlyOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.client'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.ec).to.equal(1), + span => expect(span.data.http.status).to.equal(500) + ]); + + expect(httpClientSpan.stack).to.be.an('array'); + expect(httpClientSpan.stack.length).to.be.greaterThan(0); + + httpClientSpan.stack.forEach(frame => { + expect(frame).to.have.property('m'); + expect(frame).to.have.property('c'); + expect(frame.m).to.be.a('string'); + expect(frame.c).to.be.a('string'); + }); + }) + ) + )); } function registerConnectionRefusalTest(appUsesHttps) { diff --git a/packages/core/src/tracing/instrumentation/databases/mysql.js b/packages/core/src/tracing/instrumentation/databases/mysql.js index afee7877bc..274b790f0d 100644 --- a/packages/core/src/tracing/instrumentation/databases/mysql.js +++ b/packages/core/src/tracing/instrumentation/databases/mysql.js @@ -190,7 +190,8 @@ function instrumentedAccessFunction( }) .catch(error => { span.ec = 1; - span.data.mysql.error = tracingUtil.getErrorDetails(error); + span.data.mysql.error = error.stack; + tracingUtil.setErrorStack(span, error); span.d = Date.now() - span.ts; span.transmit(); @@ -205,7 +206,8 @@ function instrumentedAccessFunction( function onResult(error) { if (error) { span.ec = 1; - span.data.mysql.error = tracingUtil.getErrorDetails(error); + span.data.mysql.error = error.stack; + tracingUtil.setErrorStack(span, error); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/pgNative.js b/packages/core/src/tracing/instrumentation/databases/pgNative.js index ec75bde81e..d641ea8a6a 100644 --- a/packages/core/src/tracing/instrumentation/databases/pgNative.js +++ b/packages/core/src/tracing/instrumentation/databases/pgNative.js @@ -198,6 +198,7 @@ function finishSpan(error, span) { if (error) { span.ec = 1; span.data.pg.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(error); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 28909342a9..774c234dba 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -242,6 +242,13 @@ function instrument(coreModule, forceHttps) { span.d = Date.now() - span.ts; span.ec = res.statusCode >= 500 ? 1 : 0; + + // Currently we don't parse anything from the body, and its not advised too + // so do not override here, only override on the event of an error catch + // if (span.ec === 1) { + // tracingUtil.setErrorStack(span, res.body.stack); + // } + span.transmit(); if (callback) { @@ -267,6 +274,8 @@ function instrument(coreModule, forceHttps) { span.data.http.error = e ? e.message : ''; span.d = Date.now() - span.ts; span.ec = 1; + tracingUtil.setErrorStack(span, e); + span.transmit(); throw e; } @@ -315,6 +324,8 @@ function instrument(coreModule, forceHttps) { span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1; + tracingUtil.setErrorStack(span, err); + span.transmit(); }); }); diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 9e0768130b..1569f5c5b3 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -276,3 +276,34 @@ exports.findCallback = (/** @type {string | any[]} */ originalArgs) => { callbackIndex }; }; + +/** + * @param {import('../core').InstanaBaseSpan} span - The span to update + * @param {Error | { stack?: string }} error - The error object or error-like object with + * stack property + * @returns {boolean} - Returns true if the stack was set, false otherwise + */ +exports.setErrorStack = function setErrorStack(span, error) { + if (!span || !error) { + return false; + } + + const maxLen = stackTraceLength; + + if (typeof error === 'object') { + /** + * @type {string | any[]} + */ + const errorStack = error.stack || []; + + if (typeof errorStack === 'string') { + return false; + } else if (Array.isArray(errorStack)) { + span.stack = errorStack.slice(0, maxLen); + + return true; + } + } + + return false; +}; From c3a24a17dfb3a47f8137d345595ae3908712d00d Mon Sep 17 00:00:00 2001 From: Abhilash Date: Fri, 21 Nov 2025 14:08:55 +0530 Subject: [PATCH 02/39] chore: update --- .../instrumentation/protocols/httpClient.js | 1 + packages/core/src/tracing/tracingUtil.js | 24 ++++--------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 774c234dba..d7e0d2e6b6 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -243,6 +243,7 @@ function instrument(coreModule, forceHttps) { span.d = Date.now() - span.ts; span.ec = res.statusCode >= 500 ? 1 : 0; + // Internal: Remove/update before RFR // Currently we don't parse anything from the body, and its not advised too // so do not override here, only override on the event of an error catch // if (span.ec === 1) { diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 1569f5c5b3..990ec10d0d 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -281,29 +281,13 @@ exports.findCallback = (/** @type {string | any[]} */ originalArgs) => { * @param {import('../core').InstanaBaseSpan} span - The span to update * @param {Error | { stack?: string }} error - The error object or error-like object with * stack property - * @returns {boolean} - Returns true if the stack was set, false otherwise */ exports.setErrorStack = function setErrorStack(span, error) { - if (!span || !error) { - return false; - } - - const maxLen = stackTraceLength; - - if (typeof error === 'object') { - /** - * @type {string | any[]} - */ - const errorStack = error.stack || []; + if (error && typeof error === 'object') { + const errorStack = error.stack; - if (typeof errorStack === 'string') { - return false; - } else if (Array.isArray(errorStack)) { - span.stack = errorStack.slice(0, maxLen); - - return true; + if (Array.isArray(errorStack)) { + span.stack = errorStack.slice(0, stackTraceLength); } } - - return false; }; From a2c90204f3207066b7e40896bceadf8684171278 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Fri, 21 Nov 2025 17:45:30 +0530 Subject: [PATCH 03/39] refactor: reused the setErrorStack for span.stack and spa.data.technology.stack --- .../instrumentation/databases/mysql.js | 6 ++--- .../instrumentation/databases/pgNative.js | 2 +- .../instrumentation/protocols/httpClient.js | 4 ++-- packages/core/src/tracing/tracingUtil.js | 24 ++++++++++++------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/databases/mysql.js b/packages/core/src/tracing/instrumentation/databases/mysql.js index 274b790f0d..823d1085aa 100644 --- a/packages/core/src/tracing/instrumentation/databases/mysql.js +++ b/packages/core/src/tracing/instrumentation/databases/mysql.js @@ -190,8 +190,7 @@ function instrumentedAccessFunction( }) .catch(error => { span.ec = 1; - span.data.mysql.error = error.stack; - tracingUtil.setErrorStack(span, error); + tracingUtil.setErrorStack(span, error, 'mysql'); span.d = Date.now() - span.ts; span.transmit(); @@ -206,8 +205,7 @@ function instrumentedAccessFunction( function onResult(error) { if (error) { span.ec = 1; - span.data.mysql.error = error.stack; - tracingUtil.setErrorStack(span, error); + tracingUtil.setErrorStack(span, error, 'mysql'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/pgNative.js b/packages/core/src/tracing/instrumentation/databases/pgNative.js index d641ea8a6a..b3ce9292c4 100644 --- a/packages/core/src/tracing/instrumentation/databases/pgNative.js +++ b/packages/core/src/tracing/instrumentation/databases/pgNative.js @@ -198,7 +198,7 @@ function finishSpan(error, span) { if (error) { span.ec = 1; span.data.pg.error = tracingUtil.getErrorDetails(error); - tracingUtil.setErrorStack(error); + tracingUtil.setErrorStack(span, error, 'pg'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index d7e0d2e6b6..1160c01172 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -275,7 +275,7 @@ function instrument(coreModule, forceHttps) { span.data.http.error = e ? e.message : ''; span.d = Date.now() - span.ts; span.ec = 1; - tracingUtil.setErrorStack(span, e); + tracingUtil.setErrorStack(span, e, 'http'); span.transmit(); throw e; @@ -325,7 +325,7 @@ function instrument(coreModule, forceHttps) { span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1; - tracingUtil.setErrorStack(span, err); + tracingUtil.setErrorStack(span, err, 'http'); span.transmit(); }); diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 990ec10d0d..df84ca2845 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -279,15 +279,23 @@ exports.findCallback = (/** @type {string | any[]} */ originalArgs) => { /** * @param {import('../core').InstanaBaseSpan} span - The span to update - * @param {Error | { stack?: string }} error - The error object or error-like object with - * stack property + * @param {Error} error + * @param {string} technology - The technology name (e.g., 'mysql', 'pg', 'http') */ -exports.setErrorStack = function setErrorStack(span, error) { - if (error && typeof error === 'object') { - const errorStack = error.stack; +// @ts-ignore +exports.setErrorStack = function setErrorStack(span, error, technology) { + if (error == null) { + return undefined; + } - if (Array.isArray(errorStack)) { - span.stack = errorStack.slice(0, stackTraceLength); - } + if (technology && span.data[technology]) { + // for some cases like http, it is already set with custom values and no need to overwrite the message + span.data[technology].error = span.data[technology].error || exports.getErrorDetails(error); + } + + if (error && error.stack) { + // no need to consider length for error cases, we can send the whole stack trace as per design + // TODO: It will be recorded as string, revisit to change structure + span.stack = error.stack; } }; From 3aee1ef0d32a1d7f2292a2a2afbcc43b439dc7ab Mon Sep 17 00:00:00 2001 From: Abhilash Date: Fri, 21 Nov 2025 18:21:29 +0530 Subject: [PATCH 04/39] chore: avoid hardcoding and use span name --- packages/core/src/tracing/instrumentation/databases/mysql.js | 4 ++-- .../core/src/tracing/instrumentation/databases/pgNative.js | 3 +-- .../core/src/tracing/instrumentation/protocols/httpClient.js | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/databases/mysql.js b/packages/core/src/tracing/instrumentation/databases/mysql.js index 823d1085aa..f9aa120ac8 100644 --- a/packages/core/src/tracing/instrumentation/databases/mysql.js +++ b/packages/core/src/tracing/instrumentation/databases/mysql.js @@ -190,7 +190,7 @@ function instrumentedAccessFunction( }) .catch(error => { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'mysql'); + tracingUtil.setErrorStack(span, error, exports.spanName); span.d = Date.now() - span.ts; span.transmit(); @@ -205,7 +205,7 @@ function instrumentedAccessFunction( function onResult(error) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'mysql'); + tracingUtil.setErrorStack(span, error, exports.spanName); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/pgNative.js b/packages/core/src/tracing/instrumentation/databases/pgNative.js index b3ce9292c4..724054d71c 100644 --- a/packages/core/src/tracing/instrumentation/databases/pgNative.js +++ b/packages/core/src/tracing/instrumentation/databases/pgNative.js @@ -197,8 +197,7 @@ function startSpanBeforeSync(ctx, originalFn, originalArgs, statement, stackTrac function finishSpan(error, span) { if (error) { span.ec = 1; - span.data.pg.error = tracingUtil.getErrorDetails(error); - tracingUtil.setErrorStack(span, error, 'pg'); + tracingUtil.setErrorStack(span, error, exports.spanName); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 1160c01172..8e7e4eb993 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -275,7 +275,7 @@ function instrument(coreModule, forceHttps) { span.data.http.error = e ? e.message : ''; span.d = Date.now() - span.ts; span.ec = 1; - tracingUtil.setErrorStack(span, e, 'http'); + tracingUtil.setErrorStack(span, e, exports.spanName); span.transmit(); throw e; @@ -325,7 +325,7 @@ function instrument(coreModule, forceHttps) { span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1; - tracingUtil.setErrorStack(span, err, 'http'); + tracingUtil.setErrorStack(span, err, exports.spanName); span.transmit(); }); From e1381648097bb5f98fde68b5e0e0bf2471c1aca0 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Sun, 23 Nov 2025 22:06:40 +0530 Subject: [PATCH 05/39] test: corrected test for stack replacement --- .../test/tracing/databases/mysql/test.js | 36 +++++++++--------- .../test/tracing/databases/pg_native/test.js | 37 +++++++++---------- .../tracing/protocols/http/client/test.js | 33 +---------------- 3 files changed, 38 insertions(+), 68 deletions(-) diff --git a/packages/collector/test/tracing/databases/mysql/test.js b/packages/collector/test/tracing/databases/mysql/test.js index de0ac70b18..f53ce17855 100644 --- a/packages/collector/test/tracing/databases/mysql/test.js +++ b/packages/collector/test/tracing/databases/mysql/test.js @@ -348,24 +348,24 @@ function test(env, agentControls) { span => expect(span.data.mysql.error).to.exist ]); - expect(mysqlSpan.stack).to.be.an('array'); - expect(mysqlSpan.stack.length).to.be.greaterThan(0); - - mysqlSpan.stack.forEach(frame => { - expect(frame).to.have.property('m'); - expect(frame).to.have.property('c'); - expect(frame.m).to.be.a('string'); - expect(frame.c).to.be.a('string'); - }); - - const hasRelevantFrame = mysqlSpan.stack.some( - frame => - frame.c.includes('app.js') || - frame.c.includes('mysql') || - frame.m.includes('query') || - frame.m.includes('Query') - ); - expect(hasRelevantFrame).to.be.true; + expect(mysqlSpan.stack).to.be.a('string'); + // expect(mysqlSpan.stack.length).to.be.greaterThan(0); + + // mysqlSpan.stack.forEach(frame => { + // expect(frame).to.have.property('m'); + // expect(frame).to.have.property('c'); + // expect(frame.m).to.be.a('string'); + // expect(frame.c).to.be.a('string'); + // }); + + // const hasRelevantFrame = mysqlSpan.stack.some( + // frame => + // frame.c.includes('app.js') || + // frame.c.includes('mysql') || + // frame.m.includes('query') || + // frame.m.includes('Query') + // ); + // expect(hasRelevantFrame).to.be.true; }) ) )); diff --git a/packages/collector/test/tracing/databases/pg_native/test.js b/packages/collector/test/tracing/databases/pg_native/test.js index 3c2831db6d..06805f3990 100644 --- a/packages/collector/test/tracing/databases/pg_native/test.js +++ b/packages/collector/test/tracing/databases/pg_native/test.js @@ -208,27 +208,26 @@ mochaSuiteFn('tracing/pg-native', function () { verifyPgExitBase(span, httpEntry, 'SELECT name, email FROM nonexistanttable'); expect(span.error).to.not.exist; expect(span.ec).to.equal(1); - expect(span.data.pg.error).to.contain('relation "nonexistanttable" does not exist'); }); - expect(pgExit.stack).to.be.an('array'); - expect(pgExit.stack.length).to.be.greaterThan(0); - - pgExit.stack.forEach(frame => { - expect(frame).to.have.property('m'); - expect(frame).to.have.property('c'); - expect(frame.m).to.be.a('string'); - expect(frame.c).to.be.a('string'); - }); - - const hasErrorFrame = pgExit.stack.some( - frame => - frame.c.includes('pg-native') || - frame.c.includes('app.js') || - frame.m.includes('query') || - frame.m.includes('Query') - ); - expect(hasErrorFrame).to.be.true; + expect(pgExit.stack).to.be.a('string'); + // expect(pgExit.stack.length).to.be.greaterThan(0); + + // pgExit.stack.forEach(frame => { + // expect(frame).to.have.property('m'); + // expect(frame).to.have.property('c'); + // expect(frame.m).to.be.a('string'); + // expect(frame.c).to.be.a('string'); + // }); + + // const hasErrorFrame = pgExit.stack.some( + // frame => + // frame.c.includes('pg-native') || + // frame.c.includes('app.js') || + // frame.m.includes('query') || + // frame.m.includes('Query') + // ); + // expect(hasErrorFrame).to.be.true; verifyHttpExit(spans, httpEntry); }) diff --git a/packages/collector/test/tracing/protocols/http/client/test.js b/packages/collector/test/tracing/protocols/http/client/test.js index a741f4a740..1ba90a4a47 100644 --- a/packages/collector/test/tracing/protocols/http/client/test.js +++ b/packages/collector/test/tracing/protocols/http/client/test.js @@ -633,7 +633,8 @@ function registerTests(appUsesHttps) { expect(span.data.http.error).to.match(/Invalid URL/); }, span => expect(span.t).to.equal(entrySpan.t), - span => expect(span.p).to.equal(entrySpan.s) + span => expect(span.p).to.equal(entrySpan.s), + span => expect(span.stack).to.be.a('string') ]); expectExactlyOneMatching(spans, span => { expect(span.n).to.equal('node.http.client'); @@ -979,36 +980,6 @@ function registerTests(appUsesHttps) { }) ) )); - - it('must replace span stack with error stack when response status >= 500', () => - clientControls - .sendRequest({ - method: 'GET', - path: '/trigger-error', - simple: false - }) - .then(() => - retry(() => - globalAgent.instance.getSpans().then(spans => { - const httpClientSpan = expectExactlyOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.client'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.ec).to.equal(1), - span => expect(span.data.http.status).to.equal(500) - ]); - - expect(httpClientSpan.stack).to.be.an('array'); - expect(httpClientSpan.stack.length).to.be.greaterThan(0); - - httpClientSpan.stack.forEach(frame => { - expect(frame).to.have.property('m'); - expect(frame).to.have.property('c'); - expect(frame.m).to.be.a('string'); - expect(frame.c).to.be.a('string'); - }); - }) - ) - )); } function registerConnectionRefusalTest(appUsesHttps) { From c239c97c6c63e41d1b91bdb61180fc25c26a50b8 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Mon, 24 Nov 2025 11:11:00 +0530 Subject: [PATCH 06/39] chore: update pg instr --- .../core/src/tracing/instrumentation/databases/pgNative.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/instrumentation/databases/pgNative.js b/packages/core/src/tracing/instrumentation/databases/pgNative.js index 724054d71c..e102805254 100644 --- a/packages/core/src/tracing/instrumentation/databases/pgNative.js +++ b/packages/core/src/tracing/instrumentation/databases/pgNative.js @@ -197,7 +197,10 @@ function startSpanBeforeSync(ctx, originalFn, originalArgs, statement, stackTrac function finishSpan(error, span) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, exports.spanName); + // Note: Instead of 'pg', we could've passed exports.spanName if they were the same, + // We can’t use spanName here because for this instr the span name is + // "postgres", but the data is stored under span.data.pg. + tracingUtil.setErrorStack(span, error, 'pg'); } span.d = Date.now() - span.ts; From bff53bc07ab839b0b0ec768230194709b3b97dd7 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 12:33:31 +0530 Subject: [PATCH 07/39] chore: update tracingutil --- .../instrumentation/protocols/httpClient.js | 6 +++--- packages/core/src/tracing/tracingUtil.js | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 8e7e4eb993..f90f7d07fb 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -272,11 +272,9 @@ function instrument(coreModule, forceHttps) { // example is a case that triggers a synchronous exception. span.data.http = {}; span.data.http.url = completeCallUrl; - span.data.http.error = e ? e.message : ''; + tracingUtil.setErrorStack(span, e, exports.spanName); span.d = Date.now() - span.ts; span.ec = 1; - tracingUtil.setErrorStack(span, e, exports.spanName); - span.transmit(); throw e; } @@ -322,6 +320,8 @@ function instrument(coreModule, forceHttps) { method: clientRequest.method, url: completeCallUrl }; + // This assignment is intentional + // Even though setErrorStack runs in this codeblock, this custom error message is a special-case behavior span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1; diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index df84ca2845..eab0c3e13e 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -284,18 +284,19 @@ exports.findCallback = (/** @type {string | any[]} */ originalArgs) => { */ // @ts-ignore exports.setErrorStack = function setErrorStack(span, error, technology) { - if (error == null) { - return undefined; + if (!error) { + return; } - if (technology && span.data[technology]) { - // for some cases like http, it is already set with custom values and no need to overwrite the message - span.data[technology].error = span.data[technology].error || exports.getErrorDetails(error); + if (technology && span.data && span.data[technology]) { + // Do not overwrite if instrumentation already set a custom value eg: httpClient + if (!span.data[technology].error) { + span.data[technology].error = exports.getErrorDetails(error); + } } - if (error && error.stack) { - // no need to consider length for error cases, we can send the whole stack trace as per design - // TODO: It will be recorded as string, revisit to change structure + // TODO: Once the configuration change is ready, optionally trim the span.stack to the configured length + if (error.stack) { span.stack = error.stack; } }; From ee4835f797c9e40fe48465cf684d7e207fec6263 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 13:01:37 +0530 Subject: [PATCH 08/39] refactor: update error assignment with setError function --- .../instrumentation/cloud/azure/blob.js | 2 +- .../instrumentation/cloud/gcp/pubsub.js | 6 +--- .../instrumentation/cloud/gcp/storage.js | 2 +- .../instrumentation/databases/couchbase.js | 6 ++-- .../tracing/instrumentation/databases/db2.js | 30 ++++++++++--------- .../databases/elasticsearch.js | 2 +- .../instrumentation/databases/ioredis.js | 8 ++--- .../instrumentation/databases/mongodb.js | 4 +-- .../instrumentation/databases/mssql.js | 2 +- .../tracing/instrumentation/databases/pg.js | 2 +- .../instrumentation/databases/prisma.js | 6 ++-- 11 files changed, 34 insertions(+), 36 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js index 2ecd9f97ab..02bba988a0 100644 --- a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js +++ b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js @@ -105,7 +105,7 @@ function instrumentingOperation({ function finishSpan(error, span) { if (error) { span.ec = 1; - span.data.azstorage.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'azstorage'); } span.d = Date.now() - span.ts; span.transmit(); diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js index 1e4a4f030f..da756b0b34 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js @@ -254,11 +254,7 @@ function finishSpan(err, messageId, span) { function addErrorToSpan(err, span) { if (err) { span.ec = 1; - if (err.message) { - span.data.gcps.error = err.message; - } else if (typeof err === 'string') { - span.data.gcps.error = err; - } + tracingUtil.setErrorStack(span, err, 'gcps'); } } diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js index 733f15b2c5..8eb423d5fa 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js @@ -458,7 +458,7 @@ function instrumentedCreateStream(operation, bindEvent, finalEvent, ctx, origina function finishSpan(error, result, span, extractorPost) { if (error) { span.ec = 1; - span.data.gcs.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'gcs'); } if (extractorPost) { diff --git a/packages/core/src/tracing/instrumentation/databases/couchbase.js b/packages/core/src/tracing/instrumentation/databases/couchbase.js index cb10cd29ff..25005143f5 100644 --- a/packages/core/src/tracing/instrumentation/databases/couchbase.js +++ b/packages/core/src/tracing/instrumentation/databases/couchbase.js @@ -430,7 +430,7 @@ function instrumentTransactions(cluster, connectionStr) { result .catch(err => { span.ec = 1; - span.data.couchbase.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'couchbase'); }) .finally(() => { span.d = Date.now() - span.ts; @@ -497,7 +497,7 @@ function instrumentOperation({ connectionStr, bucketName, getBucketTypeFn, sql, }) .catch(err => { span.ec = 1; - span.data.couchbase.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'couchbase'); }) .finally(() => { span.d = Date.now() - span.ts; @@ -510,7 +510,7 @@ function instrumentOperation({ connectionStr, bucketName, getBucketTypeFn, sql, originalArgs[callbackIndex] = cls.ns.bind(function instanaCallback(err, result) { if (err) { span.ec = 1; - span.data.couchbase.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'couchbase'); } if (resultHandler) { diff --git a/packages/core/src/tracing/instrumentation/databases/db2.js b/packages/core/src/tracing/instrumentation/databases/db2.js index 2b163095bc..2e08be4e91 100644 --- a/packages/core/src/tracing/instrumentation/databases/db2.js +++ b/packages/core/src/tracing/instrumentation/databases/db2.js @@ -222,7 +222,7 @@ function captureFetchError(result, span) { argsFetch[fetchIndex] = function instanaFetchCb(fetchCbErr) { if (fetchCbErr) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(fetchCbErr); + tracingUtil.setErrorStack(span, fetchCbErr, 'db2'); } return fetchCb.apply(this, arguments); @@ -232,7 +232,7 @@ function captureFetchError(result, span) { return originalFn.apply(this, arguments); } catch (caughtErr) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(caughtErr); + tracingUtil.setErrorStack(span, caughtErr, 'db2'); throw caughtErr; } }; @@ -249,12 +249,12 @@ function captureFetchError(result, span) { if (res instanceof Error) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(res); + tracingUtil.setErrorStack(span, res, 'db2'); } return res; } catch (err) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'db2'); return err; } @@ -278,14 +278,14 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn if (result instanceof Error) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(result); + tracingUtil.setErrorStack(span, result, 'db2'); } finishSpan(ctx, result, span); return result; } catch (e) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(e); + tracingUtil.setErrorStack(span, e, 'db2'); finishSpan(ctx, null, span); throw e; } @@ -304,7 +304,7 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn originalArgs[customerCallbackIndex] = function instanaCallback(err) { if (err) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'db2'); } finishSpan(ctx, null, span); @@ -324,7 +324,7 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn }) .catch(err => { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'db2'); finishSpan(ctx, null, span); return err; }); @@ -394,7 +394,7 @@ function instrumentExecuteHelper(ctx, originalArgs, stmtObject, prepareCallParen return result; } catch (err) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'db2'); finishSpan(ctx, null, span); return err; } @@ -441,7 +441,7 @@ function instrumentExecuteHelper(ctx, originalArgs, stmtObject, prepareCallParen args[origCallbackIndex] = function instanaExecuteCallback(executeErr, result) { if (executeErr) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(executeErr); + tracingUtil.setErrorStack(span, executeErr, 'db2'); finishSpan(ctx, null, span); return origCallback.apply(this, arguments); } @@ -470,7 +470,7 @@ function instrumentQueryResultHelper(ctx, originalArgs, originalFunction, stmt, return result; } catch (err) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'db2'); finishSpan(ctx, null, span); return err; } @@ -489,7 +489,7 @@ function instrumentQueryResultHelper(ctx, originalArgs, originalFunction, stmt, originalArgs[customerCallbackIndex] = function instanaCallback(err) { if (err) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'db2'); } const result = customerCallback.apply(this, arguments); @@ -550,6 +550,8 @@ function finishSpan(ctx, result, span) { if (!closeSyncCalled) { closeSyncCalled = true; span.ec = 1; + // there is a manualr erro string assignment and no error object pressent, + // so we will not replace stack.trace from here span.data.db2.error = `'result.closeSync' was not called within ${CLOSE_TIMEOUT_IN_MS}ms.`; span.d = Date.now() - span.ts; span.transmit(); @@ -578,7 +580,7 @@ function handleTransaction(ctx, span) { arguments[1] = function instanaOnEndOverride(onEndErr) { if (onEndErr) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(onEndErr) || 'Error not available.'; + tracingUtil.setErrorStack(span, onEndErr, 'db2'); } span.d = Date.now() - span.ts; @@ -597,7 +599,7 @@ function handleTransaction(ctx, span) { return result; } catch (err) { span.ec = 1; - span.data.db2.error = tracingUtil.getErrorDetails(err) || 'Error not available.'; + tracingUtil.setErrorStack(span, err, 'db2'); span.transmit(); throw err; } diff --git a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js index 5e04f941fd..2004723d8f 100644 --- a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js +++ b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js @@ -200,7 +200,7 @@ function onError(span, error) { span.d = Date.now() - span.ts; span.ec = 1; if (error) { - span.data.elasticsearch.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'elasticsearch'); } if (error.meta && error.meta.meta) { getConnectionDetailsFromResultMeta(span, error.meta); diff --git a/packages/core/src/tracing/instrumentation/databases/ioredis.js b/packages/core/src/tracing/instrumentation/databases/ioredis.js index 8bbb02dea8..ac06b3c98b 100644 --- a/packages/core/src/tracing/instrumentation/databases/ioredis.js +++ b/packages/core/src/tracing/instrumentation/databases/ioredis.js @@ -115,7 +115,7 @@ function instrumentSendCommand(original) { if (error) { span.ec = 1; - span.data.redis.error = error.message; + tracingUtil.setErrorStack(span, error, 'redis'); } span.transmit(); @@ -221,7 +221,7 @@ function multiCommandEndCallback(clsContextForMultiOrPipeline, span, error) { if (error) { span.ec = commandCount; - span.data.redis.error = error.message; + tracingUtil.setErrorStack(span, error, 'redis'); } span.transmit(); @@ -241,7 +241,7 @@ function pipelineCommandEndCallback(clsContextForMultiOrPipeline, span, error, r if (error) { // ioredis docs mention that this should never be possible, but better be safe than sorry span.ec = commandCount; - span.data.redis.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'redis'); } else { let numberOfErrors = 0; let sampledError; @@ -257,7 +257,7 @@ function pipelineCommandEndCallback(clsContextForMultiOrPipeline, span, error, r if (numberOfErrors > 0) { span.ec = numberOfErrors; - span.data.redis.error = tracingUtil.getErrorDetails(sampledError); + tracingUtil.setErrorStack(span, error, 'redis'); } } diff --git a/packages/core/src/tracing/instrumentation/databases/mongodb.js b/packages/core/src/tracing/instrumentation/databases/mongodb.js index 15ca92270c..9bf531523e 100644 --- a/packages/core/src/tracing/instrumentation/databases/mongodb.js +++ b/packages/core/src/tracing/instrumentation/databases/mongodb.js @@ -438,7 +438,7 @@ function createWrappedCallback(span, originalCallback) { return cls.ns.bind(function (error) { if (error) { span.ec = 1; - span.data.mongo.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'mongo'); } span.d = Date.now() - span.ts; @@ -466,7 +466,7 @@ function handleCallbackOrPromise(ctx, originalArgs, originalFunction, span) { }) .catch(err => { span.ec = 1; - span.data.mongo.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'mongo'); span.d = Date.now() - span.ts; span.transmit(); return err; diff --git a/packages/core/src/tracing/instrumentation/databases/mssql.js b/packages/core/src/tracing/instrumentation/databases/mssql.js index 9fca976a40..02cac7cf35 100644 --- a/packages/core/src/tracing/instrumentation/databases/mssql.js +++ b/packages/core/src/tracing/instrumentation/databases/mssql.js @@ -161,7 +161,7 @@ function shimBeginTransaction(originalFunction) { function finishSpan(error, span) { if (error) { span.ec = 1; - span.data.mssql.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'mssql'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/pg.js b/packages/core/src/tracing/instrumentation/databases/pg.js index d0979c58da..4acd7622cb 100644 --- a/packages/core/src/tracing/instrumentation/databases/pg.js +++ b/packages/core/src/tracing/instrumentation/databases/pg.js @@ -103,7 +103,7 @@ function instrumentedQuery(ctx, originalQuery, argsForOriginalQuery) { function finishSpan(error, span) { if (error) { span.ec = 1; - span.data.pg.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'pg'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/prisma.js b/packages/core/src/tracing/instrumentation/databases/prisma.js index 781ba7f30d..6d65284afa 100644 --- a/packages/core/src/tracing/instrumentation/databases/prisma.js +++ b/packages/core/src/tracing/instrumentation/databases/prisma.js @@ -6,7 +6,7 @@ const shimmer = require('../../shimmer'); const hook = require('../../../util/hook'); -const { getErrorDetails, getStackTrace } = require('../../tracingUtil'); +const tracingUtil = require('../../tracingUtil'); const { EXIT } = require('../../constants'); const cls = require('../../cls'); @@ -140,7 +140,7 @@ function instrumentedRequest(ctx, originalRequest, argsForOriginalRequest) { spanName: 'prisma', kind: EXIT }); - span.stack = getStackTrace(instrumentedRequest, 1); + span.stack = tracingUtil.getStackTrace(instrumentedRequest, 1); const params = argsForOriginalRequest[0] || {}; const providerAndDataSourceUri = ctx._engine ? providerAndDataSourceUriMap.get(ctx._engine) || {} : {}; @@ -221,7 +221,7 @@ function redactPasswordFromMsSQLUrl(url) { function finishSpan(error, span) { if (error) { span.ec = 1; - span.data.prisma.error = getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'prisma'); } span.d = Date.now() - span.ts; From 6339b023343bb6abb8fdedf0da3a05832834709d Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 14:57:19 +0530 Subject: [PATCH 09/39] chore: fix ioredis --- packages/core/src/tracing/instrumentation/databases/ioredis.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tracing/instrumentation/databases/ioredis.js b/packages/core/src/tracing/instrumentation/databases/ioredis.js index ac06b3c98b..6db26aa5cf 100644 --- a/packages/core/src/tracing/instrumentation/databases/ioredis.js +++ b/packages/core/src/tracing/instrumentation/databases/ioredis.js @@ -257,7 +257,7 @@ function pipelineCommandEndCallback(clsContextForMultiOrPipeline, span, error, r if (numberOfErrors > 0) { span.ec = numberOfErrors; - tracingUtil.setErrorStack(span, error, 'redis'); + tracingUtil.setErrorStack(span, sampledError, 'redis'); } } From 1c606b6f3ca09fa5fb7a8d1e97b2212fd8c38ba5 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 15:02:37 +0530 Subject: [PATCH 10/39] chore: fix httpclient --- .../core/src/tracing/instrumentation/protocols/httpClient.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index f90f7d07fb..47c862473f 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -249,6 +249,7 @@ function instrument(coreModule, forceHttps) { // if (span.ec === 1) { // tracingUtil.setErrorStack(span, res.body.stack); // } + // here no Error is found for parsing span.transmit(); @@ -272,7 +273,7 @@ function instrument(coreModule, forceHttps) { // example is a case that triggers a synchronous exception. span.data.http = {}; span.data.http.url = completeCallUrl; - tracingUtil.setErrorStack(span, e, exports.spanName); + tracingUtil.setErrorStack(span, e, 'http'); span.d = Date.now() - span.ts; span.ec = 1; span.transmit(); From 1b187f33ac5d8b4ac1ca7b446772dccd82e2b350 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 17:25:20 +0530 Subject: [PATCH 11/39] chore: updated more instr --- .../tracing/instrumentation/databases/redis.js | 16 ++++------------ .../instrumentation/frameworks/express.js | 2 +- .../tracing/instrumentation/messaging/kafkaJs.js | 4 ++-- .../instrumentation/messaging/kafkaNode.js | 2 +- .../tracing/instrumentation/messaging/rdkafka.js | 6 +++--- .../instrumentation/protocols/nativeFetch.js | 2 +- 6 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/databases/redis.js b/packages/core/src/tracing/instrumentation/databases/redis.js index ddacf5ad57..3eb2aaf044 100644 --- a/packages/core/src/tracing/instrumentation/databases/redis.js +++ b/packages/core/src/tracing/instrumentation/databases/redis.js @@ -365,7 +365,7 @@ function instrumentCommand(original, command, address, cbStyle) { if (error) { span.ec = 1; - span.data.redis.error = tracingUtil.getErrorDetails(error); + tracingUtil.setErrorStack(span, error, 'redis'); } span.transmit(); @@ -502,16 +502,11 @@ function instrumentMultiExec(origCtx, origArgs, original, address, isAtomic, cbS if (err) { span.ec = 1; - if (err.message) { - span.data.redis.error = err.message; - } else if (Array.isArray(err) && err.length) { - span.data.redis.error = err[0].message; - } else { - span.data.redis.error = 'Unknown error'; - } + tracingUtil.setErrorStack(span, err, 'redis'); // v3 = provides sub errors if (err.errors && err.errors.length) { + // TODO: Not updating now as special case span.data.redis.error = err.errors.map(subErr => subErr.message).join('\n'); } } @@ -531,10 +526,7 @@ function buildSubCommandCallback(span, userProvidedCallback) { return function subCommandCallback(err) { if (err) { span.ec++; - - if (!span.data.redis.error) { - span.data.redis.error = tracingUtil.getErrorDetails(err); - } + tracingUtil.setErrorStack(span, err, 'redis'); } if (typeof userProvidedCallback === 'function') { diff --git a/packages/core/src/tracing/instrumentation/frameworks/express.js b/packages/core/src/tracing/instrumentation/frameworks/express.js index 208daa0885..a5990bc31b 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/express.js +++ b/packages/core/src/tracing/instrumentation/frameworks/express.js @@ -116,7 +116,7 @@ function annotateHttpEntrySpanWithError(err) { return; } - span.data.http.error = tracingUtil.getErrorDetails(err); + tracingUtil.setErrorStack(span, err, 'http'); } function shimHandlerRegistration(original) { diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js index 16f44c27e3..767b76eeaa 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js @@ -105,7 +105,7 @@ function instrumentedSend(ctx, originalSend, originalArgs, topic, messages) { }) .catch(error => { span.ec = 1; - span.data.kafka.error = error.message; + tracingUtil.setErrorStack(span, error, 'kafka'); span.d = Date.now() - span.ts; span.transmit(); throw error; @@ -184,7 +184,7 @@ function instrumentedSendBatch(ctx, originalSendBatch, originalArgs, topicMessag }) .catch(error => { span.ec = 1; - span.data.kafka.error = error.message; + tracingUtil.setErrorStack(span, error, 'kafka'); span.d = Date.now() - span.ts; span.transmit(); throw error; diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js b/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js index a243cc3806..b017934d8c 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js @@ -81,7 +81,7 @@ function instrumentedSend(ctx, originalSend, produceRequests, cb) { cls.ns.bind(function onSendCompleted(err) { if (err) { span.ec = 1; - span.data.kafka.error = err.message; + tracingUtil.setErrorStack(span, err, 'kafka'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/messaging/rdkafka.js b/packages/core/src/tracing/instrumentation/messaging/rdkafka.js index d756c6f4f1..2f5f238782 100644 --- a/packages/core/src/tracing/instrumentation/messaging/rdkafka.js +++ b/packages/core/src/tracing/instrumentation/messaging/rdkafka.js @@ -167,7 +167,7 @@ function instrumentedProduce(ctx, originalProduce, originalArgs) { if (err) { span.ec = 1; - span.data.kafka.error = err.message; + tracingUtil.setErrorStack(span, err, 'kafka'); } span.transmit(); @@ -187,7 +187,7 @@ function instrumentedProduce(ctx, originalProduce, originalArgs) { // e.g. cannot send message because format is byte // "Message must be a buffer or null" span.ec = 1; - span.data.kafka.error = error.message; + tracingUtil.setErrorStack(span, error, 'kafka'); if (!deliveryCb) { span.d = Date.now() - span.ts; @@ -305,7 +305,7 @@ function instrumentedConsumerEmit(ctx, originalEmit, originalArgs) { delete messageData.headers; span.ec = 1; - span.data.kafka.error = messageData.message; + tracingUtil.setErrorStack(span, messageData, 'kafka'); } setImmediate(() => { diff --git a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js index 342c217921..0aaf9a1bf8 100644 --- a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js +++ b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js @@ -184,7 +184,7 @@ function instrument() { }) .catch(err => { span.ec = 1; - span.data.http.error = err.message; + tracingUtil.setErrorStack(span, err, 'http'); }) .finally(() => { span.d = Date.now() - span.ts; From 5c15fa70f1e55d7c308efbe2735cffb0de5bd6b1 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 17:31:04 +0530 Subject: [PATCH 12/39] chore: updated all instr, but needs some more new logic for special cases --- .../src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js | 2 +- .../instrumentation/cloud/aws-sdk/v3/sqs-consumer.js | 5 +++-- .../core/src/tracing/instrumentation/messaging/bull.js | 8 +++----- .../core/src/tracing/instrumentation/messaging/nats.js | 1 + .../tracing/instrumentation/messaging/natsStreaming.js | 1 + .../core/src/tracing/instrumentation/protocols/grpcJs.js | 5 +++++ 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js index 0590f65c54..8304e0a533 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js @@ -392,7 +392,7 @@ function finishSpan(err, data, span) { function addErrorToSpan(err, span) { if (err) { span.ec = 1; - span.data.sqs.error = err.message || err.code || JSON.stringify(err); + tracingUtil.setErrorStack(span, err, 'sqs'); } } 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 28c3ce826d..d4333d9d3c 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 @@ -7,6 +7,7 @@ const shimmer = require('../../../../shimmer'); const hook = require('../../../../../util/hook'); const cls = require('../../../../cls'); +const tracingUtil = require('../../../../tracingUtil'); function init() { hook.onModuleLoad('sqs-consumer', instrument); @@ -30,7 +31,7 @@ function instrument(SQSConsumer) { }) .catch(err => { span.ec = 1; - span.data.sqs.error = err.message || err.code || JSON.stringify(err); + tracingUtil.setErrorStack(span, err, 'sqs'); span.d = Date.now() - span.ts; span.transmitManual(); }); @@ -62,7 +63,7 @@ function instrument(SQSConsumer) { }) .catch(err => { span.ec = 1; - span.data.sqs.error = err.message || err.code || JSON.stringify(err); + tracingUtil.setErrorStack(span, err, 'sqs'); span.d = Date.now() - span.ts; span.transmitManual(); }); diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index 21115672a6..09e9e01cfc 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -325,11 +325,9 @@ function finishSpan(err, data, span) { function addErrorToSpan(err, span) { if (err) { span.ec = 1; - if (err.code) { - span.data.bull.error = err.code; - } else if (typeof err === 'string') { - span.data.bull.error = err; - } + + // TODO: need to give priority for err.code? + tracingUtil.setErrorStack(span, err, 'bull'); } } diff --git a/packages/core/src/tracing/instrumentation/messaging/nats.js b/packages/core/src/tracing/instrumentation/messaging/nats.js index 2f9a3e0465..0e10caa626 100644 --- a/packages/core/src/tracing/instrumentation/messaging/nats.js +++ b/packages/core/src/tracing/instrumentation/messaging/nats.js @@ -397,6 +397,7 @@ function addErrorToSpan(err, span) { if (err) { span.ec = 1; + // TODO: Special logic of appending error let errMsg = null; if (err.message) { errMsg = err.message; diff --git a/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js b/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js index cc72ff8777..b844b11b62 100644 --- a/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js +++ b/packages/core/src/tracing/instrumentation/messaging/natsStreaming.js @@ -194,6 +194,7 @@ function addErrorToSpan(err, span) { if (err) { span.ec = 1; + // TODO: special logic of appending error let errMsg = null; if (err.message) { errMsg = err.message; diff --git a/packages/core/src/tracing/instrumentation/protocols/grpcJs.js b/packages/core/src/tracing/instrumentation/protocols/grpcJs.js index 63c720a821..fe4e1f57b8 100644 --- a/packages/core/src/tracing/instrumentation/protocols/grpcJs.js +++ b/packages/core/src/tracing/instrumentation/protocols/grpcJs.js @@ -115,9 +115,11 @@ function modifyArgs(name, originalArgs, span) { // No-op, we do not want to mark cancelled calls as erroneous. } else { span.ec = 1; + // TODO: special case of error message if (errorMessage) { span.data.rpc.error = errorMessage; } + tracingUtil.setErrorStack(span, err, 'rpc'); } } @@ -347,6 +349,7 @@ function createInstrumentedServerHandler(name, type, originalHandler) { if (err.message || err.details) { span.data.rpc.error = err.message || err.details; } + tracingUtil.setErrorStack(span, err, 'rpc'); } span.d = Date.now() - span.ts; span.transmit(); @@ -372,6 +375,7 @@ function createInstrumentedServerHandler(name, type, originalHandler) { if (err.message || err.details) { span.data.rpc.error = err.message || err.details; } + tracingUtil.setErrorStack(span, err, 'rpc'); }); call.on('cancelled', () => { @@ -434,6 +438,7 @@ function instrumentedClientMethod( if (errorMessage) { span.data.rpc.error = errorMessage; } + tracingUtil.setErrorStack(span, err, 'rpc'); } span.transmit(); }); From 04e0e17702ebfd178e768e0d5980b54f89cea810 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 18:08:40 +0530 Subject: [PATCH 13/39] test: fixed native fetch test --- .../collector/test/tracing/protocols/http/native_fetch/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/collector/test/tracing/protocols/http/native_fetch/test.js b/packages/collector/test/tracing/protocols/http/native_fetch/test.js index a6b4748098..0ac4ea03df 100644 --- a/packages/collector/test/tracing/protocols/http/native_fetch/test.js +++ b/packages/collector/test/tracing/protocols/http/native_fetch/test.js @@ -853,7 +853,7 @@ function verifyHttpExit({ expectedClientError = `Failed to parse URL from http:127.0.0.1:${serverControls.port}malformed-url`; } expectations.push(span => expect(span.data.http.status).to.not.exist); - expectations.push(span => expect(span.data.http.error).to.equal(expectedClientError)); + expectations.push(span => expect(span.data.http.error).to.contain(expectedClientError)); } else if (withTimeout) { expectations.push(span => expect(span.data.http.status).to.not.exist); // Early v18.x Node.js versions had "The operation was aborted", the message later changed to From 48513fc48cc842ec1b4f9e913dfbc592068f5fd9 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 18:09:53 +0530 Subject: [PATCH 14/39] test: fix node-rdkafka error --- .../test/tracing/messaging/node-rdkafka/test_definition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js b/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js index 5fef745288..f5ca613a84 100644 --- a/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js +++ b/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js @@ -272,7 +272,7 @@ module.exports.run = function ({ span => expect(span.ec).to.equal(!withError ? 0 : 1), span => (!withError ? expect(span.data.kafka.error).to.not.exist : ''), span => - withError === 'deliveryErrorSender' ? expect(span.data.kafka.error).to.equal('delivery fake error') : '', + withError === 'deliveryErrorSender' ? expect(span.data.kafka.error).to.contain('delivery fake error') : '', span => withError === 'bufferErrorSender' ? expect(span.data.kafka.error).to.equal('Message must be a buffer or null') From fca3b561e61842fcc370512733b2a0a8059dec9b Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 18:22:25 +0530 Subject: [PATCH 15/39] chore: removed unused code --- .../protocols/http/client/clientApp.js | 38 ------------------- .../protocols/http/client/errorHelper.js | 23 ----------- .../protocols/http/client/serverApp.js | 4 -- 3 files changed, 65 deletions(-) delete mode 100644 packages/collector/test/tracing/protocols/http/client/errorHelper.js diff --git a/packages/collector/test/tracing/protocols/http/client/clientApp.js b/packages/collector/test/tracing/protocols/http/client/clientApp.js index f0bd42f1d7..bb7127cdb9 100644 --- a/packages/collector/test/tracing/protocols/http/client/clientApp.js +++ b/packages/collector/test/tracing/protocols/http/client/clientApp.js @@ -29,7 +29,6 @@ const baseUrl = `${protocol}://user:password@localhost:${process.env.SERVER_PORT const sslDir = path.join(__dirname, '..', '..', '..', '..', 'apps', 'ssl'); const key = fs.readFileSync(path.join(sslDir, 'key')); const cert = fs.readFileSync(path.join(sslDir, 'cert')); -const { handleErrorResponse } = require('./errorHelper'); const app = express(); @@ -377,43 +376,6 @@ app.get('/without-port', (req, res) => { request.end(); }); -app.get('/trigger-error', (req, res) => { - const options = { - hostname: 'localhost', - port: process.env.SERVER_PORT, - method: 'GET', - path: '/error-endpoint', - ca: cert - }; - - log('Initiating call to error endpoint'); - - const request = httpModule.request(options, response => { - let data = ''; - response.on('data', chunk => { - data += chunk; - }); - response.on('end', () => { - log(`Error endpoint responded with status ${response.statusCode}`); - try { - handleErrorResponse(response.statusCode, data); - res.status(response.statusCode).json({ message: 'Error endpoint called', body: data }); - } catch (error) { - log('Caught error from handleErrorResponse:', error.message); - - res.status(500).json({ error: error.message, stack: error.stack }); - } - }); - }); - - request.on('error', err => { - log('Error in downstream call:', err.message); - res.sendStatus(500); - }); - - request.end(); -}); - function createUrl(req, urlPath) { const pathWithQuery = req.query.withQuery ? `${urlPath}?q1=some&pass=verysecret&q2=value` : urlPath; return req.query.urlObject ? new URL(pathWithQuery, baseUrl) : baseUrl + pathWithQuery; diff --git a/packages/collector/test/tracing/protocols/http/client/errorHelper.js b/packages/collector/test/tracing/protocols/http/client/errorHelper.js deleted file mode 100644 index a2c8ee2eb4..0000000000 --- a/packages/collector/test/tracing/protocols/http/client/errorHelper.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * (c) Copyright IBM Corp. 2025 - */ - -'use strict'; - -/** - * Helper function that throws an error when a 500 status is received. - * This is used to test error stack replacement in HTTP client instrumentation. - */ -function handleErrorResponse(statusCode, responseBody) { - if (statusCode >= 500) { - const error = new Error(`Server error: ${statusCode}`); - error.statusCode = statusCode; - error.responseBody = responseBody; - throw error; - } - return { statusCode, responseBody }; -} - -module.exports = { - handleErrorResponse -}; diff --git a/packages/collector/test/tracing/protocols/http/client/serverApp.js b/packages/collector/test/tracing/protocols/http/client/serverApp.js index 9e19ad9dbf..df186c9974 100644 --- a/packages/collector/test/tracing/protocols/http/client/serverApp.js +++ b/packages/collector/test/tracing/protocols/http/client/serverApp.js @@ -67,10 +67,6 @@ app.put('/continue', (req, res) => { res.json({ response: 'yada yada yada' }); }); -app.get('/error-endpoint', (req, res) => { - res.status(500).json({ error: 'Internal Server Error' }); -}); - if (process.env.APP_USES_HTTPS === 'true') { const sslDir = path.join(__dirname, '..', '..', '..', '..', 'apps', 'ssl'); require('https') From 07ea2f2c3d00e9b7e754063dbf601eb279356068 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 18:22:47 +0530 Subject: [PATCH 16/39] chore: cleanup mysql --- .../test/tracing/databases/mysql/app.js | 2 -- .../test/tracing/databases/mysql/test.js | 17 ----------------- 2 files changed, 19 deletions(-) diff --git a/packages/collector/test/tracing/databases/mysql/app.js b/packages/collector/test/tracing/databases/mysql/app.js index 12fa8d9fd9..265ef82ab9 100644 --- a/packages/collector/test/tracing/databases/mysql/app.js +++ b/packages/collector/test/tracing/databases/mysql/app.js @@ -321,7 +321,6 @@ function triggerError(req, res) { return; } - // Execute an invalid SQL query to trigger an error connection[accessFunction]('SELECT * FROM non_existent_table', queryError => { connection.release(); @@ -340,7 +339,6 @@ function triggerErrorWithPromises(req, res) { pool .getConnection() .then(connection => { - // Execute an invalid SQL query to trigger an error wrapAccess(connection, 'SELECT * FROM non_existent_table', null, queryError => { connection.release(); diff --git a/packages/collector/test/tracing/databases/mysql/test.js b/packages/collector/test/tracing/databases/mysql/test.js index f53ce17855..9f233c8221 100644 --- a/packages/collector/test/tracing/databases/mysql/test.js +++ b/packages/collector/test/tracing/databases/mysql/test.js @@ -349,23 +349,6 @@ function test(env, agentControls) { ]); expect(mysqlSpan.stack).to.be.a('string'); - // expect(mysqlSpan.stack.length).to.be.greaterThan(0); - - // mysqlSpan.stack.forEach(frame => { - // expect(frame).to.have.property('m'); - // expect(frame).to.have.property('c'); - // expect(frame.m).to.be.a('string'); - // expect(frame.c).to.be.a('string'); - // }); - - // const hasRelevantFrame = mysqlSpan.stack.some( - // frame => - // frame.c.includes('app.js') || - // frame.c.includes('mysql') || - // frame.m.includes('query') || - // frame.m.includes('Query') - // ); - // expect(hasRelevantFrame).to.be.true; }) ) )); From 31202ae6abc0d4db9b7ac84c71f295916c459c4a Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 18:23:03 +0530 Subject: [PATCH 17/39] chore: cleanup pg native --- .../test/tracing/databases/pg_native/test.js | 17 ----------------- .../test/tracing/misc/stack_trace/test.js | 8 ++------ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/packages/collector/test/tracing/databases/pg_native/test.js b/packages/collector/test/tracing/databases/pg_native/test.js index 06805f3990..d89139226c 100644 --- a/packages/collector/test/tracing/databases/pg_native/test.js +++ b/packages/collector/test/tracing/databases/pg_native/test.js @@ -211,23 +211,6 @@ mochaSuiteFn('tracing/pg-native', function () { }); expect(pgExit.stack).to.be.a('string'); - // expect(pgExit.stack.length).to.be.greaterThan(0); - - // pgExit.stack.forEach(frame => { - // expect(frame).to.have.property('m'); - // expect(frame).to.have.property('c'); - // expect(frame.m).to.be.a('string'); - // expect(frame.c).to.be.a('string'); - // }); - - // const hasErrorFrame = pgExit.stack.some( - // frame => - // frame.c.includes('pg-native') || - // frame.c.includes('app.js') || - // frame.m.includes('query') || - // frame.m.includes('Query') - // ); - // expect(hasErrorFrame).to.be.true; verifyHttpExit(spans, httpEntry); }) diff --git a/packages/collector/test/tracing/misc/stack_trace/test.js b/packages/collector/test/tracing/misc/stack_trace/test.js index d3c9c480f2..d8dc6454c3 100644 --- a/packages/collector/test/tracing/misc/stack_trace/test.js +++ b/packages/collector/test/tracing/misc/stack_trace/test.js @@ -89,10 +89,6 @@ const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : descri await expressProxyControls.stop(); }); - beforeEach(async () => { - await agentControls.clearReceivedTraceData(); - }); - beforeEach(async () => { await agentControls.waitUntilAppIsCompletelyInitialized(expressControls.getPid()); }); @@ -111,7 +107,7 @@ const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : descri .then(() => testUtils.retry(() => agentControls.getSpans().then(spans => { - expect(spans.length).to.equal(3); + expect(spans.length).to.equal(6); testUtils.expectAtLeastOneMatching(spans, [ span => expect(span.n).to.equal('node.http.server'), span => expect(span.stack).to.have.lengthOf(0) @@ -130,7 +126,7 @@ const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : descri .then(() => testUtils.retry(() => agentControls.getSpans().then(spans => { - expect(spans.length).to.equal(3); + expect(spans.length).to.equal(9); testUtils.expectAtLeastOneMatching(spans, [ span => expect(span.n).to.equal('node.http.client'), span => expect(span.k).to.equal(constants.EXIT), From 18daf3f1c76466a4ae0d6e52c24b0ae6154f1af6 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 18:26:11 +0530 Subject: [PATCH 18/39] chore: update seterrorstack fn --- packages/core/src/tracing/tracingUtil.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index eab0c3e13e..0447ddea92 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -291,12 +291,12 @@ exports.setErrorStack = function setErrorStack(span, error, technology) { if (technology && span.data && span.data[technology]) { // Do not overwrite if instrumentation already set a custom value eg: httpClient if (!span.data[technology].error) { - span.data[technology].error = exports.getErrorDetails(error); + span.data[technology].error = String(error.stack || error.message || error).substring(0, 500); } } // TODO: Once the configuration change is ready, optionally trim the span.stack to the configured length if (error.stack) { - span.stack = error.stack; + span.stack = String(error.stack).substring(500); } }; From 0ae9e62bc1e0688ce7bf474f77fd0628426ec32e Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 18:27:55 +0530 Subject: [PATCH 19/39] chore: added comment on sdk case --- packages/core/src/tracing/sdk/sdk.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/src/tracing/sdk/sdk.js b/packages/core/src/tracing/sdk/sdk.js index 4ba6b6ec75..a594c90b2f 100644 --- a/packages/core/src/tracing/sdk/sdk.js +++ b/packages/core/src/tracing/sdk/sdk.js @@ -330,6 +330,8 @@ exports.generate = function (isCallbackApi) { span.data.sdk.custom.tags = {}; } if (span.data.sdk.custom.tags.message == null) { + // TODO: this is a special case as the key if not span.data.tech.error + // This fn used getErrorDetails for now and will need to change span.data.sdk.custom.tags.message = tracingUtil.getErrorDetails(error); } } From b2b42c7bec66e334432523e52eb0c210c730ff98 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 19:01:21 +0530 Subject: [PATCH 20/39] chore: nats fix --- .../test/tracing/protocols/http/native_fetch/test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/collector/test/tracing/protocols/http/native_fetch/test.js b/packages/collector/test/tracing/protocols/http/native_fetch/test.js index 0ac4ea03df..5ca9c9e4ce 100644 --- a/packages/collector/test/tracing/protocols/http/native_fetch/test.js +++ b/packages/collector/test/tracing/protocols/http/native_fetch/test.js @@ -858,7 +858,9 @@ function verifyHttpExit({ expectations.push(span => expect(span.data.http.status).to.not.exist); // Early v18.x Node.js versions had "The operation was aborted", the message later changed to // "The operation was aborted due to timeout". - expectations.push(span => expect(span.data.http.error).to.match(/^The operation was aborted(?: due to timeout)?/)); + expectations.push(span => + expect(span.data.http.error).to.match(/^TimeoutError: The operation was aborted(?: due to timeout)?/) + ); } else { expectations.push(span => expect(span.data.http.status).to.equal(status)); expectations.push(span => expect(span.data.http.error).to.not.exist); From 65c3bf900126e970627e6c5a067d991bff8aa52e Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 19:03:09 +0530 Subject: [PATCH 21/39] chore: updated fn name setErrorDetails --- .../instrumentation/cloud/aws-sdk/v2/sqs.js | 2 +- .../cloud/aws-sdk/v3/sqs-consumer.js | 4 +-- .../instrumentation/cloud/azure/blob.js | 2 +- .../instrumentation/cloud/gcp/pubsub.js | 2 +- .../instrumentation/cloud/gcp/storage.js | 2 +- .../instrumentation/databases/couchbase.js | 6 ++-- .../tracing/instrumentation/databases/db2.js | 28 +++++++++---------- .../databases/elasticsearch.js | 2 +- .../instrumentation/databases/ioredis.js | 8 +++--- .../instrumentation/databases/mongodb.js | 4 +-- .../instrumentation/databases/mssql.js | 2 +- .../instrumentation/databases/mysql.js | 4 +-- .../tracing/instrumentation/databases/pg.js | 2 +- .../instrumentation/databases/pgNative.js | 2 +- .../instrumentation/databases/prisma.js | 2 +- .../instrumentation/databases/redis.js | 6 ++-- .../instrumentation/frameworks/express.js | 2 +- .../tracing/instrumentation/messaging/bull.js | 2 +- .../instrumentation/messaging/kafkaJs.js | 4 +-- .../instrumentation/messaging/kafkaNode.js | 2 +- .../instrumentation/messaging/rdkafka.js | 6 ++-- .../instrumentation/protocols/grpcJs.js | 8 +++--- .../instrumentation/protocols/httpClient.js | 8 +++--- .../instrumentation/protocols/nativeFetch.js | 2 +- packages/core/src/tracing/tracingUtil.js | 2 +- 25 files changed, 57 insertions(+), 57 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js index 8304e0a533..d9aa07007f 100644 --- a/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js +++ b/packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/sqs.js @@ -392,7 +392,7 @@ function finishSpan(err, data, span) { function addErrorToSpan(err, span) { if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'sqs'); + tracingUtil.setErrorDetails(span, err, 'sqs'); } } 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 d4333d9d3c..cd29f1aba6 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 @@ -31,7 +31,7 @@ function instrument(SQSConsumer) { }) .catch(err => { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'sqs'); + tracingUtil.setErrorDetails(span, err, 'sqs'); span.d = Date.now() - span.ts; span.transmitManual(); }); @@ -63,7 +63,7 @@ function instrument(SQSConsumer) { }) .catch(err => { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'sqs'); + tracingUtil.setErrorDetails(span, err, 'sqs'); span.d = Date.now() - span.ts; span.transmitManual(); }); diff --git a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js index 02bba988a0..d2b076cf13 100644 --- a/packages/core/src/tracing/instrumentation/cloud/azure/blob.js +++ b/packages/core/src/tracing/instrumentation/cloud/azure/blob.js @@ -105,7 +105,7 @@ function instrumentingOperation({ function finishSpan(error, span) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'azstorage'); + tracingUtil.setErrorDetails(span, error, 'azstorage'); } span.d = Date.now() - span.ts; span.transmit(); diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js index da756b0b34..a2adfec562 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/pubsub.js @@ -254,7 +254,7 @@ function finishSpan(err, messageId, span) { function addErrorToSpan(err, span) { if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'gcps'); + tracingUtil.setErrorDetails(span, err, 'gcps'); } } diff --git a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js index 8eb423d5fa..c1d4fbb43d 100644 --- a/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js +++ b/packages/core/src/tracing/instrumentation/cloud/gcp/storage.js @@ -458,7 +458,7 @@ function instrumentedCreateStream(operation, bindEvent, finalEvent, ctx, origina function finishSpan(error, result, span, extractorPost) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'gcs'); + tracingUtil.setErrorDetails(span, error, 'gcs'); } if (extractorPost) { diff --git a/packages/core/src/tracing/instrumentation/databases/couchbase.js b/packages/core/src/tracing/instrumentation/databases/couchbase.js index 25005143f5..cd800234b0 100644 --- a/packages/core/src/tracing/instrumentation/databases/couchbase.js +++ b/packages/core/src/tracing/instrumentation/databases/couchbase.js @@ -430,7 +430,7 @@ function instrumentTransactions(cluster, connectionStr) { result .catch(err => { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'couchbase'); + tracingUtil.setErrorDetails(span, err, 'couchbase'); }) .finally(() => { span.d = Date.now() - span.ts; @@ -497,7 +497,7 @@ function instrumentOperation({ connectionStr, bucketName, getBucketTypeFn, sql, }) .catch(err => { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'couchbase'); + tracingUtil.setErrorDetails(span, err, 'couchbase'); }) .finally(() => { span.d = Date.now() - span.ts; @@ -510,7 +510,7 @@ function instrumentOperation({ connectionStr, bucketName, getBucketTypeFn, sql, originalArgs[callbackIndex] = cls.ns.bind(function instanaCallback(err, result) { if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'couchbase'); + tracingUtil.setErrorDetails(span, err, 'couchbase'); } if (resultHandler) { diff --git a/packages/core/src/tracing/instrumentation/databases/db2.js b/packages/core/src/tracing/instrumentation/databases/db2.js index 2e08be4e91..e09a4c0876 100644 --- a/packages/core/src/tracing/instrumentation/databases/db2.js +++ b/packages/core/src/tracing/instrumentation/databases/db2.js @@ -222,7 +222,7 @@ function captureFetchError(result, span) { argsFetch[fetchIndex] = function instanaFetchCb(fetchCbErr) { if (fetchCbErr) { span.ec = 1; - tracingUtil.setErrorStack(span, fetchCbErr, 'db2'); + tracingUtil.setErrorDetails(span, fetchCbErr, 'db2'); } return fetchCb.apply(this, arguments); @@ -232,7 +232,7 @@ function captureFetchError(result, span) { return originalFn.apply(this, arguments); } catch (caughtErr) { span.ec = 1; - tracingUtil.setErrorStack(span, caughtErr, 'db2'); + tracingUtil.setErrorDetails(span, caughtErr, 'db2'); throw caughtErr; } }; @@ -249,12 +249,12 @@ function captureFetchError(result, span) { if (res instanceof Error) { span.ec = 1; - tracingUtil.setErrorStack(span, res, 'db2'); + tracingUtil.setErrorDetails(span, res, 'db2'); } return res; } catch (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'db2'); + tracingUtil.setErrorDetails(span, err, 'db2'); return err; } @@ -278,14 +278,14 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn if (result instanceof Error) { span.ec = 1; - tracingUtil.setErrorStack(span, result, 'db2'); + tracingUtil.setErrorDetails(span, result, 'db2'); } finishSpan(ctx, result, span); return result; } catch (e) { span.ec = 1; - tracingUtil.setErrorStack(span, e, 'db2'); + tracingUtil.setErrorDetails(span, e, 'db2'); finishSpan(ctx, null, span); throw e; } @@ -304,7 +304,7 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn originalArgs[customerCallbackIndex] = function instanaCallback(err) { if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'db2'); + tracingUtil.setErrorDetails(span, err, 'db2'); } finishSpan(ctx, null, span); @@ -324,7 +324,7 @@ function instrumentQueryHelper(ctx, originalArgs, originalFunction, stmt, isAsyn }) .catch(err => { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'db2'); + tracingUtil.setErrorDetails(span, err, 'db2'); finishSpan(ctx, null, span); return err; }); @@ -394,7 +394,7 @@ function instrumentExecuteHelper(ctx, originalArgs, stmtObject, prepareCallParen return result; } catch (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'db2'); + tracingUtil.setErrorDetails(span, err, 'db2'); finishSpan(ctx, null, span); return err; } @@ -441,7 +441,7 @@ function instrumentExecuteHelper(ctx, originalArgs, stmtObject, prepareCallParen args[origCallbackIndex] = function instanaExecuteCallback(executeErr, result) { if (executeErr) { span.ec = 1; - tracingUtil.setErrorStack(span, executeErr, 'db2'); + tracingUtil.setErrorDetails(span, executeErr, 'db2'); finishSpan(ctx, null, span); return origCallback.apply(this, arguments); } @@ -470,7 +470,7 @@ function instrumentQueryResultHelper(ctx, originalArgs, originalFunction, stmt, return result; } catch (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'db2'); + tracingUtil.setErrorDetails(span, err, 'db2'); finishSpan(ctx, null, span); return err; } @@ -489,7 +489,7 @@ function instrumentQueryResultHelper(ctx, originalArgs, originalFunction, stmt, originalArgs[customerCallbackIndex] = function instanaCallback(err) { if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'db2'); + tracingUtil.setErrorDetails(span, err, 'db2'); } const result = customerCallback.apply(this, arguments); @@ -580,7 +580,7 @@ function handleTransaction(ctx, span) { arguments[1] = function instanaOnEndOverride(onEndErr) { if (onEndErr) { span.ec = 1; - tracingUtil.setErrorStack(span, onEndErr, 'db2'); + tracingUtil.setErrorDetails(span, onEndErr, 'db2'); } span.d = Date.now() - span.ts; @@ -599,7 +599,7 @@ function handleTransaction(ctx, span) { return result; } catch (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'db2'); + tracingUtil.setErrorDetails(span, err, 'db2'); span.transmit(); throw err; } diff --git a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js index 2004723d8f..4877c53604 100644 --- a/packages/core/src/tracing/instrumentation/databases/elasticsearch.js +++ b/packages/core/src/tracing/instrumentation/databases/elasticsearch.js @@ -200,7 +200,7 @@ function onError(span, error) { span.d = Date.now() - span.ts; span.ec = 1; if (error) { - tracingUtil.setErrorStack(span, error, 'elasticsearch'); + tracingUtil.setErrorDetails(span, error, 'elasticsearch'); } if (error.meta && error.meta.meta) { getConnectionDetailsFromResultMeta(span, error.meta); diff --git a/packages/core/src/tracing/instrumentation/databases/ioredis.js b/packages/core/src/tracing/instrumentation/databases/ioredis.js index 6db26aa5cf..e88a1c69e3 100644 --- a/packages/core/src/tracing/instrumentation/databases/ioredis.js +++ b/packages/core/src/tracing/instrumentation/databases/ioredis.js @@ -115,7 +115,7 @@ function instrumentSendCommand(original) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'redis'); + tracingUtil.setErrorDetails(span, error, 'redis'); } span.transmit(); @@ -221,7 +221,7 @@ function multiCommandEndCallback(clsContextForMultiOrPipeline, span, error) { if (error) { span.ec = commandCount; - tracingUtil.setErrorStack(span, error, 'redis'); + tracingUtil.setErrorDetails(span, error, 'redis'); } span.transmit(); @@ -241,7 +241,7 @@ function pipelineCommandEndCallback(clsContextForMultiOrPipeline, span, error, r if (error) { // ioredis docs mention that this should never be possible, but better be safe than sorry span.ec = commandCount; - tracingUtil.setErrorStack(span, error, 'redis'); + tracingUtil.setErrorDetails(span, error, 'redis'); } else { let numberOfErrors = 0; let sampledError; @@ -257,7 +257,7 @@ function pipelineCommandEndCallback(clsContextForMultiOrPipeline, span, error, r if (numberOfErrors > 0) { span.ec = numberOfErrors; - tracingUtil.setErrorStack(span, sampledError, 'redis'); + tracingUtil.setErrorDetails(span, sampledError, 'redis'); } } diff --git a/packages/core/src/tracing/instrumentation/databases/mongodb.js b/packages/core/src/tracing/instrumentation/databases/mongodb.js index 9bf531523e..7582e1917c 100644 --- a/packages/core/src/tracing/instrumentation/databases/mongodb.js +++ b/packages/core/src/tracing/instrumentation/databases/mongodb.js @@ -438,7 +438,7 @@ function createWrappedCallback(span, originalCallback) { return cls.ns.bind(function (error) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'mongo'); + tracingUtil.setErrorDetails(span, error, 'mongo'); } span.d = Date.now() - span.ts; @@ -466,7 +466,7 @@ function handleCallbackOrPromise(ctx, originalArgs, originalFunction, span) { }) .catch(err => { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'mongo'); + tracingUtil.setErrorDetails(span, err, 'mongo'); span.d = Date.now() - span.ts; span.transmit(); return err; diff --git a/packages/core/src/tracing/instrumentation/databases/mssql.js b/packages/core/src/tracing/instrumentation/databases/mssql.js index 02cac7cf35..d1cf837774 100644 --- a/packages/core/src/tracing/instrumentation/databases/mssql.js +++ b/packages/core/src/tracing/instrumentation/databases/mssql.js @@ -161,7 +161,7 @@ function shimBeginTransaction(originalFunction) { function finishSpan(error, span) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'mssql'); + tracingUtil.setErrorDetails(span, error, 'mssql'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/mysql.js b/packages/core/src/tracing/instrumentation/databases/mysql.js index f9aa120ac8..a33688fa86 100644 --- a/packages/core/src/tracing/instrumentation/databases/mysql.js +++ b/packages/core/src/tracing/instrumentation/databases/mysql.js @@ -190,7 +190,7 @@ function instrumentedAccessFunction( }) .catch(error => { span.ec = 1; - tracingUtil.setErrorStack(span, error, exports.spanName); + tracingUtil.setErrorDetails(span, error, exports.spanName); span.d = Date.now() - span.ts; span.transmit(); @@ -205,7 +205,7 @@ function instrumentedAccessFunction( function onResult(error) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, exports.spanName); + tracingUtil.setErrorDetails(span, error, exports.spanName); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/pg.js b/packages/core/src/tracing/instrumentation/databases/pg.js index 4acd7622cb..eed210ad70 100644 --- a/packages/core/src/tracing/instrumentation/databases/pg.js +++ b/packages/core/src/tracing/instrumentation/databases/pg.js @@ -103,7 +103,7 @@ function instrumentedQuery(ctx, originalQuery, argsForOriginalQuery) { function finishSpan(error, span) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'pg'); + tracingUtil.setErrorDetails(span, error, 'pg'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/pgNative.js b/packages/core/src/tracing/instrumentation/databases/pgNative.js index e102805254..60487ea8d0 100644 --- a/packages/core/src/tracing/instrumentation/databases/pgNative.js +++ b/packages/core/src/tracing/instrumentation/databases/pgNative.js @@ -200,7 +200,7 @@ function finishSpan(error, span) { // Note: Instead of 'pg', we could've passed exports.spanName if they were the same, // We can’t use spanName here because for this instr the span name is // "postgres", but the data is stored under span.data.pg. - tracingUtil.setErrorStack(span, error, 'pg'); + tracingUtil.setErrorDetails(span, error, 'pg'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/prisma.js b/packages/core/src/tracing/instrumentation/databases/prisma.js index 6d65284afa..dbd1d670af 100644 --- a/packages/core/src/tracing/instrumentation/databases/prisma.js +++ b/packages/core/src/tracing/instrumentation/databases/prisma.js @@ -221,7 +221,7 @@ function redactPasswordFromMsSQLUrl(url) { function finishSpan(error, span) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'prisma'); + tracingUtil.setErrorDetails(span, error, 'prisma'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/databases/redis.js b/packages/core/src/tracing/instrumentation/databases/redis.js index 3eb2aaf044..d469b7636f 100644 --- a/packages/core/src/tracing/instrumentation/databases/redis.js +++ b/packages/core/src/tracing/instrumentation/databases/redis.js @@ -365,7 +365,7 @@ function instrumentCommand(original, command, address, cbStyle) { if (error) { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'redis'); + tracingUtil.setErrorDetails(span, error, 'redis'); } span.transmit(); @@ -502,7 +502,7 @@ function instrumentMultiExec(origCtx, origArgs, original, address, isAtomic, cbS if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'redis'); + tracingUtil.setErrorDetails(span, err, 'redis'); // v3 = provides sub errors if (err.errors && err.errors.length) { @@ -526,7 +526,7 @@ function buildSubCommandCallback(span, userProvidedCallback) { return function subCommandCallback(err) { if (err) { span.ec++; - tracingUtil.setErrorStack(span, err, 'redis'); + tracingUtil.setErrorDetails(span, err, 'redis'); } if (typeof userProvidedCallback === 'function') { diff --git a/packages/core/src/tracing/instrumentation/frameworks/express.js b/packages/core/src/tracing/instrumentation/frameworks/express.js index a5990bc31b..2f4a89ff17 100644 --- a/packages/core/src/tracing/instrumentation/frameworks/express.js +++ b/packages/core/src/tracing/instrumentation/frameworks/express.js @@ -116,7 +116,7 @@ function annotateHttpEntrySpanWithError(err) { return; } - tracingUtil.setErrorStack(span, err, 'http'); + tracingUtil.setErrorDetails(span, err, 'http'); } function shimHandlerRegistration(original) { diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index 09e9e01cfc..7cf71cb2d5 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -327,7 +327,7 @@ function addErrorToSpan(err, span) { span.ec = 1; // TODO: need to give priority for err.code? - tracingUtil.setErrorStack(span, err, 'bull'); + tracingUtil.setErrorDetails(span, err, 'bull'); } } diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js index 767b76eeaa..f887225f13 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaJs.js @@ -105,7 +105,7 @@ function instrumentedSend(ctx, originalSend, originalArgs, topic, messages) { }) .catch(error => { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'kafka'); + tracingUtil.setErrorDetails(span, error, 'kafka'); span.d = Date.now() - span.ts; span.transmit(); throw error; @@ -184,7 +184,7 @@ function instrumentedSendBatch(ctx, originalSendBatch, originalArgs, topicMessag }) .catch(error => { span.ec = 1; - tracingUtil.setErrorStack(span, error, 'kafka'); + tracingUtil.setErrorDetails(span, error, 'kafka'); span.d = Date.now() - span.ts; span.transmit(); throw error; diff --git a/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js b/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js index b017934d8c..0721f7ce35 100644 --- a/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js +++ b/packages/core/src/tracing/instrumentation/messaging/kafkaNode.js @@ -81,7 +81,7 @@ function instrumentedSend(ctx, originalSend, produceRequests, cb) { cls.ns.bind(function onSendCompleted(err) { if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'kafka'); + tracingUtil.setErrorDetails(span, err, 'kafka'); } span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/instrumentation/messaging/rdkafka.js b/packages/core/src/tracing/instrumentation/messaging/rdkafka.js index 2f5f238782..14b245457c 100644 --- a/packages/core/src/tracing/instrumentation/messaging/rdkafka.js +++ b/packages/core/src/tracing/instrumentation/messaging/rdkafka.js @@ -167,7 +167,7 @@ function instrumentedProduce(ctx, originalProduce, originalArgs) { if (err) { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'kafka'); + tracingUtil.setErrorDetails(span, err, 'kafka'); } span.transmit(); @@ -187,7 +187,7 @@ function instrumentedProduce(ctx, originalProduce, originalArgs) { // e.g. cannot send message because format is byte // "Message must be a buffer or null" span.ec = 1; - tracingUtil.setErrorStack(span, error, 'kafka'); + tracingUtil.setErrorDetails(span, error, 'kafka'); if (!deliveryCb) { span.d = Date.now() - span.ts; @@ -305,7 +305,7 @@ function instrumentedConsumerEmit(ctx, originalEmit, originalArgs) { delete messageData.headers; span.ec = 1; - tracingUtil.setErrorStack(span, messageData, 'kafka'); + tracingUtil.setErrorDetails(span, messageData, 'kafka'); } setImmediate(() => { diff --git a/packages/core/src/tracing/instrumentation/protocols/grpcJs.js b/packages/core/src/tracing/instrumentation/protocols/grpcJs.js index fe4e1f57b8..6967156fe8 100644 --- a/packages/core/src/tracing/instrumentation/protocols/grpcJs.js +++ b/packages/core/src/tracing/instrumentation/protocols/grpcJs.js @@ -119,7 +119,7 @@ function modifyArgs(name, originalArgs, span) { if (errorMessage) { span.data.rpc.error = errorMessage; } - tracingUtil.setErrorStack(span, err, 'rpc'); + tracingUtil.setErrorDetails(span, err, 'rpc'); } } @@ -349,7 +349,7 @@ function createInstrumentedServerHandler(name, type, originalHandler) { if (err.message || err.details) { span.data.rpc.error = err.message || err.details; } - tracingUtil.setErrorStack(span, err, 'rpc'); + tracingUtil.setErrorDetails(span, err, 'rpc'); } span.d = Date.now() - span.ts; span.transmit(); @@ -375,7 +375,7 @@ function createInstrumentedServerHandler(name, type, originalHandler) { if (err.message || err.details) { span.data.rpc.error = err.message || err.details; } - tracingUtil.setErrorStack(span, err, 'rpc'); + tracingUtil.setErrorDetails(span, err, 'rpc'); }); call.on('cancelled', () => { @@ -438,7 +438,7 @@ function instrumentedClientMethod( if (errorMessage) { span.data.rpc.error = errorMessage; } - tracingUtil.setErrorStack(span, err, 'rpc'); + tracingUtil.setErrorDetails(span, err, 'rpc'); } span.transmit(); }); diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 47c862473f..6c964ad1ee 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -247,7 +247,7 @@ function instrument(coreModule, forceHttps) { // Currently we don't parse anything from the body, and its not advised too // so do not override here, only override on the event of an error catch // if (span.ec === 1) { - // tracingUtil.setErrorStack(span, res.body.stack); + // tracingUtil.setErrorDetails(span, res.body.stack); // } // here no Error is found for parsing @@ -273,7 +273,7 @@ function instrument(coreModule, forceHttps) { // example is a case that triggers a synchronous exception. span.data.http = {}; span.data.http.url = completeCallUrl; - tracingUtil.setErrorStack(span, e, 'http'); + tracingUtil.setErrorDetails(span, e, 'http'); span.d = Date.now() - span.ts; span.ec = 1; span.transmit(); @@ -322,11 +322,11 @@ function instrument(coreModule, forceHttps) { url: completeCallUrl }; // This assignment is intentional - // Even though setErrorStack runs in this codeblock, this custom error message is a special-case behavior + // Even though setErrorDetails runs in this codeblock, this custom error message is a special-case behavior span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1; - tracingUtil.setErrorStack(span, err, exports.spanName); + tracingUtil.setErrorDetails(span, err, exports.spanName); span.transmit(); }); diff --git a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js index 0aaf9a1bf8..a576ba00a7 100644 --- a/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js +++ b/packages/core/src/tracing/instrumentation/protocols/nativeFetch.js @@ -184,7 +184,7 @@ function instrument() { }) .catch(err => { span.ec = 1; - tracingUtil.setErrorStack(span, err, 'http'); + tracingUtil.setErrorDetails(span, err, 'http'); }) .finally(() => { span.d = Date.now() - span.ts; diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index 0447ddea92..f8707a3ccf 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -283,7 +283,7 @@ exports.findCallback = (/** @type {string | any[]} */ originalArgs) => { * @param {string} technology - The technology name (e.g., 'mysql', 'pg', 'http') */ // @ts-ignore -exports.setErrorStack = function setErrorStack(span, error, technology) { +exports.setErrorDetails = function setErrorDetails(span, error, technology) { if (!error) { return; } From 5fe904dbaba62b3e741f230746c9647ab7601260 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 19:18:48 +0530 Subject: [PATCH 22/39] chore: cleanup --- .../src/tracing/instrumentation/databases/pgNative.js | 3 --- .../src/tracing/instrumentation/messaging/bull.js | 1 - .../tracing/instrumentation/protocols/httpClient.js | 11 +---------- packages/core/src/tracing/tracingUtil.js | 2 +- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/databases/pgNative.js b/packages/core/src/tracing/instrumentation/databases/pgNative.js index 60487ea8d0..8407f5b5a9 100644 --- a/packages/core/src/tracing/instrumentation/databases/pgNative.js +++ b/packages/core/src/tracing/instrumentation/databases/pgNative.js @@ -197,9 +197,6 @@ function startSpanBeforeSync(ctx, originalFn, originalArgs, statement, stackTrac function finishSpan(error, span) { if (error) { span.ec = 1; - // Note: Instead of 'pg', we could've passed exports.spanName if they were the same, - // We can’t use spanName here because for this instr the span name is - // "postgres", but the data is stored under span.data.pg. tracingUtil.setErrorDetails(span, error, 'pg'); } diff --git a/packages/core/src/tracing/instrumentation/messaging/bull.js b/packages/core/src/tracing/instrumentation/messaging/bull.js index 7cf71cb2d5..d0bb2951d3 100644 --- a/packages/core/src/tracing/instrumentation/messaging/bull.js +++ b/packages/core/src/tracing/instrumentation/messaging/bull.js @@ -326,7 +326,6 @@ function addErrorToSpan(err, span) { if (err) { span.ec = 1; - // TODO: need to give priority for err.code? tracingUtil.setErrorDetails(span, err, 'bull'); } } diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 6c964ad1ee..bfe5314767 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -243,14 +243,6 @@ function instrument(coreModule, forceHttps) { span.d = Date.now() - span.ts; span.ec = res.statusCode >= 500 ? 1 : 0; - // Internal: Remove/update before RFR - // Currently we don't parse anything from the body, and its not advised too - // so do not override here, only override on the event of an error catch - // if (span.ec === 1) { - // tracingUtil.setErrorDetails(span, res.body.stack); - // } - // here no Error is found for parsing - span.transmit(); if (callback) { @@ -321,8 +313,7 @@ function instrument(coreModule, forceHttps) { method: clientRequest.method, url: completeCallUrl }; - // This assignment is intentional - // Even though setErrorDetails runs in this codeblock, this custom error message is a special-case behavior + // Note: Not removing the span.data.http.error assignment because this custom error message is a special-case span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1; diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index f8707a3ccf..df76340620 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -289,7 +289,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { } if (technology && span.data && span.data[technology]) { - // Do not overwrite if instrumentation already set a custom value eg: httpClient + // This check is for special cases where instrumentation already set a custom value like in httpClient if (!span.data[technology].error) { span.data[technology].error = String(error.stack || error.message || error).substring(0, 500); } From 0dced9d652658922e6bd9734224fbda30b4f4696 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 20:10:05 +0530 Subject: [PATCH 23/39] chore: update tracingutil --- 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 df76340620..0380b0e105 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -291,7 +291,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { if (technology && span.data && span.data[technology]) { // This check is for special cases where instrumentation already set a custom value like in httpClient if (!span.data[technology].error) { - span.data[technology].error = String(error.stack || error.message || error).substring(0, 500); + span.data[technology].error = String(error.message).substring(0, 500); } } From a74e38763d20713f08a99035827853602168d2d6 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 20:10:19 +0530 Subject: [PATCH 24/39] test: udpate tests --- .../test/tracing/messaging/node-rdkafka/test_definition.js | 2 +- .../test/tracing/protocols/http/native_fetch/test.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js b/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js index f5ca613a84..5fef745288 100644 --- a/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js +++ b/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js @@ -272,7 +272,7 @@ module.exports.run = function ({ span => expect(span.ec).to.equal(!withError ? 0 : 1), span => (!withError ? expect(span.data.kafka.error).to.not.exist : ''), span => - withError === 'deliveryErrorSender' ? expect(span.data.kafka.error).to.contain('delivery fake error') : '', + withError === 'deliveryErrorSender' ? expect(span.data.kafka.error).to.equal('delivery fake error') : '', span => withError === 'bufferErrorSender' ? expect(span.data.kafka.error).to.equal('Message must be a buffer or null') diff --git a/packages/collector/test/tracing/protocols/http/native_fetch/test.js b/packages/collector/test/tracing/protocols/http/native_fetch/test.js index 5ca9c9e4ce..a6b4748098 100644 --- a/packages/collector/test/tracing/protocols/http/native_fetch/test.js +++ b/packages/collector/test/tracing/protocols/http/native_fetch/test.js @@ -853,14 +853,12 @@ function verifyHttpExit({ expectedClientError = `Failed to parse URL from http:127.0.0.1:${serverControls.port}malformed-url`; } expectations.push(span => expect(span.data.http.status).to.not.exist); - expectations.push(span => expect(span.data.http.error).to.contain(expectedClientError)); + expectations.push(span => expect(span.data.http.error).to.equal(expectedClientError)); } else if (withTimeout) { expectations.push(span => expect(span.data.http.status).to.not.exist); // Early v18.x Node.js versions had "The operation was aborted", the message later changed to // "The operation was aborted due to timeout". - expectations.push(span => - expect(span.data.http.error).to.match(/^TimeoutError: The operation was aborted(?: due to timeout)?/) - ); + expectations.push(span => expect(span.data.http.error).to.match(/^The operation was aborted(?: due to timeout)?/)); } else { expectations.push(span => expect(span.data.http.status).to.equal(status)); expectations.push(span => expect(span.data.http.error).to.not.exist); From dced95e994d7902c96175cf7161feaaf23669e9c Mon Sep 17 00:00:00 2001 From: Abhilash Date: Wed, 26 Nov 2025 23:16:39 +0530 Subject: [PATCH 25/39] test: fixed db2 test as the new logic takes error.message --- packages/collector/test/tracing/databases/db2/test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/collector/test/tracing/databases/db2/test.js b/packages/collector/test/tracing/databases/db2/test.js index 905c9c62d2..42e535d79b 100644 --- a/packages/collector/test/tracing/databases/db2/test.js +++ b/packages/collector/test/tracing/databases/db2/test.js @@ -261,7 +261,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: 'select invalid query', - error: 'Error: [IBM][CLI Driver][DB2/LINUXX8664] SQL0104N An unexpected token' + error: '[IBM][CLI Driver][DB2/LINUXX8664] SQL0104N An unexpected token' }) ) ); @@ -598,7 +598,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: `insert into ${TABLE_NAME_1} (COLINT, COLDATETIME, COLTEXT) VALUES (?, ?, ?)`, - error: 'Error: [IBM][CLI Driver][DB2/LINUXX8664] SQL0181N The string representatio' + error: '[IBM][CLI Driver][DB2/LINUXX8664] SQL0181N The string representatio' }) ) ); @@ -646,7 +646,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: `SELECT * FROM ${TABLE_NAME_1}`, - error: 'TypeError' + error: "Cannot read properties of null (reading 'fetchMode')" }) ) ); @@ -720,7 +720,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: `insert into ${TABLE_NAME_1}(COLINT, COLDATETIME, COLTEXT) VALUES (?, ?, ?)`, - error: 'Error: [IBM][CLI Driver] CLI0100E Wrong number of parameters. SQLSTATE=07001' + error: '[IBM][CLI Driver] CLI0100E Wrong number of parameters. SQLSTATE=07001' }) ) ); @@ -790,7 +790,7 @@ mochaSuiteFn('tracing/db2', function () { }); it('[with fetch and error] must trace prepare execute mixed 1', function () { - const expectedError = 'TypeError: Cannot read properties of null (reading'; + const expectedError = 'Cannot read properties of null (reading'; return controls .sendRequest({ From dab9bc4dc677b8aef7dd7ef3893a7486de74e456 Mon Sep 17 00:00:00 2001 From: Abhilash <70062455+abhilash-sivan@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:36:36 +0530 Subject: [PATCH 26/39] chore: update packages/core/src/tracing/tracingUtil.js Co-authored-by: kirrg001 --- 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 0380b0e105..6396c9c035 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -295,7 +295,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { } } - // TODO: Once the configuration change is ready, optionally trim the span.stack to the configured length + // TODO: Change substring usage to frame capturing - see util/stackTrace.js if (error.stack) { span.stack = String(error.stack).substring(500); } From 2f563d4ae170084a97c0e5997236d32a1e9be282 Mon Sep 17 00:00:00 2001 From: Abhilash <70062455+abhilash-sivan@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:36:57 +0530 Subject: [PATCH 27/39] chore: update packages/core/src/tracing/tracingUtil.js Co-authored-by: kirrg001 --- 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 6396c9c035..d0902a28b2 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -288,7 +288,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { return; } - if (technology && span.data && span.data[technology]) { + if (technology && span.data?.[technology]) { // This check is for special cases where instrumentation already set a custom value like in httpClient if (!span.data[technology].error) { span.data[technology].error = String(error.message).substring(0, 500); From d425985a0d2f59f5b083bb4a281fa592b617e8f7 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 10:53:21 +0530 Subject: [PATCH 28/39] chore: updated the error substring logic in tracingutil --- packages/core/src/tracing/tracingUtil.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index d0902a28b2..1e59c1e155 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -291,7 +291,10 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { if (technology && span.data?.[technology]) { // This check is for special cases where instrumentation already set a custom value like in httpClient if (!span.data[technology].error) { - span.data[technology].error = String(error.message).substring(0, 500); + span.data[technology].error = error.message + ? error.message.substring(0, 100) + : // @ts-ignore + error?.code || 'No error message found.'; } } From b8b8cd16b20f8faba2242fe42c7258b5647c4389 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 11:04:53 +0530 Subject: [PATCH 29/39] chore: update comment --- packages/core/src/tracing/sdk/sdk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tracing/sdk/sdk.js b/packages/core/src/tracing/sdk/sdk.js index a594c90b2f..cc072053ed 100644 --- a/packages/core/src/tracing/sdk/sdk.js +++ b/packages/core/src/tracing/sdk/sdk.js @@ -330,7 +330,7 @@ exports.generate = function (isCallbackApi) { span.data.sdk.custom.tags = {}; } if (span.data.sdk.custom.tags.message == null) { - // TODO: this is a special case as the key if not span.data.tech.error + // TODO: fix sdk error capture and handling // This fn used getErrorDetails for now and will need to change span.data.sdk.custom.tags.message = tracingUtil.getErrorDetails(error); } From 5f9a8278fec6201560e548d7dee353ee93ca0c47 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 11:09:19 +0530 Subject: [PATCH 30/39] chore: remove unintentional export.spanName usage --- .../core/src/tracing/instrumentation/protocols/httpClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index bfe5314767..0719f40c0a 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -317,7 +317,7 @@ function instrument(coreModule, forceHttps) { span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1; - tracingUtil.setErrorDetails(span, err, exports.spanName); + tracingUtil.setErrorDetails(span, err, 'http'); span.transmit(); }); From 3da21145dc04f2f10189ad584efc94fb4290249e Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 11:12:58 +0530 Subject: [PATCH 31/39] chore: corrected db2 --- packages/core/src/tracing/instrumentation/databases/db2.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core/src/tracing/instrumentation/databases/db2.js b/packages/core/src/tracing/instrumentation/databases/db2.js index e09a4c0876..c6819d219c 100644 --- a/packages/core/src/tracing/instrumentation/databases/db2.js +++ b/packages/core/src/tracing/instrumentation/databases/db2.js @@ -550,9 +550,12 @@ function finishSpan(ctx, result, span) { if (!closeSyncCalled) { closeSyncCalled = true; span.ec = 1; - // there is a manualr erro string assignment and no error object pressent, - // so we will not replace stack.trace from here span.data.db2.error = `'result.closeSync' was not called within ${CLOSE_TIMEOUT_IN_MS}ms.`; + tracingUtil.setErrorDetails( + span, + new Error(`'result.closeSync' was not called within ${CLOSE_TIMEOUT_IN_MS}ms.`), + 'db2' + ); span.d = Date.now() - span.ts; span.transmit(); } From 91b4d3be9fdba0e4af9c38aa43a48877e00b3080 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 11:17:08 +0530 Subject: [PATCH 32/39] chore: revert redis change, handle in next PR --- .../core/src/tracing/instrumentation/databases/redis.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/instrumentation/databases/redis.js b/packages/core/src/tracing/instrumentation/databases/redis.js index d469b7636f..8e41ad763f 100644 --- a/packages/core/src/tracing/instrumentation/databases/redis.js +++ b/packages/core/src/tracing/instrumentation/databases/redis.js @@ -502,7 +502,13 @@ function instrumentMultiExec(origCtx, origArgs, original, address, isAtomic, cbS if (err) { span.ec = 1; - tracingUtil.setErrorDetails(span, err, 'redis'); + if (err.message) { + span.data.redis.error = err.message; + } else if (Array.isArray(err) && err.length) { + span.data.redis.error = err[0].message; + } else { + span.data.redis.error = 'Unknown error'; + } // v3 = provides sub errors if (err.errors && err.errors.length) { From 13e49d00626ad573733b7c13fe776e54da01c3a1 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 11:24:57 +0530 Subject: [PATCH 33/39] test: updated mysql, pg --- .../test/tracing/databases/mysql/test.js | 2 +- .../test/tracing/databases/pg_native/test.js | 29 +------------------ 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/packages/collector/test/tracing/databases/mysql/test.js b/packages/collector/test/tracing/databases/mysql/test.js index 9f233c8221..6d80793414 100644 --- a/packages/collector/test/tracing/databases/mysql/test.js +++ b/packages/collector/test/tracing/databases/mysql/test.js @@ -348,7 +348,7 @@ function test(env, agentControls) { span => expect(span.data.mysql.error).to.exist ]); - expect(mysqlSpan.stack).to.be.a('string'); + expect(mysqlSpan.stack).to.exist; }) ) )); diff --git a/packages/collector/test/tracing/databases/pg_native/test.js b/packages/collector/test/tracing/databases/pg_native/test.js index d89139226c..61cdb6cd97 100644 --- a/packages/collector/test/tracing/databases/pg_native/test.js +++ b/packages/collector/test/tracing/databases/pg_native/test.js @@ -189,34 +189,6 @@ mochaSuiteFn('tracing/pg-native', function () { ); })); - it('must replace span stack with error stack when error occurs', () => - controls - .sendRequest({ - method: 'POST', - path: '/error', - simple: false - }) - .then(response => { - expect(response).to.exist; - expect(response.error).to.contain('Error: ERROR:'); - expect(response.error).to.contain('relation "nonexistanttable" does not exist'); - - return retry(() => - agentControls.getSpans().then(spans => { - const httpEntry = verifyHttpEntry(spans, '/error'); - const pgExit = expectAtLeastOneMatching(spans, span => { - verifyPgExitBase(span, httpEntry, 'SELECT name, email FROM nonexistanttable'); - expect(span.error).to.not.exist; - expect(span.ec).to.equal(1); - }); - - expect(pgExit.stack).to.be.a('string'); - - verifyHttpExit(spans, httpEntry); - }) - ); - })); - it('must suppress', () => controls .sendRequest({ @@ -318,6 +290,7 @@ mochaSuiteFn('tracing/pg-native', function () { return expectAtLeastOneMatching(spans, span => { verifyPgExitBase(span, parent, statement); expect(span.error).to.not.exist; + expect(span.stack).to.exist; expect(span.ec).to.equal(1); expect(span.data.pg.error).to.contain(errorMessage); }); From 7a90aa750adcde7efed24c61a21101a696615aae Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 13:59:05 +0530 Subject: [PATCH 34/39] chore: update error string limit to 200 --- 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 1e59c1e155..e21948f6ca 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -292,7 +292,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { // This check is for special cases where instrumentation already set a custom value like in httpClient if (!span.data[technology].error) { span.data[technology].error = error.message - ? error.message.substring(0, 100) + ? error.message.substring(0, 200) : // @ts-ignore error?.code || 'No error message found.'; } From 3e531ca79046d4cf99b6d604a273722d85b84300 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 14:33:49 +0530 Subject: [PATCH 35/39] chore: update tracingUtil --- packages/core/src/tracing/tracingUtil.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/tracing/tracingUtil.js b/packages/core/src/tracing/tracingUtil.js index e21948f6ca..95d373ab3b 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -291,10 +291,12 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { if (technology && span.data?.[technology]) { // This check is for special cases where instrumentation already set a custom value like in httpClient if (!span.data[technology].error) { - span.data[technology].error = error.message - ? error.message.substring(0, 200) + const combinedMessage = error?.message + ? `${error.name || 'Error'}: ${error.message}` : // @ts-ignore error?.code || 'No error message found.'; + + span.data[technology].error = combinedMessage.substring(0, 200); } } From f3abfd546d6c35c62bbd28527cc03c53742e7a7d Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 14:38:19 +0530 Subject: [PATCH 36/39] revert: db2 test changes --- packages/collector/test/tracing/databases/db2/test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/collector/test/tracing/databases/db2/test.js b/packages/collector/test/tracing/databases/db2/test.js index 42e535d79b..905c9c62d2 100644 --- a/packages/collector/test/tracing/databases/db2/test.js +++ b/packages/collector/test/tracing/databases/db2/test.js @@ -261,7 +261,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: 'select invalid query', - error: '[IBM][CLI Driver][DB2/LINUXX8664] SQL0104N An unexpected token' + error: 'Error: [IBM][CLI Driver][DB2/LINUXX8664] SQL0104N An unexpected token' }) ) ); @@ -598,7 +598,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: `insert into ${TABLE_NAME_1} (COLINT, COLDATETIME, COLTEXT) VALUES (?, ?, ?)`, - error: '[IBM][CLI Driver][DB2/LINUXX8664] SQL0181N The string representatio' + error: 'Error: [IBM][CLI Driver][DB2/LINUXX8664] SQL0181N The string representatio' }) ) ); @@ -646,7 +646,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: `SELECT * FROM ${TABLE_NAME_1}`, - error: "Cannot read properties of null (reading 'fetchMode')" + error: 'TypeError' }) ) ); @@ -720,7 +720,7 @@ mochaSuiteFn('tracing/db2', function () { testUtils.retry(() => verifySpans(agentControls, controls, { stmt: `insert into ${TABLE_NAME_1}(COLINT, COLDATETIME, COLTEXT) VALUES (?, ?, ?)`, - error: '[IBM][CLI Driver] CLI0100E Wrong number of parameters. SQLSTATE=07001' + error: 'Error: [IBM][CLI Driver] CLI0100E Wrong number of parameters. SQLSTATE=07001' }) ) ); @@ -790,7 +790,7 @@ mochaSuiteFn('tracing/db2', function () { }); it('[with fetch and error] must trace prepare execute mixed 1', function () { - const expectedError = 'Cannot read properties of null (reading'; + const expectedError = 'TypeError: Cannot read properties of null (reading'; return controls .sendRequest({ From 24877646c6cc91ff3a9eff6489aac0d2d0d7c1e7 Mon Sep 17 00:00:00 2001 From: Abhilash Date: Thu, 27 Nov 2025 15:48:00 +0530 Subject: [PATCH 37/39] test: updated assertions --- packages/collector/test/tracing/databases/couchbase/test.js | 2 +- .../test/tracing/messaging/node-rdkafka/test_definition.js | 4 ++-- .../test/tracing/protocols/http/native_fetch/test.js | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/collector/test/tracing/databases/couchbase/test.js b/packages/collector/test/tracing/databases/couchbase/test.js index 24e580b78e..60e99bf384 100644 --- a/packages/collector/test/tracing/databases/couchbase/test.js +++ b/packages/collector/test/tracing/databases/couchbase/test.js @@ -45,7 +45,7 @@ const verifyCouchbaseSpan = (controls, entrySpan, options = {}) => [ span => expect(span.data.couchbase.sql).to.contain(options.sql || 'GET'), span => options.error - ? expect(span.data.couchbase.error).to.equal(options.error) + ? expect(span.data.couchbase.error).to.contain(options.error) : expect(span.data.couchbase.error).to.not.exist ]; diff --git a/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js b/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js index 5fef745288..529928a867 100644 --- a/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js +++ b/packages/collector/test/tracing/messaging/node-rdkafka/test_definition.js @@ -272,10 +272,10 @@ module.exports.run = function ({ span => expect(span.ec).to.equal(!withError ? 0 : 1), span => (!withError ? expect(span.data.kafka.error).to.not.exist : ''), span => - withError === 'deliveryErrorSender' ? expect(span.data.kafka.error).to.equal('delivery fake error') : '', + withError === 'deliveryErrorSender' ? expect(span.data.kafka.error).to.contain('delivery fake error') : '', span => withError === 'bufferErrorSender' - ? expect(span.data.kafka.error).to.equal('Message must be a buffer or null') + ? expect(span.data.kafka.error).to.contain('Message must be a buffer or null') : '' ]); } diff --git a/packages/collector/test/tracing/protocols/http/native_fetch/test.js b/packages/collector/test/tracing/protocols/http/native_fetch/test.js index a6b4748098..78d395efa8 100644 --- a/packages/collector/test/tracing/protocols/http/native_fetch/test.js +++ b/packages/collector/test/tracing/protocols/http/native_fetch/test.js @@ -853,12 +853,11 @@ function verifyHttpExit({ expectedClientError = `Failed to parse URL from http:127.0.0.1:${serverControls.port}malformed-url`; } expectations.push(span => expect(span.data.http.status).to.not.exist); - expectations.push(span => expect(span.data.http.error).to.equal(expectedClientError)); + expectations.push(span => expect(span.data.http.error).to.contain(expectedClientError)); } else if (withTimeout) { expectations.push(span => expect(span.data.http.status).to.not.exist); // Early v18.x Node.js versions had "The operation was aborted", the message later changed to - // "The operation was aborted due to timeout". - expectations.push(span => expect(span.data.http.error).to.match(/^The operation was aborted(?: due to timeout)?/)); + expectations.push(span => expect(span.data.http.error).to.contain('The operation was aborted due to timeout')); } else { expectations.push(span => expect(span.data.http.status).to.equal(status)); expectations.push(span => expect(span.data.http.error).to.not.exist); From 2c9115e740264a28b466e1b8e72eae1695320205 Mon Sep 17 00:00:00 2001 From: Abhilash <70062455+abhilash-sivan@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:44:34 +0530 Subject: [PATCH 38/39] chore: apply suggestion from @kirrg001 Co-authored-by: kirrg001 --- 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 95d373ab3b..323ea0cf8d 100644 --- a/packages/core/src/tracing/tracingUtil.js +++ b/packages/core/src/tracing/tracingUtil.js @@ -289,7 +289,7 @@ exports.setErrorDetails = function setErrorDetails(span, error, technology) { } if (technology && span.data?.[technology]) { - // This check is for special cases where instrumentation already set a custom value like in httpClient + // This extra check allows instrumentations to override the error property (not recommended) if (!span.data[technology].error) { const combinedMessage = error?.message ? `${error.name || 'Error'}: ${error.message}` From 5ac64567ce5289bc1d0fdd0664153a62c92d096d Mon Sep 17 00:00:00 2001 From: Abhilash <70062455+abhilash-sivan@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:46:07 +0530 Subject: [PATCH 39/39] chore: apply suggestion from @kirrg001 Co-authored-by: kirrg001 --- .../core/src/tracing/instrumentation/protocols/httpClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tracing/instrumentation/protocols/httpClient.js b/packages/core/src/tracing/instrumentation/protocols/httpClient.js index 0719f40c0a..7b94593db9 100644 --- a/packages/core/src/tracing/instrumentation/protocols/httpClient.js +++ b/packages/core/src/tracing/instrumentation/protocols/httpClient.js @@ -313,7 +313,7 @@ function instrument(coreModule, forceHttps) { method: clientRequest.method, url: completeCallUrl }; - // Note: Not removing the span.data.http.error assignment because this custom error message is a special-case + // TODO: special-case, we remove this in separate PR span.data.http.error = errorMessage; span.d = Date.now() - span.ts; span.ec = 1;