Skip to content

Commit e58b02f

Browse files
authored
Added more details in logger LWC's console statements (#789)
* Added more details in logger LWC to the component log entry JSON that's printed using console statements, such as the exception and tags * Calling the console function now happens with a delay (using setTimeout()) so that additional details can be added to the log entry (using the builder methods) before the log entry is stringified & printed out
1 parent 1707c41 commit e58b02f

9 files changed

Lines changed: 97 additions & 40 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
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.
77

8-
## Unlocked Package - v4.14.16
8+
## Unlocked Package - v4.14.17
99

10-
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015ocHQAQ)
11-
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015ocHQAQ)
10+
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015ocRQAQ)
11+
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015ocRQAQ)
1212
[![View Documentation](./images/btn-view-documentation.png)](https://github.com/jongpie/NebulaLogger/wiki)
1313

14-
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015ocHQAQ`
14+
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015ocRQAQ`
1515

16-
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015ocHQAQ`
16+
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015ocRQAQ`
1717

1818
---
1919

nebula-logger/core/main/logger-engine/classes/Logger.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
global with sharing class Logger {
1616
// There's no reliable way to get the version number dynamically in Apex
1717
@TestVisible
18-
private static final String CURRENT_VERSION_NUMBER = 'v4.14.16';
18+
private static final String CURRENT_VERSION_NUMBER = 'v4.14.17';
1919
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
2020
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
2121
private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.';

nebula-logger/core/main/logger-engine/lwc/logger/__tests__/logger.test.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ jest.mock(
4040
);
4141

4242
describe('logger lwc recommended sync getLogger() import approach tests', () => {
43-
beforeEach(() => {
43+
beforeAll(() => {
4444
disableSystemMessages();
45+
setTimeout = callbackFunction => callbackFunction();
4546
// One of logger's features (when enabled) is to auto-call the browser's console
4647
// so devs can see a log entry easily. But during Jest tests, seeing all of the
4748
// console statements is... a bit overwhelming, so the global console functions
@@ -651,7 +652,7 @@ describe('logger lwc recommended sync getLogger() import approach tests', () =>
651652
logEntryBuilder.setExceptionDetails(error);
652653

653654
expect(logEntry.error.message).toEqual(error.message);
654-
expect(logEntry.error.stackTrace).toBeTruthy();
655+
expect(logEntry.error.stackTrace.stackTraceString).toEqual(error.stackTrace);
655656
expect(logEntry.error.type).toEqual('JavaScript.TypeError');
656657
});
657658

@@ -679,7 +680,10 @@ describe('logger lwc recommended sync getLogger() import approach tests', () =>
679680
logEntryBuilder.setExceptionDetails(error);
680681

681682
expect(logEntry.error.message).toEqual(error.body.message);
682-
expect(logEntry.error.stackTrace).toBeTruthy();
683+
expect(logEntry.error.stackTrace.metadataType).toEqual('ApexClass');
684+
expect(logEntry.error.stackTrace.className).toEqual('SomeApexClass');
685+
expect(logEntry.error.stackTrace.methodName).toEqual('runSomeMethod');
686+
expect(logEntry.error.stackTrace.stackTraceString).toEqual(error.body.stackTrace);
683687
expect(logEntry.error.type).toEqual(error.body.exceptionType);
684688
});
685689

@@ -1253,7 +1257,7 @@ describe('logger lwc deprecated async createLogger() import tests', () => {
12531257
logEntryBuilder.setError(error);
12541258

12551259
expect(logEntry.error.message).toEqual(error.message);
1256-
expect(logEntry.error.stackTrace).toBeTruthy();
1260+
expect(logEntry.error.stackTrace.stackTraceString).toEqual(error.stackTrace);
12571261
expect(logEntry.error.type).toEqual('JavaScript.TypeError');
12581262
});
12591263

@@ -1278,7 +1282,10 @@ describe('logger lwc deprecated async createLogger() import tests', () => {
12781282
logEntryBuilder.setError(error);
12791283

12801284
expect(logEntry.error.message).toEqual(error.body.message);
1281-
expect(logEntry.error.stackTrace).toBeTruthy();
1285+
expect(logEntry.error.stackTrace.metadataType).toEqual('ApexClass');
1286+
expect(logEntry.error.stackTrace.className).toEqual('SomeApexClass');
1287+
expect(logEntry.error.stackTrace.methodName).toEqual('runSomeMethod');
1288+
expect(logEntry.error.stackTrace.stackTraceString).toEqual(error.body.stackTrace);
12821289
expect(logEntry.error.type).toEqual(error.body.exceptionType);
12831290
});
12841291

@@ -1817,7 +1824,7 @@ describe('logger lwc legacy markup tests', () => {
18171824
logEntryBuilder.setError(error);
18181825

18191826
expect(logEntry.error.message).toEqual(error.message);
1820-
expect(logEntry.error.stackTrace).toBeTruthy();
1827+
expect(logEntry.error.stackTrace.stackTraceString).toEqual(error.stackTrace);
18211828
expect(logEntry.error.type).toEqual('JavaScript.TypeError');
18221829
});
18231830

@@ -1844,7 +1851,10 @@ describe('logger lwc legacy markup tests', () => {
18441851
logEntryBuilder.setError(error);
18451852

18461853
expect(logEntry.error.message).toEqual(error.body.message);
1847-
expect(logEntry.error.stackTrace).toEqual(error.body.stackTrace);
1854+
expect(logEntry.error.stackTrace.metadataType).toEqual('ApexClass');
1855+
expect(logEntry.error.stackTrace.className).toEqual('SomeApexClass');
1856+
expect(logEntry.error.stackTrace.methodName).toEqual('runSomeMethod');
1857+
expect(logEntry.error.stackTrace.stackTraceString).toEqual(error.body.stackTrace);
18481858
expect(logEntry.error.type).toEqual(error.body.exceptionType);
18491859
});
18501860

nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,29 @@ export default class LogEntryEventBuilder {
106106
this.#componentLogEntry.error = {};
107107
if (exception.body) {
108108
this.#componentLogEntry.error.message = exception.body.message;
109-
this.#componentLogEntry.error.stackTrace = exception.body.stackTrace;
110109
this.#componentLogEntry.error.type = exception.body.exceptionType;
110+
111+
const transformedErrorStackTrace = {
112+
className: undefined,
113+
methodName: undefined,
114+
metadataType: undefined,
115+
triggerName: undefined,
116+
stackTraceString: exception.body.stackTrace
117+
};
118+
if (exception.body.stackTrace?.indexOf(':') > -1) {
119+
const stackTracePieces = exception.body.stackTrace.split(':')[0].split('.');
120+
121+
if (stackTracePieces[0] === 'Class') {
122+
transformedErrorStackTrace.className = stackTracePieces[1];
123+
transformedErrorStackTrace.methodName = stackTracePieces[stackTracePieces.length - 1];
124+
transformedErrorStackTrace.metadataType = 'ApexClass';
125+
} else if (stackTracePieces[0] === 'Trigger') {
126+
transformedErrorStackTrace.triggerName = stackTracePieces[1];
127+
transformedErrorStackTrace.metadataType = 'ApexTrigger';
128+
}
129+
}
130+
131+
this.#componentLogEntry.error.stackTrace = transformedErrorStackTrace;
111132
} else {
112133
this.#componentLogEntry.error.message = exception.message;
113134
this.#componentLogEntry.error.stackTrace = new LoggerStackTrace().parse(exception);
@@ -154,9 +175,11 @@ export default class LogEntryEventBuilder {
154175
* @return {LogEntryBuilder} The same instance of `LogEntryBuilder`, useful for chaining methods
155176
*/
156177
addTag(tag) {
157-
this.#componentLogEntry.tags.push(tag);
158-
// Deduplicate the list of tags
159-
this.#componentLogEntry.tags = Array.from(new Set(this.#componentLogEntry.tags));
178+
if (tag?.trim()) {
179+
this.#componentLogEntry.tags.push(tag?.trim());
180+
// Deduplicate the list of tags
181+
this.#componentLogEntry.tags = Array.from(new Set(this.#componentLogEntry.tags));
182+
}
160183
return this;
161184
}
162185

nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import LoggerServiceTaskQueue from './loggerServiceTaskQueue';
1010
import getSettings from '@salesforce/apex/ComponentLogger.getSettings';
1111
import saveComponentLogEntries from '@salesforce/apex/ComponentLogger.saveComponentLogEntries';
1212

13-
const CURRENT_VERSION_NUMBER = 'v4.14.16';
13+
const CURRENT_VERSION_NUMBER = 'v4.14.17';
1414

1515
const CONSOLE_OUTPUT_CONFIG = {
1616
messagePrefix: `%c Nebula Logger ${CURRENT_VERSION_NUMBER} `,
@@ -195,7 +195,11 @@ export default class LoggerService {
195195
this.#componentLogEntries.push(logEntry);
196196

197197
if (this.#settings.isConsoleLoggingEnabled) {
198-
this._logToConsole(logEntry.loggingLevel, logEntry.message, logEntry);
198+
// Use setTimeout() so any extra fields included in the log entry are added first before printing to the console
199+
// eslint-disable-next-line @lwc/lwc/no-async-operation
200+
setTimeout(() => {
201+
this._logToConsole(logEntry.loggingLevel, logEntry.message, logEntry);
202+
}, 1000);
199203
}
200204
if (this.#settings.isLightningLoggerEnabled) {
201205
lightningLog(logEntry);
@@ -218,22 +222,40 @@ export default class LoggerService {
218222
const consoleLoggingFunction = console[loggingLevel.toLowerCase()] ?? console.debug;
219223
const loggingLevelEmoji = LOGGING_LEVEL_EMOJIS[loggingLevel];
220224
const qualifiedMessage = `${loggingLevelEmoji} ${loggingLevel}: ${message}`;
221-
const formattedComponentLogEntryString = !componentLogEntry
222-
? ''
223-
: '\n' +
224-
JSON.stringify(
225-
{
226-
origin: {
227-
component: componentLogEntry.originStackTrace?.componentName,
228-
function: componentLogEntry.originStackTrace?.functionName,
229-
metadataType: componentLogEntry.originStackTrace?.metadataType
230-
},
231-
scenario: componentLogEntry.scenario,
232-
timestamp: componentLogEntry.timestamp
225+
// Clean up some extra properties for readability
226+
const simplifiedLogEntry = !componentLogEntry
227+
? undefined
228+
: {
229+
customFieldMappings: componentLogEntry.fieldToValue.length === 0 ? undefined : componentLogEntry.fieldToValue,
230+
originSource: {
231+
metadataType: componentLogEntry.originStackTrace?.metadataType,
232+
componentName: componentLogEntry.originStackTrace?.componentName,
233+
functionName: componentLogEntry.originStackTrace?.functionName
233234
},
234-
(_, value) => value ?? undefined,
235-
2
236-
);
235+
error: componentLogEntry.error,
236+
scenario: componentLogEntry.scenario,
237+
tags: componentLogEntry.tags.length === 0 ? undefined : componentLogEntry.tags,
238+
timestamp: !componentLogEntry.timestamp
239+
? undefined
240+
: {
241+
local: new Date(componentLogEntry.timestamp).toLocaleString(),
242+
utc: componentLogEntry.timestamp
243+
}
244+
};
245+
if (simplifiedLogEntry?.error?.stackTrace) {
246+
simplifiedLogEntry.error.errorSource = {
247+
metadataType: simplifiedLogEntry.error.stackTrace.metadataType,
248+
componentName: simplifiedLogEntry.error.stackTrace.componentName,
249+
functionName: simplifiedLogEntry.error.stackTrace.functionName,
250+
className: simplifiedLogEntry.error.stackTrace.className,
251+
methodName: simplifiedLogEntry.error.stackTrace.methodName,
252+
triggerName: simplifiedLogEntry.error.stackTrace.triggerName,
253+
stackTraceString: simplifiedLogEntry.error.stackTrace.stackTraceString
254+
};
255+
delete simplifiedLogEntry.error.stackTrace;
256+
}
257+
258+
const formattedComponentLogEntryString = !simplifiedLogEntry ? undefined : '\n' + JSON.stringify(simplifiedLogEntry, (_, value) => value ?? undefined, 2);
237259

238260
consoleLoggingFunction(CONSOLE_OUTPUT_CONFIG.messagePrefix, CONSOLE_OUTPUT_CONFIG.messageFormatting, qualifiedMessage, formattedComponentLogEntryString);
239261
}

nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export default class LoggerLWCCreateLoggerImportDemo extends LightningElement {
8282

8383
scenarioChange(event) {
8484
this.scenario = event.target.value;
85+
this.logger.setScenario(this.scenario);
8586
}
8687

8788
tagsStringChange(event) {
@@ -156,7 +157,6 @@ export default class LoggerLWCCreateLoggerImportDemo extends LightningElement {
156157

157158
saveLogExample() {
158159
console.log('running saveLog for btn');
159-
this.logger.setScenario(this.scenario);
160160
console.log(this.logger);
161161
// this.logger.saveLog('QUEUEABLE');
162162
this.logger.saveLog();

nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/loggerLWCGetLoggerImportDemo.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export default class LoggerLWCGetLoggerImportDemo extends LightningElement {
1919

2020
connectedCallback() {
2121
try {
22+
this.logger.setScenario(this.scenario);
2223
this.logger.error('test error entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCGetLoggerImportDemo' });
2324
this.logger.warn('test warn entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCGetLoggerImportDemo' });
2425
this.logger.info('test info entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCGetLoggerImportDemo' });
@@ -83,6 +84,7 @@ export default class LoggerLWCGetLoggerImportDemo extends LightningElement {
8384

8485
scenarioChange(event) {
8586
this.scenario = event.target.value;
87+
this.logger.setScenario(this.scenario);
8688
}
8789

8890
tagsStringChange(event) {
@@ -157,7 +159,6 @@ export default class LoggerLWCGetLoggerImportDemo extends LightningElement {
157159

158160
saveLogExample() {
159161
console.log('running saveLog for btn');
160-
this.logger.setScenario(this.scenario);
161162
console.log(this.logger);
162163
// this.logger.saveLog('QUEUEABLE');
163164
this.logger.saveLog();

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nebula-logger",
3-
"version": "4.14.16",
3+
"version": "4.14.17",
44
"description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.",
55
"author": "Jonathan Gillespie",
66
"license": "MIT",

sfdx-project.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
"path": "./nebula-logger/core",
1010
"definitionFile": "./config/scratch-orgs/base-scratch-def.json",
1111
"scopeProfiles": true,
12-
"versionNumber": "4.14.16.NEXT",
13-
"versionName": "CallableLogger Enhancements",
14-
"versionDescription": "Added 3 enhancements to the CallableLogger class (used for OmniStudio logging & loosely-coupled dependencies)\n\n 1. It now automatically appends OmniStudio's input data for OmniScript steps as JSON to the Message__c fields on LogEntryEvent__e and LogEntry__c.\n 2. The 'newEntry' action now also supports setting the parent log transaction ID, using the argument 'parentLogTransactionId'.\n 3. Transaction details are now returned in the output for all actions, including the current transaction ID, the parent log transaction ID, and the Salesforce-generated request ID.",
12+
"versionNumber": "4.14.17.NEXT",
13+
"versionName": "Improved JavaScript Console Output",
14+
"versionDescription": "Added more details to the component log entry JSON that's printed using console statements. The stringified object now includes more details, such as the exception, tags, and scenario.",
1515
"releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases",
1616
"unpackagedMetadata": {
1717
"path": "./nebula-logger/extra-tests"
@@ -201,6 +201,7 @@
201201
"Nebula Logger - Core@4.14.14-new-apex-static-method-&-javascript-function-logger.setfield()": "04t5Y0000015oWIQAY",
202202
"Nebula Logger - Core@4.14.15-create-log-entry-for-asyncapexcontext": "04t5Y0000015obxQAA",
203203
"Nebula Logger - Core@4.14.16-callablelogger-enhancements": "04t5Y0000015ocHQAQ",
204+
"Nebula Logger - Core@4.14.17-improved-javascript-console-output": "04t5Y0000015ocRQAQ",
204205
"Nebula Logger - Core Plugin - Async Failure Additions": "0Ho5Y000000blO4SAI",
205206
"Nebula Logger - Core Plugin - Async Failure Additions@1.0.0": "04t5Y0000015lhiQAA",
206207
"Nebula Logger - Core Plugin - Async Failure Additions@1.0.1": "04t5Y0000015lhsQAA",

0 commit comments

Comments
 (0)