From 717152bed5f58b7ebaf13bd1b1949696f8f6180c Mon Sep 17 00:00:00 2001 From: Abinet Debele Date: Mon, 30 Sep 2024 10:21:18 -0700 Subject: [PATCH] Rebase and sign commits --- .../tests/errors/index.spec.js | 21 ++++++++++++++++ .../errors/views/minified_file_errors.ejs | 15 ++++++++++++ .../tests/errors/views/script1.min.js | 18 ++++++++++++++ .../tests/errors/views/script2.min.js | 18 ++++++++++++++ .../web/src/SplunkErrorInstrumentation.ts | 24 +++++++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 packages/web/integration-tests/tests/errors/views/minified_file_errors.ejs create mode 100644 packages/web/integration-tests/tests/errors/views/script1.min.js create mode 100644 packages/web/integration-tests/tests/errors/views/script2.min.js diff --git a/packages/web/integration-tests/tests/errors/index.spec.js b/packages/web/integration-tests/tests/errors/index.spec.js index f8952a28..30329ad8 100644 --- a/packages/web/integration-tests/tests/errors/index.spec.js +++ b/packages/web/integration-tests/tests/errors/index.spec.js @@ -175,5 +175,26 @@ module.exports = { await browser.globals.waitForTestToFinish(); browser.assert.not.ok(browser.globals.getReceivedSpans().find(({ name }) => name === 'onerror'), 'Checking lack of error span.'); + }, + 'minified script with source map id': async function(browser) { + await browser.url(browser.globals.getUrl('/errors/views/minified_file_errors.ejs')); + // click on button1 to trigger error + await browser.click('#button1'); + await browser.pause(1000); + const errorSpan1 = await browser.globals.findSpan(s => s.name === 'onerror'); + await browser.assert.ok(!!errorSpan1, 'Checking presence of error span.'); + await browser.assert.strictEqual(errorSpan1.tags['error.message'], 'Error from script1.js'); + // check errorSpan2.tags['error.source_map_ids'] contains the strings script1.min.js and the hash text + await browser.assert.ok(errorSpan1.tags['error.source_map_ids'].includes('script1.min.js')); + await browser.assert.ok(errorSpan1.tags['error.source_map_ids'].includes('9663c60664c425cef3b141c167e9b324240ce10ae488726293892b7130266a6b')); + // clear spans and do same for button2 + browser.globals.clearReceivedSpans(); + await browser.click('#button2'); + await browser.pause(1000); + const errorSpan2 = await browser.globals.findSpan(s => s.name === 'onerror'); + await browser.assert.ok(!!errorSpan2, 'Checking presence of error span.'); + await browser.assert.strictEqual(errorSpan2.tags['error.message'], 'Error from script2.js'); + await browser.assert.ok(errorSpan2.tags['error.source_map_ids'].includes('script2.min.js')); + await browser.assert.ok(errorSpan2.tags['error.source_map_ids'].includes('9793573cdc2ab308a0b1996bea14253ec8832bc036210475ded0813cafa27066')); } }; diff --git a/packages/web/integration-tests/tests/errors/views/minified_file_errors.ejs b/packages/web/integration-tests/tests/errors/views/minified_file_errors.ejs new file mode 100644 index 00000000..49a54a15 --- /dev/null +++ b/packages/web/integration-tests/tests/errors/views/minified_file_errors.ejs @@ -0,0 +1,15 @@ + + + + + Errors test + <%- renderAgent({}) %> + + +

Minified files with source map ids

+ + + + + + diff --git a/packages/web/integration-tests/tests/errors/views/script1.min.js b/packages/web/integration-tests/tests/errors/views/script1.min.js new file mode 100644 index 00000000..6a5b3c2f --- /dev/null +++ b/packages/web/integration-tests/tests/errors/views/script1.min.js @@ -0,0 +1,18 @@ +/* +Copyright 2024 Splunk Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +document.getElementById('button1').addEventListener('click',function(){throw new Error('Error from script1.js');}); +window.sourceMapIds = window.sourceMapIds || {}; let s = ''; try { throw new Error(); } catch (e) { s = (e.stack.match(/https?:\/\/[^\s]+?(?::\d+)?(?=:[\d]+:[\d]+)/) || [])[0]; } if (s) {window.sourceMapIds[s] = '9663c60664c425cef3b141c167e9b324240ce10ae488726293892b7130266a6b';} \ No newline at end of file diff --git a/packages/web/integration-tests/tests/errors/views/script2.min.js b/packages/web/integration-tests/tests/errors/views/script2.min.js new file mode 100644 index 00000000..a2ba7de7 --- /dev/null +++ b/packages/web/integration-tests/tests/errors/views/script2.min.js @@ -0,0 +1,18 @@ +/* +Copyright 2024 Splunk Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +document.getElementById('button2').addEventListener('click',function(){throw new Error('Error from script2.js');}); +window.sourceMapIds = window.sourceMapIds || {}; let s = ''; try { throw new Error(); } catch (e) { s = (e.stack.match(/https?:\/\/[^\s]+?(?::\d+)?(?=:[\d]+:[\d]+)/) || [])[0]; } if (s) {window.sourceMapIds[s] = '9793573cdc2ab308a0b1996bea14253ec8832bc036210475ded0813cafa27066';} diff --git a/packages/web/src/SplunkErrorInstrumentation.ts b/packages/web/src/SplunkErrorInstrumentation.ts index ae5d6444..fc51a84a 100644 --- a/packages/web/src/SplunkErrorInstrumentation.ts +++ b/packages/web/src/SplunkErrorInstrumentation.ts @@ -37,9 +37,33 @@ function stringifyValue(value: unknown) { return value.toString(); } +function parseErrorStack(stack: string): string { + //get list of files in stack , find corresponding sourcemap id and add it to the source map id object + const sourceMapIds = {}; + const urlPattern = /(https?:\/\/[^\s]+\/[^\s:]+|\/[^\s:]+)/g; + const urls = stack.match(urlPattern); + if (urls) { + urls.forEach(url => { + // Strip off any line/column numbers at the end after the last colon + const cleanedUrl = url.split(/:(?=\d+$)/)[0]; + const globalSourceMapIds = (window as any).sourceMapIds; + if(globalSourceMapIds && globalSourceMapIds[cleanedUrl] && !sourceMapIds[cleanedUrl]) { + sourceMapIds[cleanedUrl] = globalSourceMapIds[cleanedUrl]; + } + }); + } + return JSON.stringify(sourceMapIds); +} + function addStackIfUseful(span: Span, err: Error) { + if (err && err.stack && useful(err.stack)) { + //get sourcemap ids and add to span as error.soruce_map_ids span.setAttribute('error.stack', limitLen(err.stack.toString(), STACK_LIMIT)); + const sourcemapIds = parseErrorStack(err.stack); + if (sourcemapIds) { + span.setAttribute('error.source_map_ids', sourcemapIds); + } } }