Skip to content

Commit

Permalink
Bugfix for broken LWC stack trace parsing (#614)
Browse files Browse the repository at this point in the history
* Fixed ComponentLogger.cls so it handles recent-ish changes to the format of stack traces in LWCs

* Suppressed a 'PropertyNamingConventions' PMD violation in ComponentLogger.cls due to a new lazy-loaded constant

* Also fixed a System.debug() call in Logger.cls to just use 'Organization' instead of 'Schema.Organization' (caused by an overeager string replacement in a previous release)
  • Loading branch information
jongpie authored Jan 28, 2024
1 parent 50449ff commit d452edd
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 20 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.

## Unlocked Package - v4.12.6
## Unlocked Package - v4.12.7

[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Mk5UQAS)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Mk5UQAS)
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Mk6IQAS)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Mk6IQAS)
[![View Documentation](./images/btn-view-documentation.png)](https://jongpie.github.io/NebulaLogger/)

`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001Mk5UQAS`
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001Mk6IQAS`

`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001Mk5UQAS`
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001Mk6IQAS`

---

Expand Down
18 changes: 14 additions & 4 deletions nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,19 @@
* @see Logger
* @see LogEntryEventBuilder
*/
@SuppressWarnings('PMD.ExcessivePublicCount, PMD.StdCyclomaticComplexity')
@SuppressWarnings('PMD.ExcessivePublicCount, PMD.PropertyNamingConventions, PMD.StdCyclomaticComplexity')
public inherited sharing class ComponentLogger {
private static final String LOGGER_COMPONENT_NAME {
get {
if (LOGGER_COMPONENT_NAME == null) {
String namespace = String.isNotBlank(Logger.getNamespacePrefix()) ? Logger.getNamespacePrefix() : 'c';
LOGGER_COMPONENT_NAME = '/' + namespace + '/logger.js';
}
return LOGGER_COMPONENT_NAME;
}
set;
}

static {
Logger.ignoreOrigin(ComponentLogger.class);
}
Expand Down Expand Up @@ -90,14 +101,13 @@ public inherited sharing class ComponentLogger {
Boolean isAuraComponent = false;

if (String.isNotBlank(stackTraceString)) {
String loggerComponentReferenceToIgnore = '/c/logger.js';
String auraComponentContent = '/n/components/';
String lwcModuleContent = '/n/modules/';
String lwcModuleContent = 'modules/';
Boolean parentComponentFound = false;

List<String> stackTraceLines = new List<String>();
for (String currentStackTraceLine : stackTraceString.split('\n')) {
if (currentStackTraceLine.contains(loggerComponentReferenceToIgnore)) {
if (currentStackTraceLine.contains(LOGGER_COMPONENT_NAME)) {
continue;
} else if (parentComponentFound == false && currentStackTraceLine.contains(auraComponentContent)) {
isAuraComponent = true;
Expand Down
4 changes: 2 additions & 2 deletions nebula-logger/core/main/logger-engine/classes/Logger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
global with sharing class Logger {
// There's no reliable way to get the version number dynamically in Apex
@TestVisible
private static final String CURRENT_VERSION_NUMBER = 'v4.12.6';
private static final String CURRENT_VERSION_NUMBER = 'v4.12.7';
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
private static final Set<String> IGNORED_APEX_CLASSES = initializeIgnoredApexClasses();
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
Expand Down Expand Up @@ -109,7 +109,7 @@ global with sharing class Logger {
// The rest of the codebase should use a method in Logger.cls
System.debug(System.LoggingLevel.INFO, 'Nebula Logger - Version Number: ' + getVersionNumber());
System.debug(System.LoggingLevel.INFO, 'Nebula Logger - Transaction ID: ' + getTransactionId());
System.debug(System.LoggingLevel.INFO, 'Nebula Logger - Schema.Organization API Version: ' + getOrganizationApiVersion());
System.debug(System.LoggingLevel.INFO, 'Nebula Logger - Organization API Version: ' + getOrganizationApiVersion());
setScenario(getUserSettings().DefaultScenario__c);
}

Expand Down
2 changes: 1 addition & 1 deletion nebula-logger/core/main/logger-engine/lwc/logger/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { LightningElement, api } from 'lwc';
import { createLoggerService } from './loggerService';

const CURRENT_VERSION_NUMBER = 'v4.12.6';
const CURRENT_VERSION_NUMBER = 'v4.12.7';

export default class Logger extends LightningElement {
#loggerService = createLoggerService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,49 @@ private class ComponentLogger_Tests {
}

@IsTest
static void it_should_parse_web_component_stack_trace() {
static void it_should_parse_web_component_stack_trace_when_stack_trace_is_modern_format() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
String expectedComponentType = 'LWC';
String expectedComponentApiName = 'c/loggerLWCDemo';
String expectedComponentFunctionName = 'logErrorExample';
ComponentLogger.ComponentLogEntry componentLogEntry = createMockComponentLogEntry();
componentLogEntry.stack = getMockModernWebComponentStackTrace();
System.Assert.areEqual(0, Logger.saveLogCallCount);
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishCallCount());
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());

ComponentLogger.saveComponentLogEntries(new List<ComponentLogger.ComponentLogEntry>{ componentLogEntry }, null);

System.Assert.areEqual(1, Logger.saveLogCallCount);
System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishCallCount());
System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
LogEntryEvent__e publishedLogEntryEvent = (LogEntryEvent__e) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0);
System.Assert.areEqual('Component', publishedLogEntryEvent.OriginType__c);
System.Assert.areEqual(componentLogEntry.browserFormFactor, publishedLogEntryEvent.BrowserFormFactor__c);
System.Assert.areEqual(componentLogEntry.browserLanguage, publishedLogEntryEvent.BrowserLanguage__c);
System.Assert.areEqual(componentLogEntry.browserScreenResolution, publishedLogEntryEvent.BrowserScreenResolution__c);
System.Assert.areEqual(componentLogEntry.browserUrl, publishedLogEntryEvent.BrowserUrl__c);
System.Assert.areEqual(componentLogEntry.browserUserAgent, publishedLogEntryEvent.BrowserUserAgent__c);
System.Assert.areEqual(componentLogEntry.browserWindowResolution, publishedLogEntryEvent.BrowserWindowResolution__c);
System.Assert.areEqual(componentLogEntry.loggingLevel, publishedLogEntryEvent.LoggingLevel__c);
System.Assert.areEqual(componentLogEntry.message, publishedLogEntryEvent.Message__c);
System.Assert.areEqual('Component', publishedLogEntryEvent.OriginType__c);
System.Assert.areEqual(expectedComponentApiName + '.' + expectedComponentFunctionName, publishedLogEntryEvent.OriginLocation__c);
// TODO Move these asserts to LogEntryHandler_Tests
// System.Assert.areEqual(expectedComponentApiName, publishedLogEntryEvent.ComponentApiName__c);
// System.Assert.areEqual(expectedComponentFunctionName, publishedLogEntryEvent.ComponentFunctionName__c);
System.Assert.areEqual(expectedComponentType, publishedLogEntryEvent.ComponentType__c);
System.Assert.areEqual(componentLogEntry.timestamp, publishedLogEntryEvent.Timestamp__c);
}

@IsTest
static void it_should_parse_web_component_stack_trace_when_stack_trace_is_legacy_format() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
String expectedComponentType = 'LWC';
String expectedComponentApiName = 'c/loggerLWCDemo';
String expectedComponentFunctionName = 'saveLogWebExample';
ComponentLogger.ComponentLogEntry componentLogEntry = createMockComponentLogEntry();
componentLogEntry.stack = getMockWebComponentStackTrace();
componentLogEntry.stack = getMockLegacyWebComponentStackTrace();
System.Assert.areEqual(0, Logger.saveLogCallCount);
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishCallCount());
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
Expand Down Expand Up @@ -422,8 +458,39 @@ private class ComponentLogger_Tests {
'\nloggerAuraDemo.js:17:17';
}

private static String getMockWebComponentStackTrace() {
// This is a copy of an actual stack trace generated from c/loggerLWCDemo
private static String getMockModernWebComponentStackTrace() {
// This is a partial copy of an actual stack trace generated from c/loggerLWCDemo in January 2024
List<String> lwcStackTraceLines = new List<String>{
'@modules/c/logger.js:33:22',
'LogEntryBuilder@modules/c/logger.js:59:87',
'newLogEntry@modules/c/logger.js:189:14',
'_newEntry@modules/c/logger.js:397:44',
'error@modules/c/logger.js:264:21',
'logErrorExample@modules/c/loggerLWCDemo.js:266:35',
'createHooksCallback$LWS/<@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js line 35465 > eval:2583:24',
'applyOrConstructTrapForTwoOrMoreArgs$LWS@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:32254:38',
'callHook@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:10035:19',
'invokeEventListener/<@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:9926:21',
'runWithBoundaryProtection@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:10517:13',
'invokeEventListener@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:9921:34',
'b/<@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:9365:32',
'handleEvent@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:1615:51',
'invokeListenersByPlacement/<@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:1660:46',
'invokeListenersByPlacement@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:1655:21',
'domListener@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:1670:39',
'EventListener.handleEvent*attachDOMListener@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:1687:30',
'addCustomElementEventListener@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:1712:30',
'patchedAddEventListener@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:3022:50',
'value@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:5567:53',
'addEventListener@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:11554:20',
'applyEventListeners@https://static.lightning.force.com/cs220/auraFW/javascript/YWYyQV90T3g3VDhySzNWUm1kcF9WUVY4bi1LdGdMbklVbHlMdER1eVVlUGcyNDYuMTUuNS0zLjAuNA/aura_proddebug.js:8200:29',
'...blah,blah,blah more javascript code goes here, etc.'
};
return String.join(lwcStackTraceLines, '\n');
}

private static String getMockLegacyWebComponentStackTrace() {
// This is a copy of an actual stack trace generated from c/loggerLWCDemo in August 2021
return 'LogEntryBuilder@https://flow-ability-5496.lightning.force.com/lightning/n/modules/c/logger.js:28:24' +
'\nnewLogEntry@https://flow-ability-5496.lightning.force.com/lightning/n/modules/c/logger.js:172:14' +
'\n_newEntry@https://flow-ability-5496.lightning.force.com/lightning/n/modules/c/logger.js:349:44' +
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nebula-logger",
"version": "4.12.6",
"version": "4.12.7",
"description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.",
"author": "Jonathan Gillespie",
"license": "MIT",
Expand Down
7 changes: 4 additions & 3 deletions sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"package": "Nebula Logger - Core",
"path": "./nebula-logger/core",
"definitionFile": "./config/scratch-orgs/base-scratch-def.json",
"versionNumber": "4.12.6.NEXT",
"versionName": "Bugfix to Automatically Truncate LogEntryEvent__e Fields",
"versionDescription": "Updated LoggerDataStore, Logger, and LogEntryEventBuilder to automatically truncate string field values that are too long for the corresponding LogEntryEvent__e fields",
"versionNumber": "4.12.7.NEXT",
"versionName": "Bugfix for LWC Stack Trace Parsing",
"versionDescription": "Fixed ComponentLogger.cls so it handles recent-ish changes to the format of stack traces in LWCs. The field LogEntry__c.OriginLocation__c should now be correctly populated again for LWCs.",
"releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases",
"unpackagedMetadata": {
"path": "./nebula-logger/extra-tests"
Expand Down Expand Up @@ -167,6 +167,7 @@
"Nebula Logger - Core@4.12.4-bugfix-child-log__c-records-not-properly-linked-to-parent-log__c-records": "04t5Y000001Mk5KQAS",
"Nebula Logger - Core@4.12.5-bugfix-for-name-shadowing-issues-with-schema-namespace": "04t5Y000001Mk5PQAS",
"Nebula Logger - Core@4.12.6-bugfix-to-automatically-truncate-logentryevent__e-fields": "04t5Y000001Mk5UQAS",
"Nebula Logger - Core@4.12.7-bugfix-for-lwc-stack-trace-parsing": "04t5Y000001Mk6IQAS",
"Nebula Logger - Core Plugin - Async Failure Additions": "0Ho5Y000000blO4SAI",
"Nebula Logger - Core Plugin - Async Failure Additions@1.0.0": "04t5Y0000015lhiQAA",
"Nebula Logger - Core Plugin - Async Failure Additions@1.0.1": "04t5Y0000015lhsQAA",
Expand Down

0 comments on commit d452edd

Please sign in to comment.