From f43a62bfcfaf0dfa976e515775dc5dd336a00197 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 10 Oct 2024 09:08:38 -0400 Subject: [PATCH] Fixed #776 by updating logic in loggerStackTrace.js to better handle parsing when lightning debug mode is disabled Previously, stack traces worked when debug mode was enabled, but was inaccurate when debug mode was off due to some subtle differences in the generated stack traces Also incorporated some code review feedback from @jamessimone Also removed opera stack trace parsing in loggerStackTrace.js - there was no test coverage for this, I haven't heard of anyone using Opera in years, and I just... don't want to have to test another browser. If/when someone says Opera support is needed, it can be revisited[D --- README.md | 2 +- docs/apex/Logger-Engine/ComponentLogger.md | 4 +- docs/lightning-components/LogEntryBuilder.md | 162 ++++++++++-------- docs/lightning-components/Logger.md | 78 ++++----- ...json => chromeBrowserError_debugMode.json} | 0 .../chromeBrowserError_withoutDebugMode.json | 3 + ...r.json => edgeBrowserError_debugMode.json} | 0 .../edgeBrowserError_withoutDebugMode.json | 3 + ...son => firefoxBrowserError_debugMode.json} | 0 .../firefoxBrowserError_withoutDebugMode.json | 3 + .../logger/__tests__/loggerStackTrace.test.js | 51 +++++- .../logger-engine/lwc/logger/loggerService.js | 20 +-- .../lwc/logger/loggerStackTrace.js | 88 +--------- 13 files changed, 182 insertions(+), 232 deletions(-) rename nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/{chromeBrowserError.json => chromeBrowserError_debugMode.json} (100%) create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json rename nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/{edgeBrowserError.json => edgeBrowserError_debugMode.json} (100%) create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json rename nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/{firefoxBrowserError.json => firefoxBrowserError_debugMode.json} (100%) create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json diff --git a/README.md b/README.md index cf44b1810..7e17876c4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build](https://github.com/jongpie/NebulaLogger/actions/workflows/build.yml/badge.svg)](https://github.com/jongpie/NebulaLogger/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/jongpie/NebulaLogger/branch/main/graph/badge.svg?token=1DJPDRM3N4)](https://codecov.io/gh/jongpie/NebulaLogger) -The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, Process Builder & integrations. +The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, OmniStudio, and integrations. ## Unlocked Package - v4.14.13 diff --git a/docs/apex/Logger-Engine/ComponentLogger.md b/docs/apex/Logger-Engine/ComponentLogger.md index be7b38dbc..6958efa6b 100644 --- a/docs/apex/Logger-Engine/ComponentLogger.md +++ b/docs/apex/Logger-Engine/ComponentLogger.md @@ -55,7 +55,7 @@ return The transaction ID (based on `Logger.getTransactionId())` ### Inner Classes -#### ComponentLogger.ComponentBrowser class +#### ComponentLogger.ComponentBrowserContext class A DTO object used to log details about the user's browser @@ -123,7 +123,7 @@ A DTO object used to create log entries for lightning components ##### Properties -###### `browser` → `ComponentBrowser` +###### `browser` → `ComponentBrowserContext` Context about the user's browser, automatically captured by Nebula Logger diff --git a/docs/lightning-components/LogEntryBuilder.md b/docs/lightning-components/LogEntryBuilder.md index 1bee4c7a4..5c4fe4140 100644 --- a/docs/lightning-components/LogEntryBuilder.md +++ b/docs/lightning-components/LogEntryBuilder.md @@ -1,89 +1,101 @@ - - -## LogEntryBuilder - -**Kind**: global class - -- [LogEntryBuilder](#LogEntryBuilder) - - [new LogEntryBuilder(loggingLevel)](#new_LogEntryBuilder_new) - - [.setMessage(message)](#LogEntryBuilder+setMessage) [LogEntryBuilder](#LogEntryBuilder) - - [.setRecordId(recordId)](#LogEntryBuilder+setRecordId) [LogEntryBuilder](#LogEntryBuilder) - - [.setRecord(record)](#LogEntryBuilder+setRecord) [LogEntryBuilder](#LogEntryBuilder) - - [.setScenario(scenario)](#LogEntryBuilder+setScenario) [LogEntryBuilder](#LogEntryBuilder) - - [.setError(error)](#LogEntryBuilder+setError) [LogEntryBuilder](#LogEntryBuilder) - - [.setExceptionDetails(exception)](#LogEntryBuilder+setExceptionDetails) [LogEntryBuilder](#LogEntryBuilder) - - [.setField(fieldToValue)](#LogEntryBuilder+setField) [LogEntryBuilder](#LogEntryBuilder) - - [.parseStackTrace(originStackTraceError)](#LogEntryBuilder+parseStackTrace) [LogEntryBuilder](#LogEntryBuilder) - - [.addTag(tag)](#LogEntryBuilder+addTag) [LogEntryBuilder](#LogEntryBuilder) - - [.addTags(tags)](#LogEntryBuilder+addTags) [LogEntryBuilder](#LogEntryBuilder) - - [.getComponentLogEntry()](#LogEntryBuilder+getComponentLogEntry) ComponentLogEntry - - - -### new LogEntryBuilder(loggingLevel) - -Constructor used to generate each JavaScript-based log entry event -This class is the JavaScript-equivalent of the Apex class `LogEntryBuilder` - -| Param | Type | Description | -| ------------ | ------------------- | ------------------------------------------------------------------------------- | -| loggingLevel | String | The `LoggingLevel` enum to use for the builder's instance of `LogEntryEvent__e` | - - - -### logEntryBuilder.setMessage(message) [LogEntryBuilder](#LogEntryBuilder) +## Functions + +
+
setMessage(message) LogEntryBuilder
+

Sets the log entry event's message field

+
+
setRecordId(recordId) LogEntryBuilder
+

Sets the log entry event's record fields

+
+
setRecord(record) LogEntryBuilder
+

Sets the log entry event's record fields

+
+
setScenario(scenario) LogEntryBuilder
+

Sets the log entry event's scenario field

+
+
setError(error) LogEntryBuilder
+

Deprecated - use setExceptionDetails(exception) instead + The name of this method is very similar to the logger function logger.error(), + resulting in confusion when used together: + logger.error('Unexpected error').setError(someErrorObject); + The new setExceptionDetails(exception) function provides the exact same functionality, + but aligns with the Apex builder's method name, and helps reduce the confusion with logger.error()

+
+
setExceptionDetails(exception) LogEntryBuilder
+

Sets the log entry event's exception fields

+
+
setField(fieldToValue) LogEntryBuilder
+

Sets multiple field values on the builder's LogEntryEvent__e record

+
+
parseStackTrace(originStackTraceError) LogEntryBuilder
+

Parses the provided error's stack trace and sets the log entry's origin & stack trace fields

+
+
addTag(tag) LogEntryBuilder
+

Appends the tag to the existing list of tags

+
+
addTags(tags) LogEntryBuilder
+

Appends the tag to the existing list of tags

+
+
getComponentLogEntry() ComponentLogEntry
+

Returns the object used to save log entry data

+
+
+ + + +## setMessage(message) LogEntryBuilder Sets the log entry event's message field -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ------- | ------------------- | -------------------------------------------------- | | message | String | The string to use to set the entry's message field | - + -### logEntryBuilder.setRecordId(recordId) [LogEntryBuilder](#LogEntryBuilder) +## setRecordId(recordId) LogEntryBuilder Sets the log entry event's record fields -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | -------- | ------------------- | ------------------------------------------------- | | recordId | String | The ID of the SObject record related to the entry | - + -### logEntryBuilder.setRecord(record) [LogEntryBuilder](#LogEntryBuilder) +## setRecord(record) LogEntryBuilder Sets the log entry event's record fields -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ------ | ------------------- | ----------------------------------------------------------------------------------------------------- | | record | Object | The `SObject` record related to the entry. The JSON of the record is automatically added to the entry | - + -### logEntryBuilder.setScenario(scenario) [LogEntryBuilder](#LogEntryBuilder) +## setScenario(scenario) LogEntryBuilder Sets the log entry event's scenario field -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | -------- | ------------------- | --------------------------------------------------- | | scenario | String | The string to use to set the entry's scenario field | - + -### logEntryBuilder.setError(error) [LogEntryBuilder](#LogEntryBuilder) +## setError(error) LogEntryBuilder Deprecated - use `setExceptionDetails(exception)` instead The name of this method is very similar to the logger function logger.error(), @@ -92,83 +104,83 @@ resulting in confusion when used together: The new `setExceptionDetails(exception)` function provides the exact same functionality, but aligns with the Apex builder's method name, and helps reduce the confusion with `logger.error()` -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ----- | ------------------ | -------------------------------------------------------------------------------- | | error | Error | The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use | - + -### logEntryBuilder.setExceptionDetails(exception) [LogEntryBuilder](#LogEntryBuilder) +## setExceptionDetails(exception) LogEntryBuilder Sets the log entry event's exception fields -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | --------- | ------------------ | -------------------------------------------------------------------------------- | | exception | Error | The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use | - + -### logEntryBuilder.setField(fieldToValue) [LogEntryBuilder](#LogEntryBuilder) +## setField(fieldToValue) LogEntryBuilder Sets multiple field values on the builder's `LogEntryEvent__e` record -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | fieldToValue | Object | An object containing the custom field name as a key, with the corresponding value to store. Example: `{"SomeField__c": "some value", "AnotherField__c": "another value"}` | - + -### logEntryBuilder.parseStackTrace(originStackTraceError) [LogEntryBuilder](#LogEntryBuilder) +## parseStackTrace(originStackTraceError) LogEntryBuilder Parses the provided error's stack trace and sets the log entry's origin & stack trace fields -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | --------------------- | ------------------ | ----------------------------------------------------------------------- | | originStackTraceError | Error | The instance of a JavaScript `Error` object with a stack trace to parse | - + -### logEntryBuilder.addTag(tag) [LogEntryBuilder](#LogEntryBuilder) +## addTag(tag) LogEntryBuilder Appends the tag to the existing list of tags -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ----- | ------------------- | ---------------------------------------------------- | | tag | String | The string to add as a tag for the current log entry | - + -### logEntryBuilder.addTags(tags) [LogEntryBuilder](#LogEntryBuilder) +## addTags(tags) LogEntryBuilder Appends the tag to the existing list of tags -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ----- | --------------------------------- | -------------------------------------------------------- | | tags | Array.<String> | The list of strings to add as tags for the current entry | - + -### logEntryBuilder.getComponentLogEntry() ComponentLogEntry +## getComponentLogEntry() ComponentLogEntry Returns the object used to save log entry data -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) +**Kind**: global function **Returns**: ComponentLogEntry - An instance of `ComponentLogEntry` that matches the Apex class `ComponentLogger.ComponentLogEntry` diff --git a/docs/lightning-components/Logger.md b/docs/lightning-components/Logger.md index 563a2d25c..8bfc5866c 100644 --- a/docs/lightning-components/Logger.md +++ b/docs/lightning-components/Logger.md @@ -1,21 +1,3 @@ -## Constants - -
-
createLogger Promise.<LoggerService>
-

Deprecated - use getLogger() instead - Async function that returns a fully-loaded logger service. - Requires some code to be executed async, so the service is not immediately available. - Example: const logger = await createLogger();

-
-
getLogger LoggerService
-

Recommended approach - Synchronous function that returns a ready-to-use logger service. - Internally, some code is still executed async, but the service can immediately - be used, without awaiting a Promise - Example: const logger = getLogger();

-
-
- ## Functions
@@ -61,31 +43,21 @@

Saves any entries in Logger's buffer, using the specified save method for only this call All subsequent calls to saveLog() will use the transaction save method

+
createLogger() Promise.<LoggerService>
+

Deprecated - use getLogger() instead + Async function that returns a fully-loaded logger service. + Requires some code to be executed async, so the service is not immediately available. + Example: const logger = await createLogger();

+
+
getLogger() LoggerService
+

Recommended approach + Synchronous function that returns a ready-to-use logger service. + Internally, some code is still executed async, but the service can immediately + be used, without awaiting a Promise + Example: const logger = getLogger();

+
- - -## createLogger Promise.<LoggerService> - -Deprecated - use `getLogger()` instead -Async function that returns a fully-loaded logger service. -Requires some code to be executed async, so the service is not immediately available. -Example: `const logger = await createLogger();` - -**Kind**: global constant -**Returns**: Promise.<LoggerService> - A Promise that resolves an instance of `LoggerService` - - -## getLogger LoggerService - -Recommended approach -Synchronous function that returns a ready-to-use logger service. -Internally, some code is still executed async, but the service can immediately -be used, without awaiting a Promise -Example: `const logger = getLogger();` - -**Kind**: global constant -**Returns**: LoggerService - An instance of `LoggerService` ## getUserSettings() ComponentLogger.ComponentLoggerSettings @@ -240,3 +212,27 @@ All subsequent calls to saveLog() will use the transaction save method | Param | Type | Description | | ---------- | ------------------- | ------------------------------------------------------------------------ | | saveMethod | String | The enum value of Logger.SaveMethod to use for this specific save action | + + + +## createLogger() Promise.<LoggerService> + +Deprecated - use `getLogger()` instead +Async function that returns a fully-loaded logger service. +Requires some code to be executed async, so the service is not immediately available. +Example: `const logger = await createLogger();` + +**Kind**: global function +**Returns**: Promise.<LoggerService> - A Promise that resolves an instance of `LoggerService` + + +## getLogger() LoggerService + +Recommended approach +Synchronous function that returns a ready-to-use logger service. +Internally, some code is still executed async, but the service can immediately +be used, without awaiting a Promise +Example: `const logger = getLogger();` + +**Kind**: global function +**Returns**: LoggerService - An instance of `LoggerService` diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_debugMode.json similarity index 100% rename from nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError.json rename to nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_debugMode.json diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json new file mode 100644 index 000000000..3dc8cc4ad --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json @@ -0,0 +1,3 @@ +{ + "stack": "Error\n at Proxy.info (modules/c/logger.js:3:10548)\n at eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:23715)\n at en. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:67030)\n at mo (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:43179)\n at c. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:21644)\n at a. [as info] (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:542:412)\n at https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:85471\n at en.eval [as applyTrapForOneOrMoreArgs] (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4751)\n at en.eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4255)\n at saveLogExample (https://dream-ability-7823.scratch.lightning.force.com/lightning/page/components/c/loggerAuraEmbedDemo.js:14:12)" +} diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_debugMode.json similarity index 100% rename from nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError.json rename to nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_debugMode.json diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json new file mode 100644 index 000000000..09d45d611 --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json @@ -0,0 +1,3 @@ +{ + "stack": "Error\nat Proxy.info (modules/c/logger.js:3:11782)\nat eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:23715)\nat en. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:67030)\nat mo (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:43179)\nat c. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:21644)\nat a.info (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:542:412)\nat https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:85471\nat en.eval [as applyTrapForOneOrMoreArgs] (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4751)\nat en.eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4255)\nat saveLogExample (https://dream-ability-7823.scratch.lightning.force.com/lightning/page/components/c/loggerAuraEmbedDemo.js:14:12)" +} diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_debugMode.json similarity index 100% rename from nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError.json rename to nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_debugMode.json diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json new file mode 100644 index 000000000..f4624442f --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json @@ -0,0 +1,3 @@ +{ + "stack": "info@modules/c/logger.js:3:11782\nzE/ eval:3:23715\nVt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:67030\nmo@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:43179\nSn/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:21644\nR.prototype.hi/ eval:3:4751\nRt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js line 105 > eval:3:4255\nsaveLogExample@https://dream-ability-7823.scratch.lightning.force.com/lightning/page/components/c/loggerAuraEmbedDemo.js:14:12\nzE/ eval:3:23715\nHt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:68352\nGt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:66429\nK.prototype.Zc@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:628:177\nK.prototype.Xa@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:627:306\nhandleClick@https://static.lightning.force.com/cs226/aurafile/%7B%22mode%22%3A%22PROD%22%2C%22cac%22%3A1%2C%22app%22%3A%22one%3Aone%22%2C%22ls%22%3A1%2C%22lrmc%22%3A%22-386269907%22%7D/pIFDZ5nqs77gFqsVXe-2Jw/apppart1-4.js:980:6\nK.prototype.Zc@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:628:177\nuj/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:514:349\nD.Xa@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:994:221\nuj@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:514:333\nXK.prototype.zr@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:1064:402\nZD.prototype.Xl@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:381:255\n$D/b<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:380:130\nBq@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:514:301\nb@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:990:72\nt@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:28:5715\nEventListener.handleEvent*ar@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:46:5450\nM.prototype.uk { jest.clearAllMocks(); }); - it('correctly parses Chrome stack trace', async () => { + it('correctly parses Chrome stack trace when debug mode is enabled', async () => { const loggerStackTrace = new LoggerStackTrace(); - const originStackTrace = loggerStackTrace.parse(CHROME_BROWSER_ERROR); + const originStackTrace = loggerStackTrace.parse(CHROME_BROWSER_ERROR_DEBUG_MODE); expect(originStackTrace.componentName).toEqual('c/loggerChromeLWCEmbedDemo'); expect(originStackTrace.functionName).toEqual('logInfoExample'); expect(originStackTrace.metadataType).toEqual('LightningComponentBundle'); }); - it('correctly parses Edge stack trace', async () => { + it('correctly parses Chrome stack trace when debug mode is disabled', async () => { const loggerStackTrace = new LoggerStackTrace(); - const originStackTrace = loggerStackTrace.parse(EDGE_BROWSER_ERROR); + const originStackTrace = loggerStackTrace.parse(CHROME_BROWSER_ERROR_WITHOUT_DEBUG_MODE); + + expect(originStackTrace.componentName).toEqual('c/loggerAuraEmbedDemo'); + expect(originStackTrace.functionName).toEqual('saveLogExample'); + expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); + }); + + it('correctly parses Edge stack trace when debug mode is enabled', async () => { + const loggerStackTrace = new LoggerStackTrace(); + + const originStackTrace = loggerStackTrace.parse(EDGE_BROWSER_ERROR_DEBUG_MODE); expect(originStackTrace.componentName).toEqual('c/loggerEdgeAuraEmbedDemo'); expect(originStackTrace.functionName).toEqual('saveLogExample'); expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); }); - it('correctly parses Firefox stack trace', async () => { + it('correctly parses Edge stack trace when debug mode is disabled', async () => { const loggerStackTrace = new LoggerStackTrace(); - const originStackTrace = loggerStackTrace.parse(FIREFOX_BROWSER_ERROR); + const originStackTrace = loggerStackTrace.parse(EDGE_BROWSER_ERROR_WITHOUT_DEBUG_MODE); + + expect(originStackTrace.componentName).toEqual('c/loggerAuraEmbedDemo'); + expect(originStackTrace.functionName).toEqual('saveLogExample'); + expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); + }); + + it('correctly parses Firefox stack trace when debug mode is enabled', async () => { + const loggerStackTrace = new LoggerStackTrace(); + + const originStackTrace = loggerStackTrace.parse(FIREFOX_BROWSER_ERROR_DEBUG_MODE); expect(originStackTrace.componentName).toEqual('c/loggerFirefoxLWCImportDemo'); expect(originStackTrace.functionName).toEqual('logInfoExample'); expect(originStackTrace.metadataType).toEqual('LightningComponentBundle'); }); + + it('correctly parses Firefox stack trace when debug mode is disabled', async () => { + const loggerStackTrace = new LoggerStackTrace(); + + const originStackTrace = loggerStackTrace.parse(FIREFOX_BROWSER_ERROR_WITHOUT_DEBUG_MODE); + + expect(originStackTrace.componentName).toEqual('c/loggerAuraEmbedDemo'); + expect(originStackTrace.functionName).toEqual('saveLogExample'); + expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); + }); }); diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js index 4be44675a..5ccfa3f66 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js @@ -202,27 +202,9 @@ export default class LoggerService { /* eslint-disable no-console */ _logToConsole(loggingLevel, message, componentLogEntry) { - let consoleLoggingFunction; - switch (loggingLevel) { - case 'ERROR': - consoleLoggingFunction = console.error; - break; - case 'WARN': - consoleLoggingFunction = console.warn; - break; - case 'INFO': - consoleLoggingFunction = console.info; - break; - default: - consoleLoggingFunction = console.debug; - break; - } - + const consoleLoggingFunction = console[loggingLevel.toLowerCase()] ?? console.debug; const loggingLevelEmoji = LOGGING_LEVEL_EMOJIS[loggingLevel]; const qualifiedMessage = `${loggingLevelEmoji} ${loggingLevel}: ${message}`; - // Some JS stack traces are huuuuge, so don't print it in the browser console. - // The stack trace will still be saved on the backend. - // '\n' + JSON.stringify(this.#componentLogEntry, replacer, 2) const formattedComponentLogEntryString = !componentLogEntry ? '' : '\n' + diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js index b0a994b6d..982a5ffa6 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js @@ -44,16 +44,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/; const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m; const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/; class ErrorStackParser { parse(error) { let stackTraceParticles; - if (typeof error.stacktrace !== 'undefined' || typeof error['opera#sourceloc'] !== 'undefined') { - stackTraceParticles = this.parseOpera(error); - } else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) { + if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) { stackTraceParticles = this.parseV8OrIE(error); } else if (error.stack) { stackTraceParticles = this.parseFFOrSafari(error); @@ -146,82 +143,6 @@ class ErrorStackParser { }; }, this); } - - parseOpera(e) { - if (!e.stacktrace || (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length)) { - return this.parseOpera9(e); - } else if (!e.stack) { - return this.parseOpera10(e); - } - return this.parseOpera11(e); - } - - parseOpera9(e) { - const lineRE = /Line (\d+).*script (?:in )?(\S+)/i; - const lines = e.message.split('\n'); - const result = []; - - for (let i = 2, len = lines.length; i < len; i += 2) { - const match = lineRE.exec(lines[i]); - if (match) { - result.push({ - fileName: match[2], - lineNumber: match[1], - source: lines[i] - }); - } - } - - return result; - } - - parseOpera10(e) { - const lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i; - const lines = e.stacktrace.split('\n'); - const result = []; - - for (let i = 0, len = lines.length; i < len; i += 2) { - const match = lineRE.exec(lines[i]); - if (match) { - result.push({ - functionName: match[3] || undefined, - fileName: match[2], - lineNumber: match[1], - source: lines[i] - }); - } - } - - return result; - } - - // Opera 10.65+ Error.stack very similar to FF/Safari - parseOpera11(error) { - const filtered = error.stack.split('\n').filter(function (line) { - return !!line.match(FIREFOX_SAFARI_STACK_REGEXP) && !line.match(/^Error created at/); - }, this); - - return filtered.map(function (line) { - const tokens = line.split('@'); - const locationParts = this.extractLocation(tokens.pop()); - const functionCall = tokens.shift() || ''; - const functionName = functionCall.replace(//, '$2').replace(/\([^)]*\)/g, '') || undefined; - let argsRaw; - if (functionCall.match(/\(([^)]*)\)/)) { - argsRaw = functionCall.replace(/^[^(]+\(([^)]*)\)$/, '$1'); - } - const args = argsRaw === undefined || argsRaw === '[arguments not available]' ? undefined : argsRaw.split(','); - - return { - functionName: functionName, - args: args, - fileName: locationParts[0], - lineNumber: locationParts[1], - columnNumber: locationParts[2], - source: line - }; - }, this); - } } /* End of code originally copied from stacktrace.js */ @@ -243,11 +164,8 @@ export default class LoggerStackTrace { return; } - if (!originStackTraceParticle && currentStackTraceParticle.fileName?.endsWith('aura_proddebug.js')) { - return; - } - - if (!originStackTraceParticle && currentStackTraceParticle.fileName?.endsWith('aura_proddebug')) { + const ignoredAuraFilenamesRegEx = /aura_prod(?:\.js|debug(?:\.js)?)$/; + if (!originStackTraceParticle && ignoredAuraFilenamesRegEx.test(currentStackTraceParticle.fileName)) { return; }